From 76b5959bb5467a18641b64db09092e0461b72f52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Martign=C3=A8ne?= Date: Sun, 19 Mar 2023 09:43:51 +0100 Subject: [PATCH 0001/1350] Fix missing symbol when rglfw.c on BSD platforms (#2968) --- src/rglfw.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rglfw.c b/src/rglfw.c index 794e0d2e4..59f5ad2f8 100644 --- a/src/rglfw.c +++ b/src/rglfw.c @@ -108,6 +108,7 @@ #include "external/glfw/src/posix_module.c" #include "external/glfw/src/posix_thread.c" #include "external/glfw/src/posix_time.c" + #include "external/glfw/src/posix_poll.c" #include "external/glfw/src/null_joystick.c" #include "external/glfw/src/xkb_unicode.c" From c14c7f0b695edb363d9622777fee1986e61afd7e Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sun, 19 Mar 2023 06:16:52 -0400 Subject: [PATCH 0002/1350] raudio: Fix warning on discarded const qualifier (#2967) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `qoaplay_open()` function expects a `char *`, but we are passing in a `const char *`. While this works just fine, it does issue a compiler warning when strict: ``` src/raudio.c: In function ‘LoadMusicStream’: src/raudio.c:1290:45: warning: passing argument 1 of ‘qoaplay_open’ discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers] 1290 | qoaplay_desc *ctxQoa = qoaplay_open(fileName); | ^~~~~~~~ In file included from src/raudio.c:233: src/external/qoaplay.c:86:34: note: expected ‘char *’ but argument is of type ‘const char *’ 86 | qoaplay_desc *qoaplay_open(char *path) | ~~~~~~^~~~ ``` This change casts the argument to a `char *` to fix the warning. --- src/raudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index d7ee183a8..9e64546d6 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1287,7 +1287,7 @@ Music LoadMusicStream(const char *fileName) #if defined(SUPPORT_FILEFORMAT_QOA) else if (IsFileExtension(fileName, ".qoa")) { - qoaplay_desc *ctxQoa = qoaplay_open(fileName); + qoaplay_desc *ctxQoa = qoaplay_open((char *)fileName); music.ctxType = MUSIC_AUDIO_QOA; music.ctxData = ctxQoa; From 2e02474b7aac2e6e3fe680a3aa996973585eeaad Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 19 Mar 2023 11:25:29 +0100 Subject: [PATCH 0003/1350] Update core_loading_thread.c --- examples/core/core_loading_thread.c | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/core/core_loading_thread.c b/examples/core/core_loading_thread.c index 5a988bb27..0538dcee3 100644 --- a/examples/core/core_loading_thread.c +++ b/examples/core/core_loading_thread.c @@ -15,6 +15,7 @@ #include "raylib.h" +// WARNING: This example does not build on Windows with MSVC compiler #include "pthread.h" // POSIX style threads management #include // C11 atomic data types From 3c02f0c75b88726ac91975ae369646b657ecb3b2 Mon Sep 17 00:00:00 2001 From: Anand Swaroop <72886192+Anut-py@users.noreply.github.com> Date: Sun, 19 Mar 2023 10:38:30 -0400 Subject: [PATCH 0004/1350] Update h-raylib version (#2970) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index bedf4b988..3d1d58953 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -27,7 +27,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-go | **4.2** | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | | raylib-guile | auto | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | | gforth-raylib | 3.5 | [Gforth](https://gforth.org/) | MIT | https://github.com/ArnautDaniel/gforth-raylib | -| h-raylib | 4.5-dev | [Haskell](https://haskell.org/) | Apache-2.0 | https://github.com/Anut-py/h-raylib | +| h-raylib | **4.5** | [Haskell](https://haskell.org/) | Apache-2.0 | https://github.com/Anut-py/h-raylib | | raylib-hx | **4.2** | [Haxe](https://haxe.org/) | Zlib | https://github.com/foreignsasquatch/raylib-hx | | hb-raylib | 3.5 | [Harbour](https://harbour.github.io) | MIT | https://github.com/MarcosLeonardoMendezGerencir/hb-raylib | | jaylib | **4.2** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | GPLv3+CE | https://github.com/electronstudio/jaylib/ | From 03e19c7f4349727765fc93a5db102ba3e84330ba Mon Sep 17 00:00:00 2001 From: Michael Scherbakow Date: Sun, 19 Mar 2023 18:27:21 +0100 Subject: [PATCH 0005/1350] Update raylib.zig version to 4.5 (#2971) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 3d1d58953..0b2acc36d 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -68,7 +68,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-vapi | **4.2** | [Vala](https://vala.dev/) | Zlib | https://github.com/lxmcf/raylib-vapi | | raylib-wren | **4.0** | [Wren](http://wren.io/) | ISC | https://github.com/TSnake41/raylib-wren | | raylib-zig | **4.2** | [Zig](https://ziglang.org/) | MIT | https://github.com/Not-Nik/raylib-zig | -| raylib.zig | **4.2** | [Zig](https://ziglang.org/) | MIT | https://github.com/ryupold/raylib.zig | +| raylib.zig | **4.5** | [Zig](https://ziglang.org/) | MIT | https://github.com/ryupold/raylib.zig | | hare-raylib | auto | [Hare](https://harelang.org/) | Zlib | https://git.sr.ht/~evantj/hare-raylib | | raylib-sunder | auto | [Sunder](https://github.com/ashn-dot-dev/sunder) | 0BSD | https://github.com/ashn-dot-dev/raylib-sunder | | rayed-bqn | auto | [BQN](https://mlochbaum.github.io/BQN/) | MIT | https://github.com/Brian-ED/rayed-bqn | From 08670ecea123bf3892206281b011a0c65a3789e6 Mon Sep 17 00:00:00 2001 From: Webfra Date: Sun, 19 Mar 2023 20:34:22 +0100 Subject: [PATCH 0006/1350] Add const qualifier to char * path argument in qoaplay_open() (#2972) * Add const qualifier to char * path argument in qoa_open() * Remove unnecessary cast --- src/external/qoaplay.c | 4 ++-- src/raudio.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/external/qoaplay.c b/src/external/qoaplay.c index 549e9f778..7f937f4fa 100644 --- a/src/external/qoaplay.c +++ b/src/external/qoaplay.c @@ -62,7 +62,7 @@ typedef struct { extern "C" { // Prevents name mangling of functions #endif -qoaplay_desc *qoaplay_open(char *path); +qoaplay_desc *qoaplay_open(const char *path); qoaplay_desc *qoaplay_open_memory(const unsigned char *data, int data_size); void qoaplay_close(qoaplay_desc *qoa_ctx); @@ -83,7 +83,7 @@ int qoaplay_get_frame(qoaplay_desc *qoa_ctx); //---------------------------------------------------------------------------------- // Open QOA file, keep FILE pointer to keep reading from file -qoaplay_desc *qoaplay_open(char *path) +qoaplay_desc *qoaplay_open(const char *path) { FILE *file = fopen(path, "rb"); if (!file) return NULL; diff --git a/src/raudio.c b/src/raudio.c index 9e64546d6..d7ee183a8 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1287,7 +1287,7 @@ Music LoadMusicStream(const char *fileName) #if defined(SUPPORT_FILEFORMAT_QOA) else if (IsFileExtension(fileName, ".qoa")) { - qoaplay_desc *ctxQoa = qoaplay_open((char *)fileName); + qoaplay_desc *ctxQoa = qoaplay_open(fileName); music.ctxType = MUSIC_AUDIO_QOA; music.ctxData = ctxQoa; From e450c75f6f33f17a3b491c16fff0951846eb6e64 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Mon, 20 Mar 2023 11:41:16 -0400 Subject: [PATCH 0007/1350] BINDINGS: Update various versions to 4.5 (#2974) --- BINDINGS.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/BINDINGS.md b/BINDINGS.md index 0b2acc36d..12b91f85b 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -6,7 +6,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | name | raylib version | language | license | repo | |:------------------:|:---------------:|:---------:|:----------:|-----------------------------------------------------------| -| raylib | **4.2** | [C/C++](https://en.wikipedia.org/wiki/C_(programming_language)) | Zlib | https://github.com/raysan5/raylib | +| raylib | **4.5** | [C/C++](https://en.wikipedia.org/wiki/C_(programming_language)) | Zlib | https://github.com/raysan5/raylib | | raylib-boo | 3.7 | [Boo](http://boo-language.github.io/)| MIT | https://github.com/Rabios/raylib-boo | | Raylib-cs | **4.2** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | https://github.com/ChrisDill/Raylib-cs | | Raylib-CsLo | **4.2** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo | @@ -42,7 +42,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | NimraylibNow! | 4.2 | [Nim](https://nim-lang.org/) | MIT | https://github.com/greenfork/nimraylib_now | | raylib-Forever | auto | [Nim](https://nim-lang.org/) | ? | https://github.com/Guevara-chan/Raylib-Forever | | naylib | auto | [Nim](https://nim-lang.org/) | MIT | https://github.com/planetis-m/naylib | -| node-raylib | **4.0** | [Node.js](https://nodejs.org/en/) | Zlib | https://github.com/RobLoach/node-raylib | +| node-raylib | **4.5** | [Node.js](https://nodejs.org/en/) | Zlib | https://github.com/RobLoach/node-raylib | | raylib_odin_bindings | 4.0-dev | [Odin](https://odin-lang.org/) | MIT | https://github.com/Deathbat2190/raylib_odin_bindings | | raylib-odin | **4.0** | [Odin](https://odin-lang.org/) | BSD-3Clause | https://github.com/odin-lang/Odin/tree/master/vendor/raylib | | raylib-ocaml | **4.2** | [OCaml](https://ocaml.org/) | MIT | https://github.com/tjammer/raylib-ocaml | @@ -63,7 +63,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-swift | **4.0** | [Swift](https://swift.org/) | MIT | https://github.com/STREGAsGate/Raylib | | raylib-scopes | auto | [Scopes](http://scopes.rocks) | MIT | https://github.com/salotz/raylib-scopes | | raylib-smallBasic | 4.1-dev | [SmallBASIC](https://github.com/smallbasic/SmallBASIC) | GPLv3 | https://github.com/smallbasic/smallbasic.plugins/tree/master/raylib | -| raylib-umka | **4.2** | [Umka](https://github.com/vtereshkov/umka-lang) | Zlib | https://github.com/robloach/raylib-umka | +| raylib-umka | **4.5** | [Umka](https://github.com/vtereshkov/umka-lang) | Zlib | https://github.com/robloach/raylib-umka | | raylib.v | **4.2** | [V](https://vlang.io/) | Zlib | https://github.com/irishgreencitrus/raylib.v | | raylib-vapi | **4.2** | [Vala](https://vala.dev/) | Zlib | https://github.com/lxmcf/raylib-vapi | | raylib-wren | **4.0** | [Wren](http://wren.io/) | ISC | https://github.com/TSnake41/raylib-wren | @@ -78,7 +78,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to These are utility wrappers for specific languages, they are not required to use raylib in the language but may adapt the raylib API to be more inline with the language's pardigm. | name | raylib version | language | license | repo | |:------------------:|:-------------: | :--------:|:-------:|:-------------------------------------------------------------| -| raylib-cpp | **4.2** | [C++](https://en.wikipedia.org/wiki/C%2B%2B) | Zlib | https://github.com/robloach/raylib-cpp | +| raylib-cpp | **4.5** | [C++](https://en.wikipedia.org/wiki/C%2B%2B) | Zlib | https://github.com/robloach/raylib-cpp | | claylib | **4.2** | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | ### Older or Unmaintained Language Bindings From ace7aef0e619b810173939ce7ae8edd89916deff Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Tue, 21 Mar 2023 00:43:22 +0900 Subject: [PATCH 0008/1350] Fix typo in rmodels.c (#2976) Upate -> Update --- src/rmodels.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index 2b1d0f936..6db7016a2 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3400,7 +3400,7 @@ void GenMeshTangents(Mesh *mesh) { if (mesh->vboId[SHADER_LOC_VERTEX_TANGENT] != 0) { - // Upate existing vertex buffer + // Update existing vertex buffer rlUpdateVertexBuffer(mesh->vboId[SHADER_LOC_VERTEX_TANGENT], mesh->tangents, mesh->vertexCount*4*sizeof(float), 0); } else From 9f7a49bec388fc886cb7d84228bb70c23bd2f1ec Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 20 Mar 2023 18:03:37 +0100 Subject: [PATCH 0009/1350] Updated version to avoid confusion with 4.5 release --- src/raylib.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 4cd9e4349..6e5d023bf 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* raylib v4.5 - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) +* raylib v4.6-dev - A simple and easy-to-use library to enjoy videogames programming (www.raylib.com) * * FEATURES: * - NO external dependencies, all required libraries included with raylib @@ -82,9 +82,9 @@ #include // Required for: va_list - Only used by TraceLogCallback #define RAYLIB_VERSION_MAJOR 4 -#define RAYLIB_VERSION_MINOR 5 +#define RAYLIB_VERSION_MINOR 6 #define RAYLIB_VERSION_PATCH 0 -#define RAYLIB_VERSION "4.5" +#define RAYLIB_VERSION "4.6-dev" // Function specifiers in case library is build/used as a shared library (Windows) // NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll From 4f43ceb0d21b71041ce0aca2a45819a6728362c9 Mon Sep 17 00:00:00 2001 From: Mansour Quddus Date: Tue, 21 Mar 2023 03:31:29 -0400 Subject: [PATCH 0010/1350] add missing space in one of the cameraDescriptions (#2977) --- examples/core/core_2d_camera_platformer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/core/core_2d_camera_platformer.c b/examples/core/core_2d_camera_platformer.c index 1ae9e0fcb..b149ea8c3 100644 --- a/examples/core/core_2d_camera_platformer.c +++ b/examples/core/core_2d_camera_platformer.c @@ -90,7 +90,7 @@ int main(void) "Follow player center", "Follow player center, but clamp to map edges", "Follow player center; smoothed", - "Follow player center horizontally; updateplayer center vertically after landing", + "Follow player center horizontally; update player center vertically after landing", "Player push camera on getting too close to screen edge" }; From a139ba9c4823d2a6d48f57c930bf3ac6ecf2d33f Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 21 Mar 2023 12:04:03 +0100 Subject: [PATCH 0011/1350] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 05ba5ccf0..b4e187cf7 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Ready to learn? Jump to [code examples!](https://www.raylib.com/examples.html) [![GitHub Releases Downloads](https://img.shields.io/github/downloads/raysan5/raylib/total)](https://github.com/raysan5/raylib/releases) [![GitHub Stars](https://img.shields.io/github/stars/raysan5/raylib?style=flat&label=stars)](https://github.com/raysan5/raylib/stargazers) -[![GitHub commits since tagged version](https://img.shields.io/github/commits-since/raysan5/raylib/4.2.0)](https://github.com/raysan5/raylib/commits/master) +[![GitHub commits since tagged version](https://img.shields.io/github/commits-since/raysan5/raylib/4.5.0)](https://github.com/raysan5/raylib/commits/master) [![GitHub Sponsors](https://img.shields.io/github/sponsors/raysan5?label=sponsors)](https://github.com/sponsors/raysan5) [![Packaging Status](https://repology.org/badge/tiny-repos/raylib.svg)](https://repology.org/project/raylib/versions) [![License](https://img.shields.io/badge/license-zlib%2Flibpng-blue.svg)](LICENSE) From 04229c5854fc311354e5b848f454ef20a9db0fd3 Mon Sep 17 00:00:00 2001 From: Gunko Vadim Date: Wed, 22 Mar 2023 00:03:38 +0500 Subject: [PATCH 0012/1350] Update BINDINGS.md (#2980) update ray4laz to raylib 4.5 --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 12b91f85b..43af6131c 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -47,7 +47,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-odin | **4.0** | [Odin](https://odin-lang.org/) | BSD-3Clause | https://github.com/odin-lang/Odin/tree/master/vendor/raylib | | raylib-ocaml | **4.2** | [OCaml](https://ocaml.org/) | MIT | https://github.com/tjammer/raylib-ocaml | | TurboRaylib | **4.2** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT | https://github.com/turborium/TurboRaylib | -| Ray4Laz | **4.2** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/GuvaCode/Ray4Laz | +| Ray4Laz | **4.5** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/GuvaCode/Ray4Laz | | Raylib.4.0.Pascal | **4.0** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/sysrpl/Raylib.4.0.Pascal | | pyraylib | 3.7 | [Python](https://www.python.org/) | Zlib | https://github.com/Ho011/pyraylib | | raylib-python-cffi | **4.2** | [Python](https://www.python.org/) | EPL-2.0 | https://github.com/electronstudio/raylib-python-cffi | From d61303b1b068c5ec579f314ef727f81de1749328 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 21 Mar 2023 21:46:20 +0000 Subject: [PATCH 0013/1350] Update BINDINGS.md for raylib Odin 4.5 (#2981) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 43af6131c..4e754150a 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -43,8 +43,8 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-Forever | auto | [Nim](https://nim-lang.org/) | ? | https://github.com/Guevara-chan/Raylib-Forever | | naylib | auto | [Nim](https://nim-lang.org/) | MIT | https://github.com/planetis-m/naylib | | node-raylib | **4.5** | [Node.js](https://nodejs.org/en/) | Zlib | https://github.com/RobLoach/node-raylib | +| raylib-odin | **4.5** | [Odin](https://odin-lang.org/) | BSD-3Clause | https://github.com/odin-lang/Odin/tree/master/vendor/raylib | | raylib_odin_bindings | 4.0-dev | [Odin](https://odin-lang.org/) | MIT | https://github.com/Deathbat2190/raylib_odin_bindings | -| raylib-odin | **4.0** | [Odin](https://odin-lang.org/) | BSD-3Clause | https://github.com/odin-lang/Odin/tree/master/vendor/raylib | | raylib-ocaml | **4.2** | [OCaml](https://ocaml.org/) | MIT | https://github.com/tjammer/raylib-ocaml | | TurboRaylib | **4.2** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT | https://github.com/turborium/TurboRaylib | | Ray4Laz | **4.5** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/GuvaCode/Ray4Laz | From 7565e274b15c5e64e814ea6fa41553ad5efcef9a Mon Sep 17 00:00:00 2001 From: Jarrod Davis <69952438+jarroddavis68@users.noreply.github.com> Date: Wed, 22 Mar 2023 03:55:58 -0400 Subject: [PATCH 0014/1350] Update BINDINGS.md (#2983) Adding raylib for Pascal --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index 4e754150a..fed0e16c5 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -24,6 +24,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | rayex | 3.7 | [elixir](https://elixir-lang.org/) | Apache-2.0 | https://github.com/shiryel/rayex | | raylib-factor | **4.0** | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | | raylib-freebasic | **4.2** | [FreeBASIC](https://www.freebasic.net/) | MIT | https://github.com/WIITD/raylib-freebasic | +| raylib for Pascal | **4.5** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | Modified Zlib | https://github.com/tinyBigGAMES/raylib | | raylib-go | **4.2** | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | | raylib-guile | auto | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | | gforth-raylib | 3.5 | [Gforth](https://gforth.org/) | MIT | https://github.com/ArnautDaniel/gforth-raylib | From 02a8a49961190cb2076955d0960695134ba83fe2 Mon Sep 17 00:00:00 2001 From: Hanaxar <87268284+hanaxar@users.noreply.github.com> Date: Wed, 22 Mar 2023 12:59:28 +0300 Subject: [PATCH 0015/1350] Calculate exact image size in GenImageFontAtlas (#2963) * Calculate exact image size in GenImageFontAtlas Calculate exact image size with a method based on total glyph width and glyph row count Current method seemed a little bit overkill with square root, log and power functions and only approximates image size which can be wonky with some weird fonts like cursive fonts. Proposed method calculates image size directly with a simpler method and results exact image size needed. * Update rtext.c * Update rtext.c Changed do-while to while loop, and also added an extra step to calculate maximum glyph width and excluding it from image width for extra safety. --- src/rtext.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index 9eec0059f..da93af7e3 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -694,14 +694,19 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC // NOTE: Rectangles memory is loaded here! Rectangle *recs = (Rectangle *)RL_MALLOC(glyphCount*sizeof(Rectangle)); - // Calculate image size based on required pixel area - // NOTE 1: Image is forced to be squared and POT... very conservative! - // NOTE 2: SDF font characters already contain an internal padding, - // so image size would result bigger than default font type - float requiredArea = 0; - for (int i = 0; i < glyphCount; i++) requiredArea += ((chars[i].image.width + 2*padding)*(fontSize + 2*padding)); - float guessSize = sqrtf(requiredArea)*1.4f; - int imageSize = (int)powf(2, ceilf(logf((float)guessSize)/logf(2))); // Calculate next POT + // Calculate image size based on total glyph width and glyph row count + int totalWidth = 0, maxGlyphWidth = 0; + for (int i = 0; i < glyphCount; i++) + { + if (chars[i].image.width > maxGlyphWidth) maxGlyphWidth = chars[i].image.width; + totalWidth += chars[i].image.width + 2*padding; + } + int rowCount = 0, imageSize = 64; // A minimum starting value to avoid unnecessary calculation steps for very small images + while (totalWidth > (imageSize - maxGlyphWidth)*rowCount) // maxGlyphWidth is maximum possible space left at the end of row + { + imageSize *= 2; // Double the size of image (to keep POT) + rowCount = imageSize/(fontSize + 2*padding); // Calculate new row count for the new image size + } atlas.width = imageSize; // Atlas bitmap width atlas.height = imageSize; // Atlas bitmap height From e55bdd5d8af913016b74776d0878b30eb830f138 Mon Sep 17 00:00:00 2001 From: Hanaxar <87268284+hanaxar@users.noreply.github.com> Date: Wed, 22 Mar 2023 13:00:13 +0300 Subject: [PATCH 0016/1350] Fix packing logic error in ```GenImageFontAtlas``` (#2979) Basic packing algorithm currently follows this order: Copy pixel data -> Move offsetX for current glyph -> Check remaining space for current glyph... Since X offset already moved according current glyph, remaining space should be checked for next glyph. Because of this, occasionally, current logic causes glyphs wrapping around texture. Proposed fix accomplishes that by moving offsetX check to the beginning of the loop. --- src/rtext.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index da93af7e3..e8493778e 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -725,24 +725,7 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC // NOTE: Using simple packaging, one char after another for (int i = 0; i < glyphCount; i++) { - // Copy pixel data from fc.data to atlas - for (int y = 0; y < chars[i].image.height; y++) - { - for (int x = 0; x < chars[i].image.width; x++) - { - ((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = ((unsigned char *)chars[i].image.data)[y*chars[i].image.width + x]; - } - } - - // Fill chars rectangles in atlas info - recs[i].x = (float)offsetX; - recs[i].y = (float)offsetY; - recs[i].width = (float)chars[i].image.width; - recs[i].height = (float)chars[i].image.height; - - // Move atlas position X for next character drawing - offsetX += (chars[i].image.width + 2*padding); - + // Check remaining space for glyph if (offsetX >= (atlas.width - chars[i].image.width - 2*padding)) { offsetX = padding; @@ -766,6 +749,24 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC break; } } + + // Copy pixel data from fc.data to atlas + for (int y = 0; y < chars[i].image.height; y++) + { + for (int x = 0; x < chars[i].image.width; x++) + { + ((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = ((unsigned char *)chars[i].image.data)[y*chars[i].image.width + x]; + } + } + + // Fill chars rectangles in atlas info + recs[i].x = (float)offsetX; + recs[i].y = (float)offsetY; + recs[i].width = (float)chars[i].image.width; + recs[i].height = (float)chars[i].image.height; + + // Move atlas position X for next character drawing + offsetX += (chars[i].image.width + 2*padding); } } else if (packMethod == 1) // Use Skyline rect packing algorithm (stb_pack_rect) From 8b8eddc8e29ddfb679100c5acc290c065e177d42 Mon Sep 17 00:00:00 2001 From: Rico P Date: Wed, 22 Mar 2023 11:01:05 +0100 Subject: [PATCH 0017/1350] slightly optimize Vector3Normalize (#2982) --- src/raymath.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/raymath.h b/src/raymath.h index 422a42eee..54eaa815d 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -703,12 +703,14 @@ RMAPI Vector3 Vector3Normalize(Vector3 v) Vector3 result = v; float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); - if (length == 0.0f) length = 1.0f; - float ilength = 1.0f/length; + if (length != 0.0f) + { + float ilength = 1.0f/length; - result.x *= ilength; - result.y *= ilength; - result.z *= ilength; + result.x *= ilength; + result.y *= ilength; + result.z *= ilength; + } return result; } From ecb6a6af32b4219207f9adc9635c280756dd0f57 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 22 Mar 2023 11:08:46 +0100 Subject: [PATCH 0018/1350] Review format --- src/rtext.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index e8493778e..2d01360ef 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -695,14 +695,20 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC Rectangle *recs = (Rectangle *)RL_MALLOC(glyphCount*sizeof(Rectangle)); // Calculate image size based on total glyph width and glyph row count - int totalWidth = 0, maxGlyphWidth = 0; + int totalWidth = 0; + int maxGlyphWidth = 0; + for (int i = 0; i < glyphCount; i++) { if (chars[i].image.width > maxGlyphWidth) maxGlyphWidth = chars[i].image.width; totalWidth += chars[i].image.width + 2*padding; } - int rowCount = 0, imageSize = 64; // A minimum starting value to avoid unnecessary calculation steps for very small images - while (totalWidth > (imageSize - maxGlyphWidth)*rowCount) // maxGlyphWidth is maximum possible space left at the end of row + + int rowCount = 0; + int imageSize = 64; // Define minimum starting value to avoid unnecessary calculation steps for very small images + + // NOTE: maxGlyphWidth is maximum possible space left at the end of row + while (totalWidth > (imageSize - maxGlyphWidth)*rowCount) { imageSize *= 2; // Double the size of image (to keep POT) rowCount = imageSize/(fontSize + 2*padding); // Calculate new row count for the new image size From 19892a3c3a08a9bfa291d0d8c745ca23a27c9972 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 22 Mar 2023 11:15:00 +0100 Subject: [PATCH 0019/1350] Update resource arch for 64bit #2978 --- src/raylib.dll.rc.data | Bin 11246 -> 11246 bytes src/raylib.rc.data | Bin 11182 -> 11182 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/src/raylib.dll.rc.data b/src/raylib.dll.rc.data index d506b8d28651d7d7e17519bcc0a382a161b688f1..7403bd020b0658ed1c8ee569850e2eff9a0b3a50 100644 GIT binary patch delta 144 zcmaDC{w`c3rHzpR1a4?EFhE!=3>!r!r>w2PP-4nX(}%nTRctOsz`2RMs?32XvTodBGr a0B0G%Sq^ZPUQuyTGDH*DQf3AgAP)doB@1`} delta 144 zcmZ1%zAjwEhmny11ZHS6FhE!=j2lHXw2Rmo4nX(}> Date: Wed, 22 Mar 2023 12:43:18 +0100 Subject: [PATCH 0020/1350] Update CMakeLists.txt --- src/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9087f8eea..5092bdf47 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,7 +1,7 @@ # Setup the project and settings project(raylib C) -set(PROJECT_VERSION 4.2.0) -set(API_VERSION 420) +set(PROJECT_VERSION 4.5.0) +set(API_VERSION 450) include(GNUInstallDirs) include(JoinPaths) From 770e239f73c45d1912fe3a9f27ce5e9fa11444ea Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 22 Mar 2023 19:47:42 +0100 Subject: [PATCH 0021/1350] Minor tweaks to raylib events automation system --- src/rcore.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index eae49515f..b1632492b 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -595,14 +595,14 @@ static const char *autoEventTypeName[] = { "ACTION_SETTARGETFPS" }; -// Automation Event (20 bytes) +// Automation Event (24 bytes) typedef struct AutomationEvent { unsigned int frame; // Event frame - unsigned int type; // Event type (AutoEventType) - int params[3]; // Event parameters (if required) + unsigned int type; // Event type (AutomationEventType) + int params[4]; // Event parameters (if required) } AutomationEvent; -static AutomationEvent *events = NULL; // Events array +static AutomationEvent *events = NULL; // Events array static unsigned int eventCount = 0; // Events count static bool eventsPlaying = false; // Play events static bool eventsRecording = false; // Record events @@ -925,7 +925,7 @@ void InitWindow(int width, int height, const char *title) #endif #if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)malloc(MAX_CODE_AUTOMATION_EVENTS*sizeof(AutomationEvent)); + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); CORE.Time.frameCounter = 0; #endif @@ -1073,7 +1073,7 @@ void CloseWindow(void) #endif #if defined(SUPPORT_EVENTS_AUTOMATION) - free(events); + RL_FREE(events); #endif CORE.Window.ready = false; @@ -6945,11 +6945,11 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt // TODO: This system should probably be redesigned static void LoadAutomationEvents(const char *fileName) { - //unsigned char fileId[4] = { 0 }; - - // Load binary + // Load events file (binary) /* FILE *repFile = fopen(fileName, "rb"); + unsigned char fileId[4] = { 0 }; + fread(fileId, 1, 4, repFile); if ((fileId[0] == 'r') && (fileId[1] == 'E') && (fileId[2] == 'P') && (fileId[1] == ' ')) @@ -6962,7 +6962,7 @@ static void LoadAutomationEvents(const char *fileName) fclose(repFile); */ - // Load events (text file) + // Load events file (text) FILE *repFile = fopen(fileName, "rt"); if (repFile != NULL) From 0925851f896fd82ef91804cdf2429d3a75e72aeb Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 22 Mar 2023 20:11:03 +0100 Subject: [PATCH 0022/1350] Update rl_gputex.h --- src/external/rl_gputex.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index c20bdc67e..f7cc00ecf 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -814,5 +814,4 @@ static int get_pixel_data_size(int width, int height, int format) return data_size; } - #endif // RL_GPUTEX_IMPLEMENTATION From 6287f68c0bb172ac70bfbe30617d9480ab4eab1f Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 22 Mar 2023 20:11:36 +0100 Subject: [PATCH 0023/1350] Lazy loading of default font for image loading (no InitWindow) --- src/rtextures.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rtextures.c b/src/rtextures.c index b5f9998a9..fb63f2ff0 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -219,7 +219,7 @@ //---------------------------------------------------------------------------------- // Other Modules Functions Declaration (required by text) //---------------------------------------------------------------------------------- -// ... +extern void LoadFontDefault(void); // [Module: text] Loads default font, required by ImageDrawText() //---------------------------------------------------------------------------------- // Module specific Functions Declaration @@ -3152,6 +3152,9 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSize, Color color) { #if defined(SUPPORT_MODULE_RTEXT) + // Make sure default font is loaded to be used on image text drawing + if (GetFontDefault().texture.id == 0) LoadFontDefault(); + Vector2 position = { (float)posX, (float)posY }; // NOTE: For default font, spacing is set to desired font size / default font size (10) ImageDrawTextEx(dst, GetFontDefault(), text, position, (float)fontSize, (float)fontSize/10, color); // WARNING: Module required: rtext From 57082d66008fbb315162541751767fc3387864ac Mon Sep 17 00:00:00 2001 From: WIITD <52134513+WIITD@users.noreply.github.com> Date: Thu, 23 Mar 2023 22:26:55 +0100 Subject: [PATCH 0024/1350] update raylib-freebasic to 4.5 (#2986) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index fed0e16c5..dc9f6700a 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -23,7 +23,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | dlang_raylib | **4.0** | [D](https://dlang.org) | MPL-2.0 |https://github.com/rc-05/dlang_raylib | | rayex | 3.7 | [elixir](https://elixir-lang.org/) | Apache-2.0 | https://github.com/shiryel/rayex | | raylib-factor | **4.0** | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | -| raylib-freebasic | **4.2** | [FreeBASIC](https://www.freebasic.net/) | MIT | https://github.com/WIITD/raylib-freebasic | +| raylib-freebasic | **4.5** | [FreeBASIC](https://www.freebasic.net/) | MIT | https://github.com/WIITD/raylib-freebasic | | raylib for Pascal | **4.5** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | Modified Zlib | https://github.com/tinyBigGAMES/raylib | | raylib-go | **4.2** | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | | raylib-guile | auto | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | From 1fc8f1bdbfdef1aaae897d018f97673963201ab4 Mon Sep 17 00:00:00 2001 From: Steven Schveighoffer Date: Sat, 25 Mar 2023 05:32:46 -0400 Subject: [PATCH 0025/1350] Update raylib-d binding version to 4.5 (#2988) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index dc9f6700a..f07c5e42e 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -19,7 +19,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | dart-raylib | **4.0** | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | | bindbc-raylib3 | **4.0** | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | | dray | **4.2** | [D](https://dlang.org/) | Apache-2.0 | https://github.com/redthing1/dray | -| raylib-d | **4.2** | [D](https://dlang.org/) | Zlib | https://github.com/schveiguy/raylib-d | +| raylib-d | **4.5** | [D](https://dlang.org/) | Zlib | https://github.com/schveiguy/raylib-d | | dlang_raylib | **4.0** | [D](https://dlang.org) | MPL-2.0 |https://github.com/rc-05/dlang_raylib | | rayex | 3.7 | [elixir](https://elixir-lang.org/) | Apache-2.0 | https://github.com/shiryel/rayex | | raylib-factor | **4.0** | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | From 02bd709043b9ec0c4557323878ba885c6582b24e Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 25 Mar 2023 10:38:21 +0100 Subject: [PATCH 0026/1350] Update BINDINGS.md --- BINDINGS.md | 74 ++++++++++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/BINDINGS.md b/BINDINGS.md index f07c5e42e..e0585117e 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -8,71 +8,71 @@ Some people ported raylib to other languages in form of bindings or wrappers to |:------------------:|:---------------:|:---------:|:----------:|-----------------------------------------------------------| | raylib | **4.5** | [C/C++](https://en.wikipedia.org/wiki/C_(programming_language)) | Zlib | https://github.com/raysan5/raylib | | raylib-boo | 3.7 | [Boo](http://boo-language.github.io/)| MIT | https://github.com/Rabios/raylib-boo | -| Raylib-cs | **4.2** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | https://github.com/ChrisDill/Raylib-cs | -| Raylib-CsLo | **4.2** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo | -| cl-raylib | **4.0** | [Common Lisp](https://common-lisp.net/) | MIT | https://github.com/longlene/cl-raylib | -| claylib/wrap | **4.2** | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | -| chez-raylib | auto | [Chez Scheme](https://cisco.github.io/ChezScheme/) | GPLv3 | https://github.com/Yunoinsky/chez-raylib | +| Raylib-cs | 4.2 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | https://github.com/ChrisDill/Raylib-cs | +| Raylib-CsLo | 4.2 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo | +| cl-raylib | 4.0 | [Common Lisp](https://common-lisp.net/) | MIT | https://github.com/longlene/cl-raylib | +| claylib/wrap | 4.2 | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | +| chez-raylib | **auto** | [Chez Scheme](https://cisco.github.io/ChezScheme/) | GPLv3 | https://github.com/Yunoinsky/chez-raylib | | raylib-cr | **4.5-dev (7e7939e)** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | -| ray-cyber | **4.2** | [Cyber](https://cyberscript.dev) | MIT | https://github.com/fubark/ray-cyber | +| ray-cyber | 4.2 | [Cyber](https://cyberscript.dev) | MIT | https://github.com/fubark/ray-cyber | | raylib-c3 | **4.5-dev** | [C3](https://c3-lang.org/) | MIT | https://github.com/Its-Kenta/Raylib-C3 | -| dart-raylib | **4.0** | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | -| bindbc-raylib3 | **4.0** | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | -| dray | **4.2** | [D](https://dlang.org/) | Apache-2.0 | https://github.com/redthing1/dray | +| dart-raylib | 4.0 | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | +| bindbc-raylib3 | 4.0 | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | +| dray | 4.2 | [D](https://dlang.org/) | Apache-2.0 | https://github.com/redthing1/dray | | raylib-d | **4.5** | [D](https://dlang.org/) | Zlib | https://github.com/schveiguy/raylib-d | -| dlang_raylib | **4.0** | [D](https://dlang.org) | MPL-2.0 |https://github.com/rc-05/dlang_raylib | +| dlang_raylib | 4.0 | [D](https://dlang.org) | MPL-2.0 |https://github.com/rc-05/dlang_raylib | | rayex | 3.7 | [elixir](https://elixir-lang.org/) | Apache-2.0 | https://github.com/shiryel/rayex | -| raylib-factor | **4.0** | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | +| raylib-factor | 4.0 | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | | raylib-freebasic | **4.5** | [FreeBASIC](https://www.freebasic.net/) | MIT | https://github.com/WIITD/raylib-freebasic | | raylib for Pascal | **4.5** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | Modified Zlib | https://github.com/tinyBigGAMES/raylib | -| raylib-go | **4.2** | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | -| raylib-guile | auto | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | +| raylib-go | 4.2 | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | +| raylib-guile | **auto** | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | | gforth-raylib | 3.5 | [Gforth](https://gforth.org/) | MIT | https://github.com/ArnautDaniel/gforth-raylib | | h-raylib | **4.5** | [Haskell](https://haskell.org/) | Apache-2.0 | https://github.com/Anut-py/h-raylib | -| raylib-hx | **4.2** | [Haxe](https://haxe.org/) | Zlib | https://github.com/foreignsasquatch/raylib-hx | +| raylib-hx | 4.2 | [Haxe](https://haxe.org/) | Zlib | https://github.com/foreignsasquatch/raylib-hx | | hb-raylib | 3.5 | [Harbour](https://harbour.github.io) | MIT | https://github.com/MarcosLeonardoMendezGerencir/hb-raylib | -| jaylib | **4.2** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | GPLv3+CE | https://github.com/electronstudio/jaylib/ | -| raylib-j | **4.0** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | Zlib | https://github.com/CreedVI/Raylib-J | -| raylib.jl | **4.2** | [Julia](https://julialang.org/) | Zlib | https://github.com/irishgreencitrus/raylib.jl | +| jaylib | 4.2 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | GPLv3+CE | https://github.com/electronstudio/jaylib/ | +| raylib-j | 4.0 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | Zlib | https://github.com/CreedVI/Raylib-J | +| raylib.jl | 4.2 | [Julia](https://julialang.org/) | Zlib | https://github.com/irishgreencitrus/raylib.jl | | kaylib | 3.7 | [Kotlin/native](https://kotlinlang.org) | ? | https://github.com/electronstudio/kaylib | | kaylib | **4.5-dev**| [Kotlin/native](https://kotlinlang.org) | Zlib | https://codeberg.org/Kenta/Kaylib | -| raylib-lua | **4.2** | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | -| raylua | **4.0** | [Lua](http://www.lua.org/) | MIT | https://github.com/Rabios/raylua | +| raylib-lua | 4.2 | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | +| raylua | 4.0 | [Lua](http://www.lua.org/) | MIT | https://github.com/Rabios/raylua | | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | -| Raylib-Nelua | **4.5-dev** | [nelua](https://nelua.io/) | MIT | https://github.com/Its-Kenta/Raylib-Nelua | +| Raylib-Nelua | **4.5-dev** | [nelua](https://nelua.io/) | MIT | https://github.com/Its-Kenta/Raylib-Nelua | | NimraylibNow! | 4.2 | [Nim](https://nim-lang.org/) | MIT | https://github.com/greenfork/nimraylib_now | | raylib-Forever | auto | [Nim](https://nim-lang.org/) | ? | https://github.com/Guevara-chan/Raylib-Forever | | naylib | auto | [Nim](https://nim-lang.org/) | MIT | https://github.com/planetis-m/naylib | | node-raylib | **4.5** | [Node.js](https://nodejs.org/en/) | Zlib | https://github.com/RobLoach/node-raylib | | raylib-odin | **4.5** | [Odin](https://odin-lang.org/) | BSD-3Clause | https://github.com/odin-lang/Odin/tree/master/vendor/raylib | | raylib_odin_bindings | 4.0-dev | [Odin](https://odin-lang.org/) | MIT | https://github.com/Deathbat2190/raylib_odin_bindings | -| raylib-ocaml | **4.2** | [OCaml](https://ocaml.org/) | MIT | https://github.com/tjammer/raylib-ocaml | -| TurboRaylib | **4.2** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT | https://github.com/turborium/TurboRaylib | +| raylib-ocaml | 4.2 | [OCaml](https://ocaml.org/) | MIT | https://github.com/tjammer/raylib-ocaml | +| TurboRaylib | 4.2 | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT | https://github.com/turborium/TurboRaylib | | Ray4Laz | **4.5** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/GuvaCode/Ray4Laz | -| Raylib.4.0.Pascal | **4.0** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/sysrpl/Raylib.4.0.Pascal | +| Raylib.4.0.Pascal | 4.0 | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/sysrpl/Raylib.4.0.Pascal | | pyraylib | 3.7 | [Python](https://www.python.org/) | Zlib | https://github.com/Ho011/pyraylib | -| raylib-python-cffi | **4.2** | [Python](https://www.python.org/) | EPL-2.0 | https://github.com/electronstudio/raylib-python-cffi | -| raylibpyctbg | **4.2** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylibpyctbg | -| raylib-py | **4.2** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylib-py | -| raylib-python-ctypes | **4.2** | [Python](https://www.python.org/) | MIT | https://github.com/sDos280/raylib-python-ctypes | +| raylib-python-cffi | 4.2 | [Python](https://www.python.org/) | EPL-2.0 | https://github.com/electronstudio/raylib-python-cffi | +| raylibpyctbg | 4.2 | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylibpyctbg | +| raylib-py | 4.2 | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylib-py | +| raylib-python-ctypes | 4.2 | [Python](https://www.python.org/) | MIT | https://github.com/sDos280/raylib-python-ctypes | | raylib-php | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/joseph-montanez/raylib-php | | raylib-phpcpp | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/oraoto/raylib-phpcpp | -| raylibr | **4.0** | [R](https://www.r-project.org) | MIT | https://github.com/jeroenjanssens/raylibr | +| raylibr | 4.0 | [R](https://www.r-project.org) | MIT | https://github.com/jeroenjanssens/raylibr | | raylib-rs | 3.5 | [Rust](https://www.rust-lang.org/) | Zlib | https://github.com/deltaphc/raylib-rs | | Relib | 3.5 | [ReCT](https://github.com/RedCubeDev-ByteSpace/ReCT) | ? | https://github.com/RedCubeDev-ByteSpace/Relib | -| racket-raylib | **4.0** | [Racket](https://racket-lang.org/) | MIT/Apache-2.0 | https://github.com/eutro/racket-raylib | -| raylib-swift | **4.0** | [Swift](https://swift.org/) | MIT | https://github.com/STREGAsGate/Raylib | +| racket-raylib | 4.0 | [Racket](https://racket-lang.org/) | MIT/Apache-2.0 | https://github.com/eutro/racket-raylib | +| raylib-swift | 4.0 | [Swift](https://swift.org/) | MIT | https://github.com/STREGAsGate/Raylib | | raylib-scopes | auto | [Scopes](http://scopes.rocks) | MIT | https://github.com/salotz/raylib-scopes | | raylib-smallBasic | 4.1-dev | [SmallBASIC](https://github.com/smallbasic/SmallBASIC) | GPLv3 | https://github.com/smallbasic/smallbasic.plugins/tree/master/raylib | | raylib-umka | **4.5** | [Umka](https://github.com/vtereshkov/umka-lang) | Zlib | https://github.com/robloach/raylib-umka | -| raylib.v | **4.2** | [V](https://vlang.io/) | Zlib | https://github.com/irishgreencitrus/raylib.v | -| raylib-vapi | **4.2** | [Vala](https://vala.dev/) | Zlib | https://github.com/lxmcf/raylib-vapi | -| raylib-wren | **4.0** | [Wren](http://wren.io/) | ISC | https://github.com/TSnake41/raylib-wren | -| raylib-zig | **4.2** | [Zig](https://ziglang.org/) | MIT | https://github.com/Not-Nik/raylib-zig | +| raylib.v | 4.2 | [V](https://vlang.io/) | Zlib | https://github.com/irishgreencitrus/raylib.v | +| raylib-vapi | 4.2 | [Vala](https://vala.dev/) | Zlib | https://github.com/lxmcf/raylib-vapi | +| raylib-wren | 4.0 | [Wren](http://wren.io/) | ISC | https://github.com/TSnake41/raylib-wren | +| raylib-zig | 4.2 | [Zig](https://ziglang.org/) | MIT | https://github.com/Not-Nik/raylib-zig | | raylib.zig | **4.5** | [Zig](https://ziglang.org/) | MIT | https://github.com/ryupold/raylib.zig | -| hare-raylib | auto | [Hare](https://harelang.org/) | Zlib | https://git.sr.ht/~evantj/hare-raylib | -| raylib-sunder | auto | [Sunder](https://github.com/ashn-dot-dev/sunder) | 0BSD | https://github.com/ashn-dot-dev/raylib-sunder | -| rayed-bqn | auto | [BQN](https://mlochbaum.github.io/BQN/) | MIT | https://github.com/Brian-ED/rayed-bqn | +| hare-raylib | **auto** | [Hare](https://harelang.org/) | Zlib | https://git.sr.ht/~evantj/hare-raylib | +| raylib-sunder | **auto** | [Sunder](https://github.com/ashn-dot-dev/sunder) | 0BSD | https://github.com/ashn-dot-dev/raylib-sunder | +| rayed-bqn | **auto** | [BQN](https://mlochbaum.github.io/BQN/) | MIT | https://github.com/Brian-ED/rayed-bqn | ### Utility Wrapers From 1a110dcbd73a9eeadd1b8aedef2d909b7536bda5 Mon Sep 17 00:00:00 2001 From: Astie Teddy Date: Sat, 25 Mar 2023 23:26:38 +0100 Subject: [PATCH 0027/1350] Update BINDINGS.md (raylib-lua -> 4.5) (#2989) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index e0585117e..38b969267 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -36,7 +36,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib.jl | 4.2 | [Julia](https://julialang.org/) | Zlib | https://github.com/irishgreencitrus/raylib.jl | | kaylib | 3.7 | [Kotlin/native](https://kotlinlang.org) | ? | https://github.com/electronstudio/kaylib | | kaylib | **4.5-dev**| [Kotlin/native](https://kotlinlang.org) | Zlib | https://codeberg.org/Kenta/Kaylib | -| raylib-lua | 4.2 | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | +| raylib-lua | **4.5** | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | | raylua | 4.0 | [Lua](http://www.lua.org/) | MIT | https://github.com/Rabios/raylua | | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | | Raylib-Nelua | **4.5-dev** | [nelua](https://nelua.io/) | MIT | https://github.com/Its-Kenta/Raylib-Nelua | From 17c443ee6db0a111d111e766bb517657204c3574 Mon Sep 17 00:00:00 2001 From: "Jorge A. Gomes" Date: Wed, 29 Mar 2023 08:52:33 -0300 Subject: [PATCH 0028/1350] Update BINDINGS.md (raylib-py -> 4.5) (#2992) Co-authored-by: Ray --- BINDINGS.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/BINDINGS.md b/BINDINGS.md index 38b969267..2835bb9c9 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -51,10 +51,10 @@ Some people ported raylib to other languages in form of bindings or wrappers to | Ray4Laz | **4.5** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/GuvaCode/Ray4Laz | | Raylib.4.0.Pascal | 4.0 | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/sysrpl/Raylib.4.0.Pascal | | pyraylib | 3.7 | [Python](https://www.python.org/) | Zlib | https://github.com/Ho011/pyraylib | -| raylib-python-cffi | 4.2 | [Python](https://www.python.org/) | EPL-2.0 | https://github.com/electronstudio/raylib-python-cffi | -| raylibpyctbg | 4.2 | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylibpyctbg | -| raylib-py | 4.2 | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylib-py | -| raylib-python-ctypes | 4.2 | [Python](https://www.python.org/) | MIT | https://github.com/sDos280/raylib-python-ctypes | +| raylib-python-cffi | 4.2 | [Python](https://www.python.org/) | EPL-2.0 | https://github.com/electronstudio/raylib-python-cffi | +| raylibpyctbg | **4.5** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylibpyctbg | +| raylib-py | **4.5** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylib-py | +| raylib-python-ctypes | 4.2 | [Python](https://www.python.org/) | MIT | https://github.com/sDos280/raylib-python-ctypes | | raylib-php | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/joseph-montanez/raylib-php | | raylib-phpcpp | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/oraoto/raylib-phpcpp | | raylibr | 4.0 | [R](https://www.r-project.org) | MIT | https://github.com/jeroenjanssens/raylibr | From 8367abad1a925fc7cf60fd8e71b5f6dbf6250ddb Mon Sep 17 00:00:00 2001 From: chocolate42 <95491609+chocolate42@users.noreply.github.com> Date: Thu, 6 Apr 2023 11:34:37 +0100 Subject: [PATCH 0029/1350] [rtext] Fix GetCodepointNext() to return default value on invalid input with size=0 (#2997) * Fix GetCodepointNext to return default value with size=0 on invalid input. Modify LoadCodepoints to work when GetCodepointNext returns a size of 0. All internal use of GetCodepointNext and GetCodepointPrev checked. This fix may break external code dealing with invalid input as the old code erroneously never returned a size of 0, external code that doesn't properly check for size=0 may endlessly loop or overflow a buffer on invalid input. * Change default behaviour of GetCodepointNext to return a size of 1 instead of 0. This matches existing prod behaviour and guarantees size 1..4 is returned. Simplify internal code that uses GetCodepointNext that previously had to account for size=0. * Simplified progressing through a UTF-8 string in ImageTextEx and MeasureTextEx. This change matches existing precedent in DrawTextEx * GetCodepointNext: Add 10xxxxxx checks to multibyte encodings. --------- Co-authored-by: anon --- src/rtext.c | 21 ++++++++------------- src/rtextures.c | 8 ++------ 2 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index 2d01360ef..dda994874 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1071,10 +1071,6 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f int codepoint = GetCodepointNext(&text[i], &codepointByteCount); int index = GetGlyphIndex(font, codepoint); - // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) - // but we need to draw all the bad bytes using the '?' symbol moving one byte - if (codepoint == 0x3f) codepointByteCount = 1; - if (codepoint == '\n') { // NOTE: Fixed line spacing of 1.5 line-height @@ -1205,7 +1201,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing int letter = 0; // Current character int index = 0; // Index position in sprite font - for (int i = 0; i < size; i++) + for (int i = 0; i < size;) { byteCounter++; @@ -1213,10 +1209,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing letter = GetCodepointNext(&text[i], &next); index = GetGlyphIndex(font, letter); - // NOTE: normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) - // but we need to draw all the bad bytes using the '?' symbol so to not skip any we set next = 1 - if (letter == 0x3f) next = 1; - i += next - 1; + i += next; if (letter != '\n') { @@ -1734,8 +1727,7 @@ int GetCodepointCount(const char *text) int next = 0; int letter = GetCodepointNext(ptr, &next); - if (letter == 0x3f) ptr += 1; - else ptr += next; + ptr += next; length++; } @@ -1896,28 +1888,31 @@ int GetCodepointNext(const char *text, int *codepointSize) { const char *ptr = text; int codepoint = 0x3f; // Codepoint (defaults to '?') - *codepointSize = 0; + *codepointSize = 1; // Get current codepoint and bytes processed if (0xf0 == (0xf8 & ptr[0])) { // 4 byte UTF-8 codepoint + if(((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80) || ((ptr[3] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks codepoint = ((0x07 & ptr[0]) << 18) | ((0x3f & ptr[1]) << 12) | ((0x3f & ptr[2]) << 6) | (0x3f & ptr[3]); *codepointSize = 4; } else if (0xe0 == (0xf0 & ptr[0])) { // 3 byte UTF-8 codepoint */ + if(((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]); *codepointSize = 3; } else if (0xc0 == (0xe0 & ptr[0])) { // 2 byte UTF-8 codepoint + if((ptr[1] & 0xC0) ^ 0x80) { return codepoint; } //10xxxxxx checks codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]); *codepointSize = 2; } - else + else if (0x00 == (0x80 & ptr[0])) { // 1 byte UTF-8 codepoint codepoint = ptr[0]; diff --git a/src/rtextures.c b/src/rtextures.c index fb63f2ff0..bd652e6b3 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -1266,17 +1266,13 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co // Create image to store text imText = GenImageColor((int)imSize.x, (int)imSize.y, BLANK); - for (int i = 0; i < size; i++) + for (int i = 0; i < size;) { // Get next codepoint from byte string and glyph index in font int codepointByteCount = 0; int codepoint = GetCodepointNext(&text[i], &codepointByteCount); // WARNING: Module required: rtext int index = GetGlyphIndex(font, codepoint); // WARNING: Module required: rtext - // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) - // but we need to draw all the bad bytes using the '?' symbol moving one byte - if (codepoint == 0x3f) codepointByteCount = 1; - if (codepoint == '\n') { // NOTE: Fixed line spacing of 1.5 line-height @@ -1296,7 +1292,7 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co else textOffsetX += font.glyphs[index].advanceX + (int)spacing; } - i += (codepointByteCount - 1); // Move text bytes counter to next codepoint + i += codepointByteCount; // Move text bytes counter to next codepoint } // Scale image depending on text size From d8c7b01a3cef6ebf3f5ea7db3974c1de2316171d Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 6 Apr 2023 12:49:59 +0200 Subject: [PATCH 0030/1350] REVIEWED: `GetGlyphIndex()` #3000 --- src/rtext.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index dda994874..1d2b4f35b 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1239,17 +1239,17 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing // NOTE: If codepoint is not found in the font it fallbacks to '?' int GetGlyphIndex(Font font, int codepoint) { -#ifndef GLYPH_NOTFOUND_CHAR_FALLBACK - #define GLYPH_NOTFOUND_CHAR_FALLBACK 63 // Character used if requested codepoint is not found: '?' -#endif - -// Support charsets with any characters order + int index = 0; + #define SUPPORT_UNORDERED_CHARSET #if defined(SUPPORT_UNORDERED_CHARSET) - int index = GLYPH_NOTFOUND_CHAR_FALLBACK; + int fallbackIndex = 0; // Get index of fallback glyph '?' + // Look for character index in the unordered charset for (int i = 0; i < font.glyphCount; i++) { + if (font.glyphs[i].value == 63) fallbackIndex = i; + if (font.glyphs[i].value == codepoint) { index = i; @@ -1257,10 +1257,12 @@ int GetGlyphIndex(Font font, int codepoint) } } - return index; + if ((index == 0) && (font.glyphs[0].value != codepoint)) index = fallbackIndex; #else - return (codepoint - 32); + index = codepoint - 32; #endif + + return index; } // Get glyph font info data for a codepoint (unicode character) From 06c17ab7f1eec157f70a9fde702a6332c89763af Mon Sep 17 00:00:00 2001 From: fubark Date: Sat, 8 Apr 2023 10:05:41 -0700 Subject: [PATCH 0031/1350] Update BINDINGS.md (#3002) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 2835bb9c9..beb1e25aa 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -14,7 +14,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | claylib/wrap | 4.2 | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | | chez-raylib | **auto** | [Chez Scheme](https://cisco.github.io/ChezScheme/) | GPLv3 | https://github.com/Yunoinsky/chez-raylib | | raylib-cr | **4.5-dev (7e7939e)** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | -| ray-cyber | 4.2 | [Cyber](https://cyberscript.dev) | MIT | https://github.com/fubark/ray-cyber | +| ray-cyber | 4.5 | [Cyber](https://cyberscript.dev) | MIT | https://github.com/fubark/ray-cyber | | raylib-c3 | **4.5-dev** | [C3](https://c3-lang.org/) | MIT | https://github.com/Its-Kenta/Raylib-C3 | | dart-raylib | 4.0 | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | | bindbc-raylib3 | 4.0 | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | From e57ee9c0e84de9cbf6fddf2f46786e87c64cb64e Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Sun, 9 Apr 2023 13:42:15 -0700 Subject: [PATCH 0032/1350] Fix warnings in raylib for MSVC (#3004) --- src/raudio.c | 8 ++++++++ src/utils.c | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index d7ee183a8..1eec97dab 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -228,6 +228,14 @@ typedef struct tagBITMAPINFOHEADER { #define QOA_MALLOC RL_MALLOC #define QOA_FREE RL_FREE +#if defined(_MSC_VER ) // par shapes has 2 warnings on windows, so disable them just fof this file +#pragma warning( push ) +#pragma warning( disable : 4018) +#pragma warning( disable : 4267) +#pragma warning( disable : 4244) +#endif + + #define QOA_IMPLEMENTATION #include "external/qoa.h" // QOA loading and saving functions #include "external/qoaplay.c" // QOA stream playing helper functions diff --git a/src/utils.c b/src/utils.c index 771271d35..da92ce520 100644 --- a/src/utils.c +++ b/src/utils.c @@ -145,7 +145,7 @@ void TraceLog(int logType, const char *text, ...) default: break; } - unsigned int textSize = strlen(text); + unsigned int textSize = (unsigned int)strlen(text); memcpy(buffer + strlen(buffer), text, (textSize < (MAX_TRACELOG_MSG_LENGTH - 12))? textSize : (MAX_TRACELOG_MSG_LENGTH - 12)); strcat(buffer, "\n"); vprintf(buffer, args); From 8f741d894ab350eec275df8eacf85636d38861f3 Mon Sep 17 00:00:00 2001 From: eternalStudent Date: Sun, 9 Apr 2023 23:43:06 +0300 Subject: [PATCH 0033/1350] Minor fix in DrawLineBezier* (#3006) When `i` starts with `0`, `t` is also `0`, which results in `previous == startPos == current`, this segment is not only redundant, but it also causes division-by-zero since `sqrtf(dx*dx + dy*dy)` is zero. --- src/rshapes.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index f7ee8f5cd..52c5648da 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -241,7 +241,7 @@ void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, fl Vector2 points[2*BEZIER_LINE_DIVISIONS + 2] = { 0 }; - for (int i = 0; i <= BEZIER_LINE_DIVISIONS; i++) + for (int i = 1; i <= BEZIER_LINE_DIVISIONS; i++) { t = step*i; float a = powf(1 - t, 2); @@ -286,7 +286,7 @@ void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlP Vector2 points[2*BEZIER_LINE_DIVISIONS + 2] = { 0 }; - for (int i = 0; i <= BEZIER_LINE_DIVISIONS; i++) + for (int i = 1; i <= BEZIER_LINE_DIVISIONS; i++) { t = step*i; float a = powf(1 - t, 3); From 9aa71f04f2410319f7a8862a073bb9639939a4a3 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 10 Apr 2023 11:02:00 +0200 Subject: [PATCH 0034/1350] Avoid tracelog about not found uniforms #3003 --- src/rlgl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 86208de4c..937535a56 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3923,8 +3923,8 @@ int rlGetLocationUniform(unsigned int shaderId, const char *uniformName) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) location = glGetUniformLocation(shaderId, uniformName); - if (location == -1) TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to find shader uniform: %s", shaderId, uniformName); - else TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Shader uniform (%s) set at location: %i", shaderId, uniformName, location); + //if (location == -1) TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to find shader uniform: %s", shaderId, uniformName); + //else TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Shader uniform (%s) set at location: %i", shaderId, uniformName, location); #endif return location; } From 709b14180a6ed0379c134bd86612994cb2f36f80 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 13 Apr 2023 23:08:32 +0200 Subject: [PATCH 0035/1350] Make assets loading extension case insensitive #3008 --- src/raudio.c | 24 ++++++++++++------------ src/rtextures.c | 32 +++++++++++++++++--------------- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 1eec97dab..d636b8fe2 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -749,7 +749,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int if (false) { } #if defined(SUPPORT_FILEFORMAT_WAV) - else if (strcmp(fileType, ".wav") == 0) + else if ((strcmp(fileType, ".wav") == 0) || (strcmp(fileType, ".WAV") == 0)) { drwav wav = { 0 }; bool success = drwav_init_memory(&wav, fileData, dataSize, NULL); @@ -771,7 +771,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int } #endif #if defined(SUPPORT_FILEFORMAT_OGG) - else if (strcmp(fileType, ".ogg") == 0) + else if ((strcmp(fileType, ".ogg") == 0) || (strcmp(fileType, ".OGG") == 0)) { stb_vorbis *oggData = stb_vorbis_open_memory((unsigned char *)fileData, dataSize, NULL, NULL); @@ -793,7 +793,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int } #endif #if defined(SUPPORT_FILEFORMAT_MP3) - else if (strcmp(fileType, ".mp3") == 0) + else if ((strcmp(fileType, ".mp3") == 0) || (strcmp(fileType, ".MP3") == 0)) { drmp3_config config = { 0 }; unsigned long long int totalFrameCount = 0; @@ -813,7 +813,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int } #endif #if defined(SUPPORT_FILEFORMAT_QOA) - else if (strcmp(fileType, ".qoa") == 0) + else if ((strcmp(fileType, ".qoa") == 0) || (strcmp(fileType, ".QOA") == 0)) { qoa_desc qoa = { 0 }; @@ -832,7 +832,7 @@ Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileData, int } #endif #if defined(SUPPORT_FILEFORMAT_FLAC) - else if (strcmp(fileType, ".flac") == 0) + else if ((strcmp(fileType, ".flac") == 0) || (strcmp(fileType, ".FLAC") == 0)) { unsigned long long int totalFrameCount = 0; @@ -1425,7 +1425,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, if (false) { } #if defined(SUPPORT_FILEFORMAT_WAV) - else if (strcmp(fileType, ".wav") == 0) + else if ((strcmp(fileType, ".wav") == 0) || (strcmp(fileType, ".WAV") == 0)) { drwav *ctxWav = RL_CALLOC(1, sizeof(drwav)); @@ -1447,7 +1447,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, } #endif #if defined(SUPPORT_FILEFORMAT_OGG) - else if (strcmp(fileType, ".ogg") == 0) + else if ((strcmp(fileType, ".ogg") == 0) || (strcmp(fileType, ".OGG") == 0)) { // Open ogg audio stream music.ctxType = MUSIC_AUDIO_OGG; @@ -1469,7 +1469,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, } #endif #if defined(SUPPORT_FILEFORMAT_MP3) - else if (strcmp(fileType, ".mp3") == 0) + else if ((strcmp(fileType, ".mp3") == 0) || (strcmp(fileType, ".MP3") == 0)) { drmp3 *ctxMp3 = RL_CALLOC(1, sizeof(drmp3)); int success = drmp3_init_memory(ctxMp3, (const void*)data, dataSize, NULL); @@ -1487,7 +1487,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, } #endif #if defined(SUPPORT_FILEFORMAT_QOA) - else if (strcmp(fileType, ".qoa") == 0) + else if ((strcmp(fileType, ".qoa") == 0) || (strcmp(fileType, ".QOA") == 0)) { qoaplay_desc *ctxQoa = qoaplay_open_memory(data, dataSize); music.ctxType = MUSIC_AUDIO_QOA; @@ -1505,7 +1505,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, } #endif #if defined(SUPPORT_FILEFORMAT_FLAC) - else if (strcmp(fileType, ".flac") == 0) + else if ((strcmp(fileType, ".flac") == 0) || (strcmp(fileType, ".FLAC") == 0)) { music.ctxType = MUSIC_AUDIO_FLAC; music.ctxData = drflac_open_memory((const void*)data, dataSize, NULL); @@ -1522,7 +1522,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, } #endif #if defined(SUPPORT_FILEFORMAT_XM) - else if (strcmp(fileType, ".xm") == 0) + else if ((strcmp(fileType, ".xm") == 0) || (strcmp(fileType, ".XM") == 0)) { jar_xm_context_t *ctxXm = NULL; int result = jar_xm_create_context_safe(&ctxXm, (const char *)data, dataSize, AUDIO.System.device.sampleRate); @@ -1547,7 +1547,7 @@ Music LoadMusicStreamFromMemory(const char *fileType, const unsigned char *data, } #endif #if defined(SUPPORT_FILEFORMAT_MOD) - else if (strcmp(fileType, ".mod") == 0) + else if ((strcmp(fileType, ".mod") == 0) || (strcmp(fileType, ".MOD") == 0)) { jar_mod_context_t *ctxMod = (jar_mod_context_t *)RL_MALLOC(sizeof(jar_mod_context_t)); int result = 0; diff --git a/src/rtextures.c b/src/rtextures.c index bd652e6b3..20244d914 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -340,28 +340,30 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i if ((false) #if defined(SUPPORT_FILEFORMAT_PNG) - || (strcmp(fileType, ".png") == 0) + || (strcmp(fileType, ".png") == 0) || (strcmp(fileType, ".PNG") == 0) #endif #if defined(SUPPORT_FILEFORMAT_BMP) - || (strcmp(fileType, ".bmp") == 0) + || (strcmp(fileType, ".bmp") == 0) || (strcmp(fileType, ".BMP") == 0) #endif #if defined(SUPPORT_FILEFORMAT_TGA) - || (strcmp(fileType, ".tga") == 0) + || (strcmp(fileType, ".tga") == 0) || (strcmp(fileType, ".TGA") == 0) #endif #if defined(SUPPORT_FILEFORMAT_JPG) - || ((strcmp(fileType, ".jpg") == 0) || (strcmp(fileType, ".jpeg") == 0)) + || (strcmp(fileType, ".jpg") == 0) || (strcmp(fileType, ".jpeg") == 0) + || (strcmp(fileType, ".JPG") == 0) || (strcmp(fileType, ".JPEG") == 0) #endif #if defined(SUPPORT_FILEFORMAT_GIF) - || (strcmp(fileType, ".gif") == 0) + || (strcmp(fileType, ".gif") == 0) || (strcmp(fileType, ".GIF") == 0) #endif #if defined(SUPPORT_FILEFORMAT_PIC) - || (strcmp(fileType, ".pic") == 0) + || (strcmp(fileType, ".pic") == 0) || (strcmp(fileType, ".PIC") == 0) #endif #if defined(SUPPORT_FILEFORMAT_PNM) - || ((strcmp(fileType, ".ppm") == 0) || (strcmp(fileType, ".pgm") == 0)) + || (strcmp(fileType, ".ppm") == 0) || (strcmp(fileType, ".pgm") == 0) + || (strcmp(fileType, ".PPM") == 0) || (strcmp(fileType, ".PGM") == 0) #endif #if defined(SUPPORT_FILEFORMAT_PSD) - || (strcmp(fileType, ".psd") == 0) + || (strcmp(fileType, ".psd") == 0) || (strcmp(fileType, ".PSD") == 0) #endif ) { @@ -386,7 +388,7 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i #endif } #if defined(SUPPORT_FILEFORMAT_HDR) - else if (strcmp(fileType, ".hdr") == 0) + else if ((strcmp(fileType, ".hdr") == 0) || (strcmp(fileType, ".HDR") == 0)) { #if defined(STBI_REQUIRED) if (fileData != NULL) @@ -409,7 +411,7 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i } #endif #if defined(SUPPORT_FILEFORMAT_QOI) - else if (strcmp(fileType, ".qoi") == 0) + else if ((strcmp(fileType, ".qoi") == 0) || (strcmp(fileType, ".QOI") == 0)) { qoi_desc desc = { 0 }; image.data = qoi_decode(fileData, dataSize, &desc, 4); @@ -420,31 +422,31 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i } #endif #if defined(SUPPORT_FILEFORMAT_DDS) - else if (strcmp(fileType, ".dds") == 0) + else if ((strcmp(fileType, ".dds") == 0) || (strcmp(fileType, ".DDS") == 0)) { image.data = rl_load_dds_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); } #endif #if defined(SUPPORT_FILEFORMAT_PKM) - else if (strcmp(fileType, ".pkm") == 0) + else if ((strcmp(fileType, ".pkm") == 0) || (strcmp(fileType, ".PKM") == 0)) { image.data = rl_load_pkm_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); } #endif #if defined(SUPPORT_FILEFORMAT_KTX) - else if (strcmp(fileType, ".ktx") == 0) + else if ((strcmp(fileType, ".ktx") == 0) || (strcmp(fileType, ".KTX") == 0)) { image.data = rl_load_ktx_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); } #endif #if defined(SUPPORT_FILEFORMAT_PVR) - else if (strcmp(fileType, ".pvr") == 0) + else if ((strcmp(fileType, ".pvr") == 0) || (strcmp(fileType, ".PVR") == 0)) { image.data = rl_load_pvr_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); } #endif #if defined(SUPPORT_FILEFORMAT_ASTC) - else if (strcmp(fileType, ".astc") == 0) + else if ((strcmp(fileType, ".astc") == 0) || (strcmp(fileType, ".ASTC") == 0)) { image.data = rl_load_astc_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); } From 70d7f67bd87cbb4782217d87312159f42de00948 Mon Sep 17 00:00:00 2001 From: Benjamin Thomas Date: Sat, 15 Apr 2023 10:55:40 +0200 Subject: [PATCH 0036/1350] CMake project example: fix a couple of typos (#3014) --- projects/CMake/core_basic_window.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/projects/CMake/core_basic_window.c b/projects/CMake/core_basic_window.c index 4357ae598..0e5542967 100644 --- a/projects/CMake/core_basic_window.c +++ b/projects/CMake/core_basic_window.c @@ -3,7 +3,7 @@ * raylib [core] example - Basic window (adapted for HTML5 platform) * * This example is prepared to compile for PLATFORM_WEB, PLATFORM_DESKTOP and PLATFORM_RPI -* As you will notice, code structure is slightly diferent to the other examples... +* As you will notice, code structure is slightly different to the other examples... * To compile it for PLATFORM_WEB just uncomment #define PLATFORM_WEB at beginning * * This example has been created using raylib 1.3 (www.raylib.com) @@ -31,7 +31,7 @@ int screenHeight = 450; void UpdateDrawFrame(void); // Update and Draw one frame //---------------------------------------------------------------------------------- -// Main Enry Point +// Main Entry Point //---------------------------------------------------------------------------------- int main() { @@ -80,4 +80,4 @@ void UpdateDrawFrame(void) EndDrawing(); //---------------------------------------------------------------------------------- -} \ No newline at end of file +} From e2da32e2daf2cf4de86cc1128a7b3ba66a1bab1c Mon Sep 17 00:00:00 2001 From: RadsammyT <32146976+RadsammyT@users.noreply.github.com> Date: Sat, 15 Apr 2023 04:58:00 -0400 Subject: [PATCH 0037/1350] [raudio] Rewritten `ExportWaveAsCode()` file saving to be more like rtextures `ExportImageAsCode()` (#3013) * Update raudio.c Review `raudio.c`: rewritten `ExportWaveAsCode()` to be more like rtextures.c `ExportImageAsCode()' * no tab november accidentally inserted a tab somewhere. corrected it. --- src/raudio.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index d636b8fe2..cddc08354 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1038,29 +1038,30 @@ bool ExportWaveAsCode(Wave wave, const char *fileName) byteCount += sprintf(txtData + byteCount, "// //\n"); byteCount += sprintf(txtData + byteCount, "//////////////////////////////////////////////////////////////////////////////////\n\n"); - char fileNameLower[256] = { 0 }; - char fileNameUpper[256] = { 0 }; - for (int i = 0; fileName[i] != '.'; i++) { fileNameLower[i] = fileName[i]; } // Get filename without extension - for (int i = 0; fileNameLower[i] != '\0'; i++) if (fileNameLower[i] >= 'a' && fileNameLower[i] <= 'z') { fileNameUpper[i] = fileNameLower[i] - 32; } + // Get file name from path and convert variable name to uppercase + char varFileName[256] = { 0 }; + strcpy(varFileName, GetFileNameWithoutExt(fileName)); + for (int i = 0; varFileName[i] != '\0'; i++) if (varFileName[i] >= 'a' && varFileName[i] <= 'z') { varFileName[i] = varFileName[i] - 32; } + //Add wave information byteCount += sprintf(txtData + byteCount, "// Wave data information\n"); - byteCount += sprintf(txtData + byteCount, "#define %s_FRAME_COUNT %u\n", fileNameUpper, wave.frameCount); - byteCount += sprintf(txtData + byteCount, "#define %s_SAMPLE_RATE %u\n", fileNameUpper, wave.sampleRate); - byteCount += sprintf(txtData + byteCount, "#define %s_SAMPLE_SIZE %u\n", fileNameUpper, wave.sampleSize); - byteCount += sprintf(txtData + byteCount, "#define %s_CHANNELS %u\n\n", fileNameUpper, wave.channels); + byteCount += sprintf(txtData + byteCount, "#define %s_FRAME_COUNT %u\n", varFileName, wave.frameCount); + byteCount += sprintf(txtData + byteCount, "#define %s_SAMPLE_RATE %u\n", varFileName, wave.sampleRate); + byteCount += sprintf(txtData + byteCount, "#define %s_SAMPLE_SIZE %u\n", varFileName, wave.sampleSize); + byteCount += sprintf(txtData + byteCount, "#define %s_CHANNELS %u\n\n", varFileName, wave.channels); // Write wave data as an array of values // Wave data is exported as byte array for 8/16bit and float array for 32bit float data // NOTE: Frame data exported is channel-interlaced: frame01[sampleChannel1, sampleChannel2, ...], frame02[], frame03[] if (wave.sampleSize == 32) { - byteCount += sprintf(txtData + byteCount, "static float %sData[%i] = {\n", fileNameLower, waveDataSize/4); + byteCount += sprintf(txtData + byteCount, "static float %s_DATA[%i] = {\n", varFileName, waveDataSize/4); for (int i = 1; i < waveDataSize/4; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "%.4ff,\n " : "%.4ff, "), ((float *)wave.data)[i - 1]); byteCount += sprintf(txtData + byteCount, "%.4ff };\n", ((float *)wave.data)[waveDataSize/4 - 1]); } else { - byteCount += sprintf(txtData + byteCount, "static unsigned char %sData[%i] = { ", fileNameLower, waveDataSize); + byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%i] = { ", varFileName, waveDataSize); for (int i = 1; i < waveDataSize; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%x,\n " : "0x%x, "), ((unsigned char *)wave.data)[i - 1]); byteCount += sprintf(txtData + byteCount, "0x%x };\n", ((unsigned char *)wave.data)[waveDataSize - 1]); } From 9d38363e09aa8725415b444cde37a909d4d9d8a0 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 17 Apr 2023 12:18:06 +0200 Subject: [PATCH 0038/1350] Remove trailing spaces --- src/rcore.c | 2 +- src/rtext.c | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index b1632492b..3f2a1df8d 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -6949,7 +6949,7 @@ static void LoadAutomationEvents(const char *fileName) /* FILE *repFile = fopen(fileName, "rb"); unsigned char fileId[4] = { 0 }; - + fread(fileId, 1, 4, repFile); if ((fileId[0] == 'r') && (fileId[1] == 'E') && (fileId[2] == 'P') && (fileId[1] == ' ')) diff --git a/src/rtext.c b/src/rtext.c index 1d2b4f35b..2df8be8dc 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -697,18 +697,18 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC // Calculate image size based on total glyph width and glyph row count int totalWidth = 0; int maxGlyphWidth = 0; - + for (int i = 0; i < glyphCount; i++) { if (chars[i].image.width > maxGlyphWidth) maxGlyphWidth = chars[i].image.width; totalWidth += chars[i].image.width + 2*padding; } - + int rowCount = 0; int imageSize = 64; // Define minimum starting value to avoid unnecessary calculation steps for very small images - + // NOTE: maxGlyphWidth is maximum possible space left at the end of row - while (totalWidth > (imageSize - maxGlyphWidth)*rowCount) + while (totalWidth > (imageSize - maxGlyphWidth)*rowCount) { imageSize *= 2; // Double the size of image (to keep POT) rowCount = imageSize/(fontSize + 2*padding); // Calculate new row count for the new image size @@ -1240,7 +1240,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing int GetGlyphIndex(Font font, int codepoint) { int index = 0; - + #define SUPPORT_UNORDERED_CHARSET #if defined(SUPPORT_UNORDERED_CHARSET) int fallbackIndex = 0; // Get index of fallback glyph '?' @@ -1249,7 +1249,7 @@ int GetGlyphIndex(Font font, int codepoint) for (int i = 0; i < font.glyphCount; i++) { if (font.glyphs[i].value == 63) fallbackIndex = i; - + if (font.glyphs[i].value == codepoint) { index = i; From 771957458daa8b25c7f89c3ec29dd739075457e2 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 17 Apr 2023 17:50:46 +0200 Subject: [PATCH 0039/1350] Avoid shader attribute not found log --- src/rlgl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 937535a56..e786fa380 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3936,8 +3936,8 @@ int rlGetLocationAttrib(unsigned int shaderId, const char *attribName) #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) location = glGetAttribLocation(shaderId, attribName); - if (location == -1) TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to find shader attribute: %s", shaderId, attribName); - else TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Shader attribute (%s) set at location: %i", shaderId, attribName, location); + //if (location == -1) TRACELOG(RL_LOG_WARNING, "SHADER: [ID %i] Failed to find shader attribute: %s", shaderId, attribName); + //else TRACELOG(RL_LOG_INFO, "SHADER: [ID %i] Shader attribute (%s) set at location: %i", shaderId, attribName, location); #endif return location; } From 535680668be3ca2e688a73132150c16f0160e0cc Mon Sep 17 00:00:00 2001 From: Soutaisei <131299089+Soutaisei@users.noreply.github.com> Date: Wed, 19 Apr 2023 20:25:25 +0100 Subject: [PATCH 0040/1350] Update BINDINGS.md (#3017) Update Kaylib to Raylib version 4.5 --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index beb1e25aa..2191e88bb 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -35,7 +35,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-j | 4.0 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | Zlib | https://github.com/CreedVI/Raylib-J | | raylib.jl | 4.2 | [Julia](https://julialang.org/) | Zlib | https://github.com/irishgreencitrus/raylib.jl | | kaylib | 3.7 | [Kotlin/native](https://kotlinlang.org) | ? | https://github.com/electronstudio/kaylib | -| kaylib | **4.5-dev**| [Kotlin/native](https://kotlinlang.org) | Zlib | https://codeberg.org/Kenta/Kaylib | +| kaylib | **4.5**| [Kotlin/native](https://kotlinlang.org) | Zlib | https://codeberg.org/Soutaisei/Kaylib | | raylib-lua | **4.5** | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | | raylua | 4.0 | [Lua](http://www.lua.org/) | MIT | https://github.com/Rabios/raylua | | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | From 7b7c0c83ef3d52695b7c939ad181e7ca3faddbf6 Mon Sep 17 00:00:00 2001 From: Mingjie Shen Date: Sat, 22 Apr 2023 04:13:11 -0400 Subject: [PATCH 0041/1350] Fix offset used before range check (#3021) This use of offset 'i' should follow the range check. --- examples/shapes/raygui.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/shapes/raygui.h b/examples/shapes/raygui.h index 112370aca..ea95e5c85 100644 --- a/examples/shapes/raygui.h +++ b/examples/shapes/raygui.h @@ -3743,7 +3743,7 @@ static int GetTextWidth(const char *text) { if (text[0] == '#') { - for (int i = 1; (text[i] != '\0') && (i < 5); i++) + for (int i = 1; (i < 5) && (text[i] != '\0'); i++) { if (text[i] == '#') { From 2d04dd8b88959bf28f070ba5d8631088e1273b56 Mon Sep 17 00:00:00 2001 From: Dan Bechard Date: Sat, 22 Apr 2023 04:15:19 -0400 Subject: [PATCH 0042/1350] Fix off-by-one error in CheckCollisionPointRec (#3022) Checking `<= x + w` causes off-by-one error where `CheckCollisionPointRec` will return true at the same time for two rectangles rendered right next to each, but which don't overlap (e.g. when making a 2D tile editor). This is clearly not what was intended. --- src/rshapes.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rshapes.c b/src/rshapes.c index 52c5648da..5d7eae0ad 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -1601,7 +1601,7 @@ bool CheckCollisionPointRec(Vector2 point, Rectangle rec) { bool collision = false; - if ((point.x >= rec.x) && (point.x <= (rec.x + rec.width)) && (point.y >= rec.y) && (point.y <= (rec.y + rec.height))) collision = true; + if ((point.x >= rec.x) && (point.x < (rec.x + rec.width)) && (point.y >= rec.y) && (point.y < (rec.y + rec.height))) collision = true; return collision; } From 05af08080e49fdcdfd603e11c0ddd7d80f0b63ae Mon Sep 17 00:00:00 2001 From: Dor Shapira <107134807+sDos280@users.noreply.github.com> Date: Sat, 22 Apr 2023 20:38:56 +0300 Subject: [PATCH 0043/1350] Update BINDINGS.md (#3023) update raypyc version to 4.6-dev --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 2191e88bb..057e6852b 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -54,7 +54,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-python-cffi | 4.2 | [Python](https://www.python.org/) | EPL-2.0 | https://github.com/electronstudio/raylib-python-cffi | | raylibpyctbg | **4.5** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylibpyctbg | | raylib-py | **4.5** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylib-py | -| raylib-python-ctypes | 4.2 | [Python](https://www.python.org/) | MIT | https://github.com/sDos280/raylib-python-ctypes | +| raylib-python-ctypes | 4.6-dev | [Python](https://www.python.org/) | MIT | https://github.com/sDos280/raylib-python-ctypes | | raylib-php | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/joseph-montanez/raylib-php | | raylib-phpcpp | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/oraoto/raylib-phpcpp | | raylibr | 4.0 | [R](https://www.r-project.org) | MIT | https://github.com/jeroenjanssens/raylibr | From 838fc7e3033945a5df68a4e360405dc96e910987 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 22 Apr 2023 21:17:53 +0200 Subject: [PATCH 0044/1350] REVIEWED: Some old TODOs --- src/raudio.c | 2 +- src/rlgl.h | 7 +++---- src/rmodels.c | 7 +++---- src/rtextures.c | 4 +--- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index cddc08354..8dfead37f 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1389,7 +1389,7 @@ Music LoadMusicStream(const char *fileName) else if (music.ctxType == MUSIC_AUDIO_MP3) { drmp3_uninit((drmp3 *)music.ctxData); RL_FREE(music.ctxData); } #endif #if defined(SUPPORT_FILEFORMAT_QOA) - else if (music.ctxType == MUSIC_AUDIO_QOA) { /*TODO: Release QOA context data*/ RL_FREE(music.ctxData); } + else if (music.ctxType == MUSIC_AUDIO_QOA) qoaplay_close((qoaplay_desc *)music.ctxData); #endif #if defined(SUPPORT_FILEFORMAT_FLAC) else if (music.ctxType == MUSIC_AUDIO_FLAC) drflac_free((drflac *)music.ctxData, NULL); diff --git a/src/rlgl.h b/src/rlgl.h index e786fa380..38d4ebb0e 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1416,8 +1416,7 @@ void rlVertex3f(float x, float y, float z) RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].texcoords[2*RLGL.State.vertexCounter] = RLGL.State.texcoordx; RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].texcoords[2*RLGL.State.vertexCounter + 1] = RLGL.State.texcoordy; - // TODO: Add current normal - // By default rlVertexBuffer type does not store normals + // WARNING: By default rlVertexBuffer struct does not store normals // Add current color RLGL.currentBatch->vertexBuffer[RLGL.currentBatch->currentBuffer].colors[4*RLGL.State.vertexCounter] = RLGL.State.colorr; @@ -2047,7 +2046,7 @@ void rlglInit(int width, int height) if ((glDebugMessageCallback != NULL) && (glDebugMessageControl != NULL)) { glDebugMessageCallback(rlDebugMessageCallback, 0); - // glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, GL_DEBUG_SEVERITY_HIGH, 0, 0, GL_TRUE); // TODO: Filter message + // glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_ERROR, GL_DEBUG_SEVERITY_HIGH, 0, 0, GL_TRUE); // Debug context options: // - GL_DEBUG_OUTPUT - Faster version but not useful for breakpoints @@ -2648,7 +2647,7 @@ void rlDrawRenderBatch(rlRenderBatch *batch) // Update batch vertex buffers //------------------------------------------------------------------------------------------------------------ // NOTE: If there is not vertex data, buffers doesn't need to be updated (vertexCount > 0) - // TODO: If no data changed on the CPU arrays --> No need to re-update GPU arrays (change flag required) + // TODO: If no data changed on the CPU arrays --> No need to re-update GPU arrays (use a change detector flag?) if (RLGL.State.vertexCounter > 0) { // Activate elements VAO diff --git a/src/rmodels.c b/src/rmodels.c index 6db7016a2..6b2b4b9f0 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -4525,7 +4525,7 @@ static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, unsigned int animations[a].boneCount = iqmHeader->num_poses; animations[a].bones = RL_MALLOC(iqmHeader->num_poses*sizeof(BoneInfo)); animations[a].framePoses = RL_MALLOC(anim[a].num_frames*sizeof(Transform *)); - // animations[a].framerate = anim.framerate; // TODO: Use framerate? + // animations[a].framerate = anim.framerate; // TODO: Use animation framerate data? for (unsigned int j = 0; j < iqmHeader->num_poses; j++) { @@ -4825,7 +4825,7 @@ static Model LoadGLTF(const char *fileName) TRACELOG(LOG_DEBUG, " > Textures count: %i", data->textures_count); // Force reading data buffers (fills buffer_view->buffer->data) - // NOTE: If an uri is defined to base64 data or external path, it's automatically loaded -> TODO: Verify this assumption + // NOTE: If an uri is defined to base64 data or external path, it's automatically loaded result = cgltf_load_buffers(&options, data, fileName); if (result != cgltf_result_success) TRACELOG(LOG_INFO, "MODEL: [%s] Failed to load mesh/material buffers", fileName); @@ -5517,7 +5517,6 @@ static Model LoadVOX(const char *fileName) memcpy(pmesh->vertices, pvertices, size); // Copy indices - // TODO: Compute globals indices array size = voxarray.indices.used*sizeof(unsigned short); pmesh->indices = RL_MALLOC(size); memcpy(pmesh->indices, pindices, size); @@ -5803,7 +5802,7 @@ static Model LoadM3D(const char *fileName) model.bindPose[i].rotation.z = m3d->vertex[m3d->bone[i].ori].z; model.bindPose[i].rotation.w = m3d->vertex[m3d->bone[i].ori].w; - // TODO: if the orientation quaternion not normalized, then that's encoding scaling + // TODO: If the orientation quaternion is not normalized, then that's encoding scaling model.bindPose[i].rotation = QuaternionNormalize(model.bindPose[i].rotation); model.bindPose[i].scale.x = model.bindPose[i].scale.y = model.bindPose[i].scale.z = 1.0f; diff --git a/src/rtextures.c b/src/rtextures.c index 20244d914..102e244bb 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -326,8 +326,6 @@ Image LoadImageAnim(const char *fileName, int *frames) frameCount = 1; } - // TODO: Support APNG animated images - *frames = frameCount; return image; } @@ -1252,6 +1250,7 @@ Image ImageText(const char *text, int fontSize, Color color) } // Create an image from text (custom sprite font) +// WARNING: Module required: rtext Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Color tint) { Image imText = { 0 }; @@ -1305,7 +1304,6 @@ Image ImageTextEx(Font font, const char *text, float fontSize, float spacing, Co // Using nearest-neighbor scaling algorithm for default font // TODO: Allow defining the preferred scaling mechanism externally - // WARNING: Module required: rtext if (font.texture.id == GetFontDefault().texture.id) ImageResizeNN(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor)); else ImageResize(&imText, (int)(imSize.x*scaleFactor), (int)(imSize.y*scaleFactor)); } From b9b045cdd8b81446340d6d213eb3e2d79a171d12 Mon Sep 17 00:00:00 2001 From: Chris Date: Sun, 23 Apr 2023 10:41:34 +0100 Subject: [PATCH 0045/1350] Update BINDINGS.md (#3026) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 057e6852b..8f293e78e 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -8,7 +8,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to |:------------------:|:---------------:|:---------:|:----------:|-----------------------------------------------------------| | raylib | **4.5** | [C/C++](https://en.wikipedia.org/wiki/C_(programming_language)) | Zlib | https://github.com/raysan5/raylib | | raylib-boo | 3.7 | [Boo](http://boo-language.github.io/)| MIT | https://github.com/Rabios/raylib-boo | -| Raylib-cs | 4.2 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | https://github.com/ChrisDill/Raylib-cs | +| Raylib-cs | **4.5** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | https://github.com/ChrisDill/Raylib-cs | | Raylib-CsLo | 4.2 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo | | cl-raylib | 4.0 | [Common Lisp](https://common-lisp.net/) | MIT | https://github.com/longlene/cl-raylib | | claylib/wrap | 4.2 | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | From ac2e9cd00f05bf820faea737bb9beba75dc90529 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 23 Apr 2023 11:48:01 +0200 Subject: [PATCH 0046/1350] REVIEWED: Update `CORE.Input.Touch.pointCount` #3024 --- src/rcore.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 3f2a1df8d..bb8852826 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -5944,11 +5944,12 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) if (flags == AMOTION_EVENT_ACTION_POINTER_UP || flags == AMOTION_EVENT_ACTION_UP) { // One of the touchpoints is released, remove it from touch point arrays - for (int i = pointerIndex; (i < CORE.Input.Touch.pointCount-1) && (i < MAX_TOUCH_POINTS); i++) + for (int i = pointerIndex; (i < CORE.Input.Touch.pointCount - 1) && (i < MAX_TOUCH_POINTS); i++) { CORE.Input.Touch.pointId[i] = CORE.Input.Touch.pointId[i+1]; CORE.Input.Touch.position[i] = CORE.Input.Touch.position[i+1]; } + CORE.Input.Touch.pointCount--; } @@ -6720,20 +6721,22 @@ static void *EventThread(void *arg) if (CORE.Input.Mouse.currentPosition.y < 0) CORE.Input.Mouse.currentPosition.y = 0; if (CORE.Input.Mouse.currentPosition.y > CORE.Window.screen.height/CORE.Input.Mouse.scale.y) CORE.Input.Mouse.currentPosition.y = CORE.Window.screen.height/CORE.Input.Mouse.scale.y; } + + // Update touch point count + CORE.Input.Touch.pointCount = 0; + if (CORE.Input.Touch.position[0].x >= 0) CORE.Input.Touch.pointCount++; + if (CORE.Input.Touch.position[1].x >= 0) CORE.Input.Touch.pointCount++; + if (CORE.Input.Touch.position[2].x >= 0) CORE.Input.Touch.pointCount++; + if (CORE.Input.Touch.position[3].x >= 0) CORE.Input.Touch.pointCount++; #if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_RPI, PLATFORM_DRM if (gestureUpdate) { GestureEvent gestureEvent = { 0 }; - gestureEvent.pointCount = 0; gestureEvent.touchAction = touchAction; - - if (CORE.Input.Touch.position[0].x >= 0) gestureEvent.pointCount++; - if (CORE.Input.Touch.position[1].x >= 0) gestureEvent.pointCount++; - if (CORE.Input.Touch.position[2].x >= 0) gestureEvent.pointCount++; - if (CORE.Input.Touch.position[3].x >= 0) gestureEvent.pointCount++; - + gestureEvent.pointCount = CORE.Input.Touch.pointCount; + gestureEvent.pointId[0] = 0; gestureEvent.pointId[1] = 1; gestureEvent.pointId[2] = 2; From 6472928cf1443fdac73abee356709b7e529f62b4 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 25 Apr 2023 14:16:48 +0200 Subject: [PATCH 0047/1350] REVIEWED: `ImageDrawRectangleRec()` #3027 --- src/rtextures.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index 102e244bb..3c535d9db 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -2692,9 +2692,9 @@ void ImageClearBackground(Image *dst, Color color) int bytesPerPixel = GetPixelDataSize(1, 1, dst->format); // Repeat the first pixel data throughout the image - for (int i = 1; i < dst->width * dst->height; i++) + for (int i = 1; i < dst->width*dst->height; i++) { - memcpy(pSrcPixel + i * bytesPerPixel, pSrcPixel, bytesPerPixel); + memcpy(pSrcPixel + i*bytesPerPixel, pSrcPixel, bytesPerPixel); } } @@ -2996,6 +2996,12 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color) // Security check to avoid program crash if ((dst->data == NULL) || (dst->width == 0) || (dst->height == 0)) return; + // Security check to avoid drawing out of bounds in case of bad user data + if (rec.x < 0) { rec.width -= rec.x; rec.x = 0; } + if (rec.y < 0) { rec.height -= rec.y; rec.y = 0; } + if (rec.width < 0) rec.width = 0; + if (rec.heigh < 0) rec.height = 0; + int sy = (int)rec.y; int ey = sy + (int)rec.height; @@ -3008,13 +3014,13 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color) // Fill in the first pixel of the row based on image format ImageDrawPixel(dst, sx, y, color); - int bytesOffset = ((y * dst->width) + sx) * bytesPerPixel; + int bytesOffset = ((y*dst->width) + sx)*bytesPerPixel; unsigned char *pSrcPixel = (unsigned char *)dst->data + bytesOffset; // Repeat the first pixel data throughout the row for (int x = 1; x < (int)rec.width; x++) { - memcpy(pSrcPixel + x * bytesPerPixel, pSrcPixel, bytesPerPixel); + memcpy(pSrcPixel + x*bytesPerPixel, pSrcPixel, bytesPerPixel); } } } From 64f2f86d325a46856c9f5b6fb28b32a0ad9d5abd Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 25 Apr 2023 15:15:57 +0200 Subject: [PATCH 0048/1350] Update rtextures.c --- src/rtextures.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtextures.c b/src/rtextures.c index 3c535d9db..d83d11e5a 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3000,7 +3000,7 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color) if (rec.x < 0) { rec.width -= rec.x; rec.x = 0; } if (rec.y < 0) { rec.height -= rec.y; rec.y = 0; } if (rec.width < 0) rec.width = 0; - if (rec.heigh < 0) rec.height = 0; + if (rec.height < 0) rec.height = 0; int sy = (int)rec.y; int ey = sy + (int)rec.height; From e2996f167ede62b38cc280a46a8dceb6b5bcd01e Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 25 Apr 2023 20:01:26 +0200 Subject: [PATCH 0049/1350] Update rtextures.c --- src/rtextures.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rtextures.c b/src/rtextures.c index d83d11e5a..5e39260e2 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -525,6 +525,8 @@ bool ExportImage(Image image, const char *fileName) { int success = 0; + if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return success; + #if defined(SUPPORT_IMAGE_EXPORT) int channels = 4; bool allocatedData = false; From 98cb7a19a1a444726eb31af34d4c25cd06c347cb Mon Sep 17 00:00:00 2001 From: kolunmi <113054217+kolunmi@users.noreply.github.com> Date: Thu, 27 Apr 2023 13:17:57 -0700 Subject: [PATCH 0050/1350] ensure distance is greater than 0 in CameraMoveToTarget (#3031) --- src/rcamera.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcamera.h b/src/rcamera.h index 8b37ff7ba..852339ab5 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -294,7 +294,7 @@ void CameraMoveToTarget(Camera *camera, float delta) distance += delta; // Distance must be greater than 0 - if (distance < 0) distance = 0.001f; + if (distance <= 0) distance = 0.001f; // Set new distance by moving the position along the forward vector Vector3 forward = GetCameraForward(camera); From 662dfad670eb634c1aa4cab2bfa31880cd995df3 Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Sat, 29 Apr 2023 15:03:19 +0000 Subject: [PATCH 0051/1350] =?UTF-8?q?Correction=20of=20values=20=E2=80=8B?= =?UTF-8?q?=E2=80=8Bused=20only=20once=20in=20GenMeshCubicmap=20(#3032)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Correction of values ​​used only once in GenMeshCubicmap The mapWidth and mapHeight values ​​were only used as a limit in the for loop when they could be used throughout the function. * mapWidth and mapHeight removed from GenMeshCubicmap mapWidth and mapHeight have been removed from GenMeshCubicmap in favor of using cubicmap.width and cubicmap.height --- src/rmodels.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 6b2b4b9f0..9c4c3388d 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2940,11 +2940,8 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) Color *pixels = LoadImageColors(cubicmap); - int mapWidth = cubicmap.width; - int mapHeight = cubicmap.height; - // NOTE: Max possible number of triangles numCubes*(12 triangles by cube) - int maxTriangles = cubicmap.width*cubicmap.height*12; + int maxTriangles = cubicmap.width * cubicmap.height * 12; int vCounter = 0; // Used to count vertices int tcCounter = 0; // Used to count texcoords @@ -2981,9 +2978,9 @@ Mesh GenMeshCubicmap(Image cubicmap, Vector3 cubeSize) RectangleF topTexUV = { 0.0f, 0.5f, 0.5f, 0.5f }; RectangleF bottomTexUV = { 0.5f, 0.5f, 0.5f, 0.5f }; - for (int z = 0; z < mapHeight; ++z) + for (int z = 0; z < cubicmap.height; ++z) { - for (int x = 0; x < mapWidth; ++x) + for (int x = 0; x < cubicmap.width; ++x) { // Define the 8 vertex of the cube, we will combine them accordingly later... Vector3 v1 = { w*(x - 0.5f), h2, h*(z - 0.5f) }; From 66f0de280705f10959a4ef0c82f3b34d7350ea15 Mon Sep 17 00:00:00 2001 From: Thiago P <33945251+Stopfield@users.noreply.github.com> Date: Sat, 29 Apr 2023 15:34:14 -0300 Subject: [PATCH 0052/1350] WingBGI now leads to the correct website! (#3033) The old link led to an unregistered site (http://www.codecutter.net/tools/winbgim/). The actual domain has changed to (https://winbgim.codecutter.org/). I checked the wayback machine, it's the same site, take a look: https://web.archive.org/web/20190421035959/http://www.codecutter.net/tools/winbgim/. --- HISTORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 5ae12c05c..735e66514 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -5,7 +5,7 @@ introduction I started developing videogames in 2006 and some years later I started teaching videogames development to young people with artistic profile, most of students had never written a single line of code. -I decided to start with C language basis and, after searching for the most simple and easy-to-use library to teach videogames programming, I found [WinBGI](http://www.codecutter.net/tools/winbgim/); it was great and it worked very well with students, in just a couple of weeks, those students that had never written a single line of code were able to program (and understand) a simple PONG game, some of them even a BREAKOUT! +I decided to start with C language basis and, after searching for the most simple and easy-to-use library to teach videogames programming, I found [WinBGI](https://winbgim.codecutter.org/); it was great and it worked very well with students, in just a couple of weeks, those students that had never written a single line of code were able to program (and understand) a simple PONG game, some of them even a BREAKOUT! But WinBGI was not the clearer and most organized library for my taste. There were lots of things I found confusing and some function names were not clear enough for most of the students; not to mention the lack of transparencies support and no hardware acceleration. From 59596e4266aa19692dcf29e3e0c4b3e7ffd50598 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 29 Apr 2023 20:39:36 +0200 Subject: [PATCH 0053/1350] Update rcore.c --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index bb8852826..500e9edde 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3000,7 +3000,7 @@ bool IsFileExtension(const char *fileName, const char *ext) const char **checkExts = TextSplit(ext, ';', &extCount); // WARNING: Module required: rtext char fileExtLower[MAX_FILE_EXTENSION_SIZE + 1] = { 0 }; - strncpy(fileExtLower, TextToLower(fileExt),MAX_FILE_EXTENSION_SIZE); // WARNING: Module required: rtext + strncpy(fileExtLower, TextToLower(fileExt), MAX_FILE_EXTENSION_SIZE); // WARNING: Module required: rtext for (int i = 0; i < extCount; i++) { From ed2caa12775da95d3e19ce42dccdca4a0ba8f8a0 Mon Sep 17 00:00:00 2001 From: star-tek-mb Date: Mon, 1 May 2023 14:02:34 +0500 Subject: [PATCH 0054/1350] fix for latest zig master (#3037) --- build.zig | 16 ++-------------- examples/build.zig | 14 ++++++-------- src/build.zig | 8 ++++---- 3 files changed, 12 insertions(+), 26 deletions(-) diff --git a/build.zig b/build.zig index 9959d8e0c..2bca6a1d4 100644 --- a/build.zig +++ b/build.zig @@ -2,17 +2,5 @@ const std = @import("std"); const raylib = @import("src/build.zig"); pub fn build(b: *std.Build) void { - // Standard target options allows the person running `zig build` to choose - // what target to build for. Here we do not override the defaults, which - // means any target is allowed, and the default is native. Other options - // for restricting supported target set are available. - const target = b.standardTargetOptions(.{}); - // Standard optimization options allow the person running `zig build` to select - // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not - // set a preferred release mode, allowing the user to decide how to optimize. - const optimize = b.standardOptimizeOption(.{}); - - const lib = raylib.addRaylib(b, target, optimize); - lib.installHeader("src/raylib.h", "raylib.h"); - lib.install(); -} \ No newline at end of file + raylib.build(b); +} diff --git a/examples/build.zig b/examples/build.zig index 65586f12f..f94a3b4bf 100644 --- a/examples/build.zig +++ b/examples/build.zig @@ -26,10 +26,10 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT exe.addCSourceFile(path, &[_][]const u8{}); exe.linkLibC(); exe.addObjectFile(switch (target.getOsTag()) { - .windows => "../src/raylib.lib", - .linux => "../src/libraylib.a", - .macos => "../src/libraylib.a", - .emscripten => "../src/libraylib.a", + .windows => "../src/zig-out/lib/raylib.lib", + .linux => "../src/zig-out/lib/libraylib.a", + .macos => "../src/zig-out/lib/libraylib.a", + .emscripten => "../src/zig-out/lib/libraylib.a", else => @panic("Unsupported OS"), }); @@ -70,10 +70,8 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT }, } - exe.setOutputDir(module); - - var run = exe.run(); - run.step.dependOn(&b.addInstallArtifact(exe).step); + b.installArtifact(exe); + var run = b.addRunArtifact(exe); run.cwd = module; b.step(name, name).dependOn(&run.step); all.dependOn(&exe.step); diff --git a/src/build.zig b/src/build.zig index 84684fef8..b39f23ed8 100644 --- a/src/build.zig +++ b/src/build.zig @@ -90,7 +90,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built const cache_include = std.fs.path.join(b.allocator, &.{ b.sysroot.?, "cache", "sysroot", "include" }) catch @panic("Out of memory"); defer b.allocator.free(cache_include); - var dir = std.fs.openDirAbsolute(cache_include, std.fs.Dir.OpenDirOptions{.access_sub_paths = true, .no_follow = true}) catch @panic("No emscripten cache. Generate it!"); + var dir = std.fs.openDirAbsolute(cache_include, std.fs.Dir.OpenDirOptions{ .access_sub_paths = true, .no_follow = true }) catch @panic("No emscripten cache. Generate it!"); dir.close(); raylib.addIncludePath(cache_include); @@ -115,11 +115,11 @@ pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); const lib = addRaylib(b, target, optimize); - lib.setOutputDir(srcdir); - lib.install(); + lib.installHeader("src/raylib.h", "raylib.h"); + b.installArtifact(lib); } -const srcdir = struct{ +const srcdir = struct { fn getSrcDir() []const u8 { return std.fs.path.dirname(@src().file).?; } From a4a5a798bdb546646b607b3348a8f2d43b161a09 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 May 2023 14:03:32 +0200 Subject: [PATCH 0055/1350] Update rlgl_compute_shader.c --- examples/others/rlgl_compute_shader.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/others/rlgl_compute_shader.c b/examples/others/rlgl_compute_shader.c index e19a6c2f5..544e0a534 100644 --- a/examples/others/rlgl_compute_shader.c +++ b/examples/others/rlgl_compute_shader.c @@ -74,10 +74,10 @@ int main(void) unsigned int ssboA = rlLoadShaderBuffer(GOL_WIDTH*GOL_WIDTH*sizeof(unsigned int), NULL, RL_DYNAMIC_COPY); unsigned int ssboB = rlLoadShaderBuffer(GOL_WIDTH*GOL_WIDTH*sizeof(unsigned int), NULL, RL_DYNAMIC_COPY); unsigned int ssboTransfert = rlLoadShaderBuffer(sizeof(GolUpdateSSBO), NULL, RL_DYNAMIC_COPY); - + GolUpdateSSBO transfertBuffer = { 0 }; - // Create a white texture of the size of the window to update + // Create a white texture of the size of the window to update // each pixel of the window using the fragment shader: golRenderShader Image whiteImage = GenImageColor(GOL_WIDTH, GOL_WIDTH, WHITE); Texture whiteTex = LoadTextureFromImage(whiteImage); @@ -105,7 +105,7 @@ int main(void) { // Send SSBO buffer to GPU rlUpdateShaderBuffer(ssboTransfert, &transfertBuffer, sizeof(GolUpdateSSBO), 0); - + // Process SSBO commands on GPU rlEnableShader(golTransfertProgram); rlBindShaderBuffer(ssboA, 1); @@ -143,7 +143,7 @@ int main(void) BeginShaderMode(golRenderShader); DrawTexture(whiteTex, 0, 0, WHITE); EndShaderMode(); - + DrawRectangleLines(GetMouseX() - brushSize/2, GetMouseY() - brushSize/2, brushSize, brushSize, RED); DrawText("Use Mouse wheel to increase/decrease brush size", 10, 10, 20, WHITE); From 7d68aa686974347cefe0ef481c835e3d60bdc4b9 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 1 May 2023 14:04:22 +0200 Subject: [PATCH 0056/1350] REVIEWED: Modules description layout --- src/raudio.c | 31 +++++++------ src/raymath.h | 24 +++++----- src/rcamera.h | 15 +++---- src/rcore.c | 104 +++++++++++++++++++++--------------------- src/rgestures.h | 17 +++---- src/rlgl.h | 117 ++++++++++++++++++++++++------------------------ src/rmodels.c | 25 +++++------ src/rshapes.c | 29 ++++++------ src/rtext.c | 28 ++++++------ src/rtextures.c | 53 +++++++++++----------- src/utils.c | 7 ++- 11 files changed, 218 insertions(+), 232 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 8dfead37f..5e2ccf996 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -11,23 +11,22 @@ * - Play/Stop/Pause/Resume loaded audio * * CONFIGURATION: +* #define SUPPORT_MODULE_RAUDIO +* raudio module is included in the build * -* #define SUPPORT_MODULE_RAUDIO -* raudio module is included in the build +* #define RAUDIO_STANDALONE +* Define to use the module as standalone library (independently of raylib). +* Required types and functions are defined in the same module. * -* #define RAUDIO_STANDALONE -* Define to use the module as standalone library (independently of raylib). -* Required types and functions are defined in the same module. -* -* #define SUPPORT_FILEFORMAT_WAV -* #define SUPPORT_FILEFORMAT_OGG -* #define SUPPORT_FILEFORMAT_MP3 -* #define SUPPORT_FILEFORMAT_QOA -* #define SUPPORT_FILEFORMAT_FLAC -* #define SUPPORT_FILEFORMAT_XM -* #define SUPPORT_FILEFORMAT_MOD -* Selected desired fileformats to be supported for loading. Some of those formats are -* supported by default, to remove support, just comment unrequired #define in this module +* #define SUPPORT_FILEFORMAT_WAV +* #define SUPPORT_FILEFORMAT_OGG +* #define SUPPORT_FILEFORMAT_MP3 +* #define SUPPORT_FILEFORMAT_QOA +* #define SUPPORT_FILEFORMAT_FLAC +* #define SUPPORT_FILEFORMAT_XM +* #define SUPPORT_FILEFORMAT_MOD +* Selected desired fileformats to be supported for loading. Some of those formats are +* supported by default, to remove support, just comment unrequired #define in this module * * DEPENDENCIES: * miniaudio.h - Audio device management lib (https://github.com/mackron/miniaudio) @@ -42,7 +41,7 @@ * David Reid (github: @mackron) (Nov. 2017): * - Complete port to miniaudio library * -* Joshua Reisenauer (github: @kd7tck) (2015) +* Joshua Reisenauer (github: @kd7tck) (2015): * - XM audio module support (jar_xm) * - MOD audio module support (jar_mod) * - Mixing channels support diff --git a/src/raymath.h b/src/raymath.h index 54eaa815d..ddc3f58a1 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -2,19 +2,7 @@ * * raymath v1.5 - Math functions to work with Vector2, Vector3, Matrix and Quaternions * -* CONFIGURATION: -* -* #define RAYMATH_IMPLEMENTATION -* Generates the implementation of the library into the included file. -* If not defined, the library is in header only mode and can be included in other headers -* or source files without problems. But only ONE file should hold the implementation. -* -* #define RAYMATH_STATIC_INLINE -* Define static inline functions code, so #include header suffices for use. -* This may use up lots of memory. -* * CONVENTIONS: -* * - Functions are always self-contained, no function use another raymath function inside, * required code is directly re-implemented inside * - Functions input parameters are always received by value (2 unavoidable exceptions) @@ -22,6 +10,16 @@ * - Functions are always defined inline * - Angles are always in radians (DEG2RAD/RAD2DEG macros provided for convenience) * +* CONFIGURATION: +* #define RAYMATH_IMPLEMENTATION +* Generates the implementation of the library into the included file. +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation. +* +* #define RAYMATH_STATIC_INLINE +* Define static inline functions code, so #include header suffices for use. +* This may use up lots of memory. +* * * LICENSE: zlib/libpng * @@ -703,7 +701,7 @@ RMAPI Vector3 Vector3Normalize(Vector3 v) Vector3 result = v; float length = sqrtf(v.x*v.x + v.y*v.y + v.z*v.z); - if (length != 0.0f) + if (length != 0.0f) { float ilength = 1.0f/length; diff --git a/src/rcamera.h b/src/rcamera.h index 852339ab5..0b9b8076a 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -3,15 +3,14 @@ * rcamera - Basic camera system with support for multiple camera modes * * CONFIGURATION: +* #define CAMERA_IMPLEMENTATION +* Generates the implementation of the library into the included file. +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation. * -* #define CAMERA_IMPLEMENTATION -* Generates the implementation of the library into the included file. -* If not defined, the library is in header only mode and can be included in other headers -* or source files without problems. But only ONE file should hold the implementation. -* -* #define CAMERA_STANDALONE -* If defined, the library can be used as standalone as a camera system but some -* functions must be redefined to manage inputs accordingly. +* #define CAMERA_STANDALONE +* If defined, the library can be used as standalone as a camera system but some +* functions must be redefined to manage inputs accordingly. * * CONTRIBUTORS: * Ramon Santamaria: Supervision, review, update and maintenance diff --git a/src/rcore.c b/src/rcore.c index 500e9edde..476ea8e2f 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -13,75 +13,75 @@ * - PLATFORM_WEB: HTML5 with WebAssembly * * CONFIGURATION: +* #define PLATFORM_DESKTOP +* Windowing and input system configured for desktop platforms: +* Windows, Linux, OSX, FreeBSD, OpenBSD, NetBSD, DragonFly * -* #define PLATFORM_DESKTOP -* Windowing and input system configured for desktop platforms: Windows, Linux, OSX, FreeBSD, OpenBSD, NetBSD, DragonFly -* NOTE: Oculus Rift CV1 requires PLATFORM_DESKTOP for mirror rendering - View [rlgl] module to enable it +* #define PLATFORM_ANDROID +* Windowing and input system configured for Android device, app activity managed internally in this module. +* NOTE: OpenGL ES 2.0 is required and graphic device is managed by EGL * -* #define PLATFORM_ANDROID -* Windowing and input system configured for Android device, app activity managed internally in this module. -* NOTE: OpenGL ES 2.0 is required and graphic device is managed by EGL +* #define PLATFORM_RPI (deprecated - RPI OS Buster only) +* Windowing and input system configured for Raspberry Pi in native mode (no XWindow required), +* graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/ +* WARNING: This platform is deprecated, since RPI OS Bullseye, the old Dispmanx libraries are not +* supported and you must be using PLATFORM_DRM * -* #define PLATFORM_RPI (deprecated - RPI OS Buster only) -* Windowing and input system configured for Raspberry Pi in native mode (no XWindow required), -* graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/ -* WARNING: This platform is deprecated, since RPI OS Bullseye, the old Dispmanx libraries are not -* supported and you must be using PLATFORM_DRM +* #define PLATFORM_DRM +* Windowing and input system configured for DRM native mode (RPI4 and other devices) +* graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/ * -* #define PLATFORM_DRM -* Windowing and input system configured for DRM native mode (RPI4 and other devices) -* graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/ +* #define PLATFORM_WEB +* Windowing and input system configured for HTML5 (run on browser), code converted from C to asm.js +* using emscripten compiler. OpenGL ES 2.0 required for direct translation to WebGL equivalent code. * -* #define PLATFORM_WEB -* Windowing and input system configured for HTML5 (run on browser), code converted from C to asm.js -* using emscripten compiler. OpenGL ES 2.0 required for direct translation to WebGL equivalent code. +* #define SUPPORT_DEFAULT_FONT (default) +* Default font is loaded on window initialization to be available for the user to render simple text. +* NOTE: If enabled, uses external module functions to load default raylib font (module: text) * -* #define SUPPORT_DEFAULT_FONT (default) -* Default font is loaded on window initialization to be available for the user to render simple text. -* NOTE: If enabled, uses external module functions to load default raylib font (module: text) +* #define SUPPORT_CAMERA_SYSTEM +* Camera module is included (rcamera.h) and multiple predefined cameras are available: +* free, 1st/3rd person, orbital, custom * -* #define SUPPORT_CAMERA_SYSTEM -* Camera module is included (rcamera.h) and multiple predefined cameras are available: free, 1st/3rd person, orbital +* #define SUPPORT_GESTURES_SYSTEM +* Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag * -* #define SUPPORT_GESTURES_SYSTEM -* Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag +* #define SUPPORT_MOUSE_GESTURES +* Mouse gestures are directly mapped like touches and processed by gestures system. * -* #define SUPPORT_MOUSE_GESTURES -* Mouse gestures are directly mapped like touches and processed by gestures system. +* #define SUPPORT_TOUCH_AS_MOUSE +* Touch input and mouse input are shared. Mouse functions also return touch information. * -* #define SUPPORT_TOUCH_AS_MOUSE -* Touch input and mouse input are shared. Mouse functions also return touch information. +* #define SUPPORT_SSH_KEYBOARD_RPI (Raspberry Pi only) +* Reconfigure standard input to receive key inputs, works with SSH connection. +* WARNING: Reconfiguring standard input could lead to undesired effects, like breaking other +* running processes orblocking the device if not restored properly. Use with care. * -* #define SUPPORT_SSH_KEYBOARD_RPI (Raspberry Pi only) -* Reconfigure standard input to receive key inputs, works with SSH connection. -* WARNING: Reconfiguring standard input could lead to undesired effects, like breaking other running processes or -* blocking the device if not restored properly. Use with care. +* #define SUPPORT_BUSY_WAIT_LOOP +* Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used * -* #define SUPPORT_BUSY_WAIT_LOOP -* Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used +* #define SUPPORT_PARTIALBUSY_WAIT_LOOP +* Use a partial-busy wait loop, in this case frame sleeps for most of the time and runs a busy-wait-loop at the end * -* #define SUPPORT_PARTIALBUSY_WAIT_LOOP -* Use a partial-busy wait loop, in this case frame sleeps for most of the time and runs a busy-wait-loop at the end +* #define SUPPORT_EVENTS_WAITING +* Wait for events passively (sleeping while no events) instead of polling them actively every frame * -* #define SUPPORT_EVENTS_WAITING -* Wait for events passively (sleeping while no events) instead of polling them actively every frame +* #define SUPPORT_SCREEN_CAPTURE +* Allow automatic screen capture of current screen pressing F12, defined in KeyCallback() * -* #define SUPPORT_SCREEN_CAPTURE -* Allow automatic screen capture of current screen pressing F12, defined in KeyCallback() +* #define SUPPORT_GIF_RECORDING +* Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback() * -* #define SUPPORT_GIF_RECORDING -* Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback() +* #define SUPPORT_COMPRESSION_API +* Support CompressData() and DecompressData() functions, those functions use zlib implementation +* provided by stb_image and stb_image_write libraries, so, those libraries must be enabled on textures module +* for linkage * -* #define SUPPORT_COMPRESSION_API -* Support CompressData() and DecompressData() functions, those functions use zlib implementation -* provided by stb_image and stb_image_write libraries, so, those libraries must be enabled on textures module -* for linkage -* -* #define SUPPORT_EVENTS_AUTOMATION -* Support automatic generated events, loading and recording of those events when required +* #define SUPPORT_EVENTS_AUTOMATION +* Support automatic generated events, loading and recording of those events when required * * DEPENDENCIES: -* rglfw - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX. FreeBSD, OpenBSD, NetBSD, DragonFly) +* rglfw - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX, FreeBSD...) * raymath - 3D math functionality (Vector2, Vector3, Matrix, Quaternion) * camera - Multiple 3D camera modes (free, orbital, 1st person, 3rd person) * gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) @@ -6721,7 +6721,7 @@ static void *EventThread(void *arg) if (CORE.Input.Mouse.currentPosition.y < 0) CORE.Input.Mouse.currentPosition.y = 0; if (CORE.Input.Mouse.currentPosition.y > CORE.Window.screen.height/CORE.Input.Mouse.scale.y) CORE.Input.Mouse.currentPosition.y = CORE.Window.screen.height/CORE.Input.Mouse.scale.y; } - + // Update touch point count CORE.Input.Touch.pointCount = 0; if (CORE.Input.Touch.position[0].x >= 0) CORE.Input.Touch.pointCount++; @@ -6736,7 +6736,7 @@ static void *EventThread(void *arg) gestureEvent.touchAction = touchAction; gestureEvent.pointCount = CORE.Input.Touch.pointCount; - + gestureEvent.pointId[0] = 0; gestureEvent.pointId[1] = 1; gestureEvent.pointId[2] = 2; diff --git a/src/rgestures.h b/src/rgestures.h index a7440fb98..0be2fbb5c 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -2,18 +2,15 @@ * * rgestures - Gestures system, gestures processing based on input events (touch/mouse) * -* NOTE: Memory footprint of this library is aproximately 128 bytes (global variables) -* * CONFIGURATION: +* #define GESTURES_IMPLEMENTATION +* Generates the implementation of the library into the included file. +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation. * -* #define GESTURES_IMPLEMENTATION -* Generates the implementation of the library into the included file. -* If not defined, the library is in header only mode and can be included in other headers -* or source files without problems. But only ONE file should hold the implementation. -* -* #define GESTURES_STANDALONE -* If defined, the library can be used as standalone to process gesture events with -* no external dependencies. +* #define GESTURES_STANDALONE +* If defined, the library can be used as standalone to process gesture events with +* no external dependencies. * * CONTRIBUTORS: * Marc Palau: Initial implementation (2014) diff --git a/src/rlgl.h b/src/rlgl.h index 38d4ebb0e..85a8ec368 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2,82 +2,81 @@ * * rlgl v4.5 - A multi-OpenGL abstraction layer with an immediate-mode style API * -* An abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0) -* that provides a pseudo-OpenGL 1.1 immediate-mode style API (rlVertex, rlTranslate, rlRotate...) +* DESCRIPTION: +* An abstraction layer for multiple OpenGL versions (1.1, 2.1, 3.3 Core, 4.3 Core, ES 2.0) +* that provides a pseudo-OpenGL 1.1 immediate-mode style API (rlVertex, rlTranslate, rlRotate...) * -* When choosing an OpenGL backend different than OpenGL 1.1, some internal buffer are -* initialized on rlglInit() to accumulate vertex data. +* ADDITIONAL NOTES: +* When choosing an OpenGL backend different than OpenGL 1.1, some internal buffer are +* initialized on rlglInit() to accumulate vertex data. * -* When an internal state change is required all the stored vertex data is renderer in batch, -* additionally, rlDrawRenderBatchActive() could be called to force flushing of the batch. +* When an internal state change is required all the stored vertex data is renderer in batch, +* additionally, rlDrawRenderBatchActive() could be called to force flushing of the batch. * -* Some additional resources are also loaded for convenience, here the complete list: -* - Default batch (RLGL.defaultBatch): RenderBatch system to accumulate vertex data -* - Default texture (RLGL.defaultTextureId): 1x1 white pixel R8G8B8A8 -* - Default shader (RLGL.State.defaultShaderId, RLGL.State.defaultShaderLocs) -* -* Internal buffer (and additional resources) must be manually unloaded calling rlglClose(). +* Some resources are also loaded for convenience, here the complete list: +* - Default batch (RLGL.defaultBatch): RenderBatch system to accumulate vertex data +* - Default texture (RLGL.defaultTextureId): 1x1 white pixel R8G8B8A8 +* - Default shader (RLGL.State.defaultShaderId, RLGL.State.defaultShaderLocs) * +* Internal buffer (and resources) must be manually unloaded calling rlglClose(). * * CONFIGURATION: +* #define GRAPHICS_API_OPENGL_11 +* #define GRAPHICS_API_OPENGL_21 +* #define GRAPHICS_API_OPENGL_33 +* #define GRAPHICS_API_OPENGL_43 +* #define GRAPHICS_API_OPENGL_ES2 +* Use selected OpenGL graphics backend, should be supported by platform +* Those preprocessor defines are only used on rlgl module, if OpenGL version is +* required by any other module, use rlGetVersion() to check it * -* #define GRAPHICS_API_OPENGL_11 -* #define GRAPHICS_API_OPENGL_21 -* #define GRAPHICS_API_OPENGL_33 -* #define GRAPHICS_API_OPENGL_43 -* #define GRAPHICS_API_OPENGL_ES2 -* Use selected OpenGL graphics backend, should be supported by platform -* Those preprocessor defines are only used on rlgl module, if OpenGL version is -* required by any other module, use rlGetVersion() to check it +* #define RLGL_IMPLEMENTATION +* Generates the implementation of the library into the included file. +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation. * -* #define RLGL_IMPLEMENTATION -* Generates the implementation of the library into the included file. -* If not defined, the library is in header only mode and can be included in other headers -* or source files without problems. But only ONE file should hold the implementation. +* #define RLGL_RENDER_TEXTURES_HINT +* Enable framebuffer objects (fbo) support (enabled by default) +* Some GPUs could not support them despite the OpenGL version * -* #define RLGL_RENDER_TEXTURES_HINT -* Enable framebuffer objects (fbo) support (enabled by default) -* Some GPUs could not support them despite the OpenGL version +* #define RLGL_SHOW_GL_DETAILS_INFO +* Show OpenGL extensions and capabilities detailed logs on init * -* #define RLGL_SHOW_GL_DETAILS_INFO -* Show OpenGL extensions and capabilities detailed logs on init +* #define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT +* Enable debug context (only available on OpenGL 4.3) * -* #define RLGL_ENABLE_OPENGL_DEBUG_CONTEXT -* Enable debug context (only available on OpenGL 4.3) +* rlgl capabilities could be customized just defining some internal +* values before library inclusion (default values listed): * -* rlgl capabilities could be customized just defining some internal -* values before library inclusion (default values listed): +* #define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 8192 // Default internal render batch elements limits +* #define RL_DEFAULT_BATCH_BUFFERS 1 // Default number of batch buffers (multi-buffering) +* #define RL_DEFAULT_BATCH_DRAWCALLS 256 // Default number of batch draw calls (by state changes: mode, texture) +* #define RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS 4 // Maximum number of textures units that can be activated on batch drawing (SetShaderValueTexture()) * -* #define RL_DEFAULT_BATCH_BUFFER_ELEMENTS 8192 // Default internal render batch elements limits -* #define RL_DEFAULT_BATCH_BUFFERS 1 // Default number of batch buffers (multi-buffering) -* #define RL_DEFAULT_BATCH_DRAWCALLS 256 // Default number of batch draw calls (by state changes: mode, texture) -* #define RL_DEFAULT_BATCH_MAX_TEXTURE_UNITS 4 // Maximum number of textures units that can be activated on batch drawing (SetShaderValueTexture()) +* #define RL_MAX_MATRIX_STACK_SIZE 32 // Maximum size of internal Matrix stack +* #define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported +* #define RL_CULL_DISTANCE_NEAR 0.01 // Default projection matrix near cull distance +* #define RL_CULL_DISTANCE_FAR 1000.0 // Default projection matrix far cull distance * -* #define RL_MAX_MATRIX_STACK_SIZE 32 // Maximum size of internal Matrix stack -* #define RL_MAX_SHADER_LOCATIONS 32 // Maximum number of shader locations supported -* #define RL_CULL_DISTANCE_NEAR 0.01 // Default projection matrix near cull distance -* #define RL_CULL_DISTANCE_FAR 1000.0 // Default projection matrix far cull distance +* When loading a shader, the following vertex attribute and uniform +* location names are tried to be set automatically: * -* When loading a shader, the following vertex attribute and uniform -* location names are tried to be set automatically: -* -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: 0 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: 1 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: 2 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: 3 -* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: 4 -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView)) -* #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color) -* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0) -* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1) -* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2) +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_POSITION "vertexPosition" // Bound by default to shader location: 0 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TEXCOORD "vertexTexCoord" // Bound by default to shader location: 1 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_NORMAL "vertexNormal" // Bound by default to shader location: 2 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_COLOR "vertexColor" // Bound by default to shader location: 3 +* #define RL_DEFAULT_SHADER_ATTRIB_NAME_TANGENT "vertexTangent" // Bound by default to shader location: 4 +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MVP "mvp" // model-view-projection matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_VIEW "matView" // view matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_PROJECTION "matProjection" // projection matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_MODEL "matModel" // model matrix +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_NORMAL "matNormal" // normal matrix (transpose(inverse(matModelView)) +* #define RL_DEFAULT_SHADER_UNIFORM_NAME_COLOR "colDiffuse" // color diffuse (base tint color, multiplied by texture color) +* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE0 "texture0" // texture0 (texture slot active 0) +* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE1 "texture1" // texture1 (texture slot active 1) +* #define RL_DEFAULT_SHADER_SAMPLER2D_NAME_TEXTURE2 "texture2" // texture2 (texture slot active 2) * * DEPENDENCIES: -* * - OpenGL libraries (depending on platform and OpenGL version selected) * - GLAD OpenGL extensions loading library (only for OpenGL 3.3 Core, 4.3 Core) * diff --git a/src/rmodels.c b/src/rmodels.c index 9c4c3388d..34352ec1b 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3,21 +3,20 @@ * rmodels - Basic functions to draw 3d shapes and load and draw 3d models * * CONFIGURATION: +* #define SUPPORT_MODULE_RMODELS +* rmodels module is included in the build * -* #define SUPPORT_MODULE_RMODELS -* rmodels module is included in the build +* #define SUPPORT_FILEFORMAT_OBJ +* #define SUPPORT_FILEFORMAT_MTL +* #define SUPPORT_FILEFORMAT_IQM +* #define SUPPORT_FILEFORMAT_GLTF +* #define SUPPORT_FILEFORMAT_VOX +* #define SUPPORT_FILEFORMAT_M3D +* Selected desired fileformats to be supported for model data loading. * -* #define SUPPORT_FILEFORMAT_OBJ -* #define SUPPORT_FILEFORMAT_MTL -* #define SUPPORT_FILEFORMAT_IQM -* #define SUPPORT_FILEFORMAT_GLTF -* #define SUPPORT_FILEFORMAT_VOX -* #define SUPPORT_FILEFORMAT_M3D -* Selected desired fileformats to be supported for model data loading. -* -* #define SUPPORT_MESH_GENERATION -* Support procedural mesh generation functions, uses external par_shapes.h library -* NOTE: Some generated meshes DO NOT include generated texture coordinates +* #define SUPPORT_MESH_GENERATION +* Support procedural mesh generation functions, uses external par_shapes.h library +* NOTE: Some generated meshes DO NOT include generated texture coordinates * * * LICENSE: zlib/libpng diff --git a/src/rshapes.c b/src/rshapes.c index 5d7eae0ad..45cf6ac67 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -2,26 +2,25 @@ * * rshapes - Basic functions to draw 2d shapes and check collisions * -* NOTES: -* Shapes can be draw using 3 types of primitives: LINES, TRIANGLES and QUADS. -* Some functions implement two drawing options: TRIANGLES and QUADS, by default TRIANGLES -* are used but QUADS implementation can be selected with SUPPORT_QUADS_DRAW_MODE define +* ADDITIONAL NOTES: +* Shapes can be draw using 3 types of primitives: LINES, TRIANGLES and QUADS. +* Some functions implement two drawing options: TRIANGLES and QUADS, by default TRIANGLES +* are used but QUADS implementation can be selected with SUPPORT_QUADS_DRAW_MODE define * -* Some functions define texture coordinates (rlTexCoord2f()) for the shapes and use a -* user-provided texture with SetShapesTexture(), the pourpouse of this implementation -* is allowing to reduce draw calls when combined with a texture-atlas. +* Some functions define texture coordinates (rlTexCoord2f()) for the shapes and use a +* user-provided texture with SetShapesTexture(), the pourpouse of this implementation +* is allowing to reduce draw calls when combined with a texture-atlas. * -* By default, raylib sets the default texture and rectangle at InitWindow()[rcore] to one -* white character of default font [rtext], this way, raylib text and shapes can be draw with -* a single draw call and it also allows users to configure it the same way with their own fonts. +* By default, raylib sets the default texture and rectangle at InitWindow()[rcore] to one +* white character of default font [rtext], this way, raylib text and shapes can be draw with +* a single draw call and it also allows users to configure it the same way with their own fonts. * * CONFIGURATION: +* #define SUPPORT_MODULE_RSHAPES +* rshapes module is included in the build * -* #define SUPPORT_MODULE_RSHAPES -* rshapes module is included in the build -* -* #define SUPPORT_QUADS_DRAW_MODE -* Use QUADS instead of TRIANGLES for drawing when possible. Lines-based shapes still use LINES +* #define SUPPORT_QUADS_DRAW_MODE +* Use QUADS instead of TRIANGLES for drawing when possible. Lines-based shapes still use LINES * * * LICENSE: zlib/libpng diff --git a/src/rtext.c b/src/rtext.c index 2df8be8dc..1f9ce1b65 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -3,25 +3,23 @@ * rtext - Basic functions to load fonts and draw text * * CONFIGURATION: +* #define SUPPORT_MODULE_RTEXT +* rtext module is included in the build * -* #define SUPPORT_MODULE_RTEXT -* rtext module is included in the build +* #define SUPPORT_FILEFORMAT_FNT +* #define SUPPORT_FILEFORMAT_TTF +* Selected desired fileformats to be supported for loading. Some of those formats are +* supported by default, to remove support, just comment unrequired #define in this module * -* #define SUPPORT_FILEFORMAT_FNT -* #define SUPPORT_FILEFORMAT_TTF -* Selected desired fileformats to be supported for loading. Some of those formats are -* supported by default, to remove support, just comment unrequired #define in this module +* #define SUPPORT_DEFAULT_FONT +* Load default raylib font on initialization to be used by DrawText() and MeasureText(). +* If no default font loaded, DrawTextEx() and MeasureTextEx() are required. * -* #define SUPPORT_DEFAULT_FONT -* Load default raylib font on initialization to be used by DrawText() and MeasureText(). -* If no default font loaded, DrawTextEx() and MeasureTextEx() are required. -* -* #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH -* TextSplit() function static buffer max size -* -* #define MAX_TEXTSPLIT_COUNT -* TextSplit() function static substrings pointers array (pointing to static buffer) +* #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH +* TextSplit() function static buffer max size * +* #define MAX_TEXTSPLIT_COUNT +* TextSplit() function static substrings pointers array (pointing to static buffer) * * DEPENDENCIES: * stb_truetype - Load TTF file and rasterize characters data diff --git a/src/rtextures.c b/src/rtextures.c index 5e39260e2..5c8029e5a 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3,37 +3,36 @@ * rtextures - Basic functions to load and draw textures * * CONFIGURATION: +* #define SUPPORT_MODULE_RTEXTURES +* rtextures module is included in the build * -* #define SUPPORT_MODULE_RTEXTURES -* rtextures module is included in the build +* #define SUPPORT_FILEFORMAT_BMP +* #define SUPPORT_FILEFORMAT_PNG +* #define SUPPORT_FILEFORMAT_TGA +* #define SUPPORT_FILEFORMAT_JPG +* #define SUPPORT_FILEFORMAT_GIF +* #define SUPPORT_FILEFORMAT_QOI +* #define SUPPORT_FILEFORMAT_PSD +* #define SUPPORT_FILEFORMAT_HDR +* #define SUPPORT_FILEFORMAT_PIC +* #define SUPPORT_FILEFORMAT_PNM +* #define SUPPORT_FILEFORMAT_DDS +* #define SUPPORT_FILEFORMAT_PKM +* #define SUPPORT_FILEFORMAT_KTX +* #define SUPPORT_FILEFORMAT_PVR +* #define SUPPORT_FILEFORMAT_ASTC +* Select desired fileformats to be supported for image data loading. Some of those formats are +* supported by default, to remove support, just comment unrequired #define in this module * -* #define SUPPORT_FILEFORMAT_BMP -* #define SUPPORT_FILEFORMAT_PNG -* #define SUPPORT_FILEFORMAT_TGA -* #define SUPPORT_FILEFORMAT_JPG -* #define SUPPORT_FILEFORMAT_GIF -* #define SUPPORT_FILEFORMAT_QOI -* #define SUPPORT_FILEFORMAT_PSD -* #define SUPPORT_FILEFORMAT_HDR -* #define SUPPORT_FILEFORMAT_PIC -* #define SUPPORT_FILEFORMAT_PNM -* #define SUPPORT_FILEFORMAT_DDS -* #define SUPPORT_FILEFORMAT_PKM -* #define SUPPORT_FILEFORMAT_KTX -* #define SUPPORT_FILEFORMAT_PVR -* #define SUPPORT_FILEFORMAT_ASTC -* Select desired fileformats to be supported for image data loading. Some of those formats are -* supported by default, to remove support, just comment unrequired #define in this module +* #define SUPPORT_IMAGE_EXPORT +* Support image export in multiple file formats * -* #define SUPPORT_IMAGE_EXPORT -* Support image export in multiple file formats +* #define SUPPORT_IMAGE_MANIPULATION +* Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop... +* If not defined only some image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageResize*() * -* #define SUPPORT_IMAGE_MANIPULATION -* Support multiple image editing functions to scale, adjust colors, flip, draw on images, crop... -* If not defined only some image editing functions supported: ImageFormat(), ImageAlphaMask(), ImageResize*() -* -* #define SUPPORT_IMAGE_GENERATION -* Support procedural image generation functionality (gradient, spot, perlin-noise, cellular) +* #define SUPPORT_IMAGE_GENERATION +* Support procedural image generation functionality (gradient, spot, perlin-noise, cellular) * * DEPENDENCIES: * stb_image - Multiple image formats loading (JPEG, PNG, BMP, TGA, PSD, GIF, PIC) diff --git a/src/utils.c b/src/utils.c index da92ce520..2bdc490b6 100644 --- a/src/utils.c +++ b/src/utils.c @@ -3,10 +3,9 @@ * raylib.utils - Some common utility functions * * CONFIGURATION: -* -* #define SUPPORT_TRACELOG -* Show TraceLog() output messages -* NOTE: By default LOG_DEBUG traces not shown +* #define SUPPORT_TRACELOG +* Show TraceLog() output messages +* NOTE: By default LOG_DEBUG traces not shown * * * LICENSE: zlib/libpng From 3a21301724686335c8dd79e60e051f4fc2c45a41 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 2 May 2023 19:29:14 +0200 Subject: [PATCH 0057/1350] ADDED: Comment about Matrix conventions --- src/raymath.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/raymath.h b/src/raymath.h index ddc3f58a1..fa72400d3 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -3,6 +3,11 @@ * raymath v1.5 - Math functions to work with Vector2, Vector3, Matrix and Quaternions * * CONVENTIONS: +* - Matrix structure is defined as row-major (memory layout) but parameters naming AND all +* math operations performed by the library consider the structure as it was column-major +* It is like transposed versions of the matrices are used for all the maths +* It benefits some functions making them cache-friendly and also avoids matrix +* transpositions sometimes required by OpenGL * - Functions are always self-contained, no function use another raymath function inside, * required code is directly re-implemented inside * - Functions input parameters are always received by value (2 unavoidable exceptions) From fc56940055cbb7b6e5f4f79d84b3d8c5707d31ec Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 2 May 2023 20:40:45 +0200 Subject: [PATCH 0058/1350] REVIEWED: `ExportDataAsCode()` --- src/utils.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/utils.c b/src/utils.c index 2bdc490b6..4175fb693 100644 --- a/src/utils.c +++ b/src/utils.c @@ -298,8 +298,10 @@ bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char * char varFileName[256] = { 0 }; strcpy(varFileName, GetFileNameWithoutExt(fileName)); for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; } + + byteCount += sprintf(txtData + byteCount, "#define %s_DATA_SIZE %i\n\n", varFileName, size); - byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%i] = { ", varFileName, size); + byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%s_DATA_SIZE] = { ", varFileName, varFileName); for (unsigned int i = 0; i < size - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), data[i]); byteCount += sprintf(txtData + byteCount, "0x%x };\n", data[size - 1]); From a48bb6e1ed7b33190e486ba65b7875f0dff73701 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 2 May 2023 20:51:54 +0200 Subject: [PATCH 0059/1350] ADDED: Comment to clarify raymath semantics --- src/raymath.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/raymath.h b/src/raymath.h index fa72400d3..fbb970486 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -8,6 +8,7 @@ * It is like transposed versions of the matrices are used for all the maths * It benefits some functions making them cache-friendly and also avoids matrix * transpositions sometimes required by OpenGL +* Example: In memory order, row0 is [m0 m4 m8 m12] but in semantic math row0 is [m0 m1 m2 m3] * - Functions are always self-contained, no function use another raymath function inside, * required code is directly re-implemented inside * - Functions input parameters are always received by value (2 unavoidable exceptions) From abcbd9817e9163bd10008b42848dfa2d72ebb6c5 Mon Sep 17 00:00:00 2001 From: Jussi Viitala Date: Thu, 4 May 2023 21:21:35 +0300 Subject: [PATCH 0060/1350] Lightmap example. (#3043) --- examples/shaders/resources/LICENSE.md | 2 + examples/shaders/resources/cubicmap_atlas.png | Bin 0 -> 37160 bytes .../resources/shaders/glsl330/lightmap.fs | 20 +++ .../resources/shaders/glsl330/lightmap.vs | 27 +++ examples/shaders/resources/spark_flame.png | Bin 0 -> 7537 bytes examples/shaders/shaders_lightmap.c | 169 ++++++++++++++++++ examples/shaders/shaders_lightmap.png | Bin 0 -> 229671 bytes 7 files changed, 218 insertions(+) create mode 100644 examples/shaders/resources/cubicmap_atlas.png create mode 100644 examples/shaders/resources/shaders/glsl330/lightmap.fs create mode 100644 examples/shaders/resources/shaders/glsl330/lightmap.vs create mode 100644 examples/shaders/resources/spark_flame.png create mode 100644 examples/shaders/shaders_lightmap.c create mode 100644 examples/shaders/shaders_lightmap.png diff --git a/examples/shaders/resources/LICENSE.md b/examples/shaders/resources/LICENSE.md index 96458eca5..7210e34b2 100644 --- a/examples/shaders/resources/LICENSE.md +++ b/examples/shaders/resources/LICENSE.md @@ -9,3 +9,5 @@ | raysan.png | [@raysan5](https://github.com/raysan5) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | - | | space.png | ❔ | ❔ | - | | texel_checker.png | [@raysan5](https://github.com/raysan5) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | Made with [UV Checker Map Maker](http://uvchecker.byvalle.com/) | +| cubicmap.png | [@raysan5](https://github.com/raysan5) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | - | +| spark_flame.png | [@raysan5](https://github.com/raysan5) | [CC0](https://creativecommons.org/publicdomain/zero/1.0/) | Made with [EffectTextureMaker](https://mebiusbox.github.io/contents/EffectTextureMaker/) | \ No newline at end of file diff --git a/examples/shaders/resources/cubicmap_atlas.png b/examples/shaders/resources/cubicmap_atlas.png new file mode 100644 index 0000000000000000000000000000000000000000..9fc404a70b541330b4c1418dae1b790ecb056434 GIT binary patch literal 37160 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 b>;E4B$|lPUU0GMd00000NkvXXu0mjf2K(5M literal 0 HcmV?d00001 diff --git a/examples/shaders/resources/shaders/glsl330/lightmap.fs b/examples/shaders/resources/shaders/glsl330/lightmap.fs new file mode 100644 index 000000000..95558610d --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/lightmap.fs @@ -0,0 +1,20 @@ +#version 330 +// Input vertex attributes (from vertex shader) +in vec2 fragTexCoord; +in vec2 fragTexCoord2; +in vec3 fragPosition; +in vec4 fragColor; + +// Input uniform values +uniform sampler2D texture0; +uniform sampler2D texture1; + +// Output fragment color +out vec4 finalColor; + +void main() { + // Texel color fetching from texture sampler + vec4 texelColor = texture( texture0, fragTexCoord ); + vec4 texelColor2 = texture( texture1, fragTexCoord2 ); + finalColor = texelColor * texelColor2; +} diff --git a/examples/shaders/resources/shaders/glsl330/lightmap.vs b/examples/shaders/resources/shaders/glsl330/lightmap.vs new file mode 100644 index 000000000..00278eaa2 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/lightmap.vs @@ -0,0 +1,27 @@ +#version 330 +// Input vertex attributes +in vec3 vertexPosition; +in vec2 vertexTexCoord; +in vec2 vertexTexCoord2; +in vec4 vertexColor; + +// Input uniform values +uniform mat4 mvp; +uniform mat4 matModel; + +// Output vertex attributes (to fragment shader) +out vec3 fragPosition; +out vec2 fragTexCoord; +out vec2 fragTexCoord2; +out vec4 fragColor; + +void main() { + // Send vertex attributes to fragment shader + fragPosition = vec3( matModel * vec4( vertexPosition, 1.0 ) ); + fragTexCoord = vertexTexCoord; + fragTexCoord2 = vertexTexCoord2; + fragColor = vertexColor; + + // Calculate final vertex position + gl_Position = mvp * vec4( vertexPosition, 1.0 ); +} diff --git a/examples/shaders/resources/spark_flame.png b/examples/shaders/resources/spark_flame.png new file mode 100644 index 0000000000000000000000000000000000000000..72cea2e9b079c3510a7a4bce91bb7599dc6dd88b GIT binary patch literal 7537 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 +#include + +#include "raylib.h" +#include "raymath.h" +#include "rlgl.h" + +#if defined(PLATFORM_DESKTOP) + #define GLSL_VERSION 330 +#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB + #define GLSL_VERSION 100 +#endif + +#define MAP_SIZE 10 + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + SetConfigFlags(FLAG_MSAA_4X_HINT); // Enable Multi Sampling Anti Aliasing 4x (if available) + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - lightmap"); + + // Define the camera to look into our 3d world + Camera camera = { 0 }; + camera.position = (Vector3){ 2.0f, 4.0f, 6.0f }; // Camera position + camera.target = (Vector3){ 0.0f, 0.5f, 0.0f }; // Camera looking at point + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) + camera.fovy = 45.0f; // Camera field-of-view Y + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type + + Mesh mesh = GenMeshPlane((float)MAP_SIZE, (float)MAP_SIZE, 1, 1); + + // GenMeshPlane doesn't generate texcoords2 so we will upload them separately + mesh.texcoords2 = (float *)RL_MALLOC(mesh.vertexCount * 2 * sizeof(float)); + + // X // Y + mesh.texcoords2[0] = 0.0f; mesh.texcoords2[1] = 0.0f; + mesh.texcoords2[2] = 1.0f; mesh.texcoords2[3] = 0.0f; + mesh.texcoords2[4] = 0.0f; mesh.texcoords2[5] = 1.0f; + mesh.texcoords2[6] = 1.0f; mesh.texcoords2[7] = 1.0f; + + // Load a new texcoords2 attributes buffer + mesh.vboId[SHADER_LOC_VERTEX_TEXCOORD02] = rlLoadVertexBuffer(mesh.texcoords2, mesh.vertexCount*2*sizeof(float), false); + rlEnableVertexArray(mesh.vaoId); + // Index 5 is for texcoords2 + rlSetVertexAttribute(5, 2, RL_FLOAT, 0, 0, 0); + rlEnableVertexAttribute(5); + rlDisableVertexArray(); + + // Load lightmap shader + Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/lightmap.vs", GLSL_VERSION), + TextFormat("resources/shaders/glsl%i/lightmap.fs", GLSL_VERSION)); + + Texture texture = LoadTexture("resources/cubicmap_atlas.png"); + Texture light = LoadTexture("resources/spark_flame.png"); + + GenTextureMipmaps(&texture); + SetTextureFilter(texture, TEXTURE_FILTER_TRILINEAR); + + RenderTexture lightmap = LoadRenderTexture(MAP_SIZE, MAP_SIZE); + + SetTextureFilter(lightmap.texture, TEXTURE_FILTER_TRILINEAR); + + Material material = LoadMaterialDefault(); + material.shader = shader; + material.maps[MATERIAL_MAP_ALBEDO].texture = texture; + material.maps[MATERIAL_MAP_METALNESS].texture = lightmap.texture; + + // Drawing to lightmap + BeginTextureMode(lightmap); + ClearBackground(BLACK); + + BeginBlendMode(BLEND_ADDITIVE); + DrawTexturePro( + light, + (Rectangle){ 0, 0, light.width, light.height }, + (Rectangle){ 0, 0, 20, 20 }, + (Vector2){ 10.0, 10.0 }, + 0.0, + RED + ); + DrawTexturePro( + light, + (Rectangle){ 0, 0, light.width, light.height }, + (Rectangle){ 8, 4, 20, 20 }, + (Vector2){ 10.0, 10.0 }, + 0.0, + BLUE + ); + DrawTexturePro( + light, + (Rectangle){ 0, 0, light.width, light.height }, + (Rectangle){ 8, 8, 10, 10 }, + (Vector2){ 5.0, 5.0 }, + 0.0, + GREEN + ); + BeginBlendMode(BLEND_ALPHA); + EndTextureMode(); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(&camera, CAMERA_ORBITAL); + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(RAYWHITE); + + BeginMode3D(camera); + DrawMesh(mesh, material, MatrixIdentity()); + EndMode3D(); + + DrawFPS(10, 10); + + DrawTexturePro( + lightmap.texture, + (Rectangle){ 0, 0, MAP_SIZE, MAP_SIZE }, + (Rectangle){ 0, 36, MAP_SIZE * 4, MAP_SIZE * 4 }, + (Vector2){ 0.0, 0.0 }, + 0.0, + WHITE + ); + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadMesh(mesh); // Unload the mesh + UnloadShader(shader); // Unload shader + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + diff --git a/examples/shaders/shaders_lightmap.png b/examples/shaders/shaders_lightmap.png new file mode 100644 index 0000000000000000000000000000000000000000..bdd8d3b91c8484ca4c8ac2edf156ee6988e95671 GIT binary patch literal 229671 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%0A0bd6;+2`j10!2y$Kb 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 z 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 I-Jg~Ff9qpl!2kdN literal 0 HcmV?d00001 From 5573f0f1c7b29bfe46d0b70487e4adb4d01cba62 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 4 May 2023 20:35:20 +0200 Subject: [PATCH 0061/1350] REVIEWED: Ligthmap example --- .../resources/shaders/glsl330/lightmap.fs | 13 +- .../resources/shaders/glsl330/lightmap.vs | 8 +- examples/shaders/shaders_lightmap.c | 132 +++++++++--------- examples/shaders/shaders_lightmap.png | Bin 229671 -> 209671 bytes 4 files changed, 81 insertions(+), 72 deletions(-) diff --git a/examples/shaders/resources/shaders/glsl330/lightmap.fs b/examples/shaders/resources/shaders/glsl330/lightmap.fs index 95558610d..827473d25 100644 --- a/examples/shaders/resources/shaders/glsl330/lightmap.fs +++ b/examples/shaders/resources/shaders/glsl330/lightmap.fs @@ -1,4 +1,5 @@ #version 330 + // Input vertex attributes (from vertex shader) in vec2 fragTexCoord; in vec2 fragTexCoord2; @@ -12,9 +13,11 @@ uniform sampler2D texture1; // Output fragment color out vec4 finalColor; -void main() { - // Texel color fetching from texture sampler - vec4 texelColor = texture( texture0, fragTexCoord ); - vec4 texelColor2 = texture( texture1, fragTexCoord2 ); - finalColor = texelColor * texelColor2; +void main() +{ + // Texel color fetching from texture sampler + vec4 texelColor = texture(texture0, fragTexCoord); + vec4 texelColor2 = texture(texture1, fragTexCoord2); + + finalColor = texelColor * texelColor2; } diff --git a/examples/shaders/resources/shaders/glsl330/lightmap.vs b/examples/shaders/resources/shaders/glsl330/lightmap.vs index 00278eaa2..d92c2f097 100644 --- a/examples/shaders/resources/shaders/glsl330/lightmap.vs +++ b/examples/shaders/resources/shaders/glsl330/lightmap.vs @@ -1,4 +1,5 @@ #version 330 + // Input vertex attributes in vec3 vertexPosition; in vec2 vertexTexCoord; @@ -15,13 +16,14 @@ out vec2 fragTexCoord; out vec2 fragTexCoord2; out vec4 fragColor; -void main() { +void main() +{ // Send vertex attributes to fragment shader - fragPosition = vec3( matModel * vec4( vertexPosition, 1.0 ) ); + fragPosition = vec3(matModel*vec4(vertexPosition, 1.0)); fragTexCoord = vertexTexCoord; fragTexCoord2 = vertexTexCoord2; fragColor = vertexColor; // Calculate final vertex position - gl_Position = mvp * vec4( vertexPosition, 1.0 ); + gl_Position = mvp*vec4(vertexPosition, 1.0); } diff --git a/examples/shaders/shaders_lightmap.c b/examples/shaders/shaders_lightmap.c index a37c9922d..b636c8b28 100644 --- a/examples/shaders/shaders_lightmap.c +++ b/examples/shaders/shaders_lightmap.c @@ -46,81 +46,82 @@ int main(void) // Define the camera to look into our 3d world Camera camera = { 0 }; - camera.position = (Vector3){ 2.0f, 4.0f, 6.0f }; // Camera position - camera.target = (Vector3){ 0.0f, 0.5f, 0.0f }; // Camera looking at point + camera.position = (Vector3){ 4.0f, 6.0f, 8.0f }; // Camera position + camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Camera looking at point camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) camera.fovy = 45.0f; // Camera field-of-view Y camera.projection = CAMERA_PERSPECTIVE; // Camera projection type - Mesh mesh = GenMeshPlane((float)MAP_SIZE, (float)MAP_SIZE, 1, 1); + Mesh mesh = GenMeshPlane((float)MAP_SIZE, (float)MAP_SIZE, 1, 1); - // GenMeshPlane doesn't generate texcoords2 so we will upload them separately - mesh.texcoords2 = (float *)RL_MALLOC(mesh.vertexCount * 2 * sizeof(float)); + // GenMeshPlane doesn't generate texcoords2 so we will upload them separately + mesh.texcoords2 = (float *)RL_MALLOC(mesh.vertexCount*2*sizeof(float)); - // X // Y - mesh.texcoords2[0] = 0.0f; mesh.texcoords2[1] = 0.0f; - mesh.texcoords2[2] = 1.0f; mesh.texcoords2[3] = 0.0f; - mesh.texcoords2[4] = 0.0f; mesh.texcoords2[5] = 1.0f; - mesh.texcoords2[6] = 1.0f; mesh.texcoords2[7] = 1.0f; + // X // Y + mesh.texcoords2[0] = 0.0f; mesh.texcoords2[1] = 0.0f; + mesh.texcoords2[2] = 1.0f; mesh.texcoords2[3] = 0.0f; + mesh.texcoords2[4] = 0.0f; mesh.texcoords2[5] = 1.0f; + mesh.texcoords2[6] = 1.0f; mesh.texcoords2[7] = 1.0f; - // Load a new texcoords2 attributes buffer - mesh.vboId[SHADER_LOC_VERTEX_TEXCOORD02] = rlLoadVertexBuffer(mesh.texcoords2, mesh.vertexCount*2*sizeof(float), false); - rlEnableVertexArray(mesh.vaoId); - // Index 5 is for texcoords2 - rlSetVertexAttribute(5, 2, RL_FLOAT, 0, 0, 0); - rlEnableVertexAttribute(5); - rlDisableVertexArray(); + // Load a new texcoords2 attributes buffer + mesh.vboId[SHADER_LOC_VERTEX_TEXCOORD02] = rlLoadVertexBuffer(mesh.texcoords2, mesh.vertexCount*2*sizeof(float), false); + rlEnableVertexArray(mesh.vaoId); + + // Index 5 is for texcoords2 + rlSetVertexAttribute(5, 2, RL_FLOAT, 0, 0, 0); + rlEnableVertexAttribute(5); + rlDisableVertexArray(); // Load lightmap shader Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/lightmap.vs", GLSL_VERSION), TextFormat("resources/shaders/glsl%i/lightmap.fs", GLSL_VERSION)); - Texture texture = LoadTexture("resources/cubicmap_atlas.png"); - Texture light = LoadTexture("resources/spark_flame.png"); + Texture texture = LoadTexture("resources/cubicmap_atlas.png"); + Texture light = LoadTexture("resources/spark_flame.png"); - GenTextureMipmaps(&texture); - SetTextureFilter(texture, TEXTURE_FILTER_TRILINEAR); + GenTextureMipmaps(&texture); + SetTextureFilter(texture, TEXTURE_FILTER_TRILINEAR); - RenderTexture lightmap = LoadRenderTexture(MAP_SIZE, MAP_SIZE); + RenderTexture lightmap = LoadRenderTexture(MAP_SIZE, MAP_SIZE); - SetTextureFilter(lightmap.texture, TEXTURE_FILTER_TRILINEAR); + SetTextureFilter(lightmap.texture, TEXTURE_FILTER_TRILINEAR); - Material material = LoadMaterialDefault(); + Material material = LoadMaterialDefault(); material.shader = shader; material.maps[MATERIAL_MAP_ALBEDO].texture = texture; material.maps[MATERIAL_MAP_METALNESS].texture = lightmap.texture; - // Drawing to lightmap - BeginTextureMode(lightmap); - ClearBackground(BLACK); + // Drawing to lightmap + BeginTextureMode(lightmap); + ClearBackground(BLACK); - BeginBlendMode(BLEND_ADDITIVE); - DrawTexturePro( - light, - (Rectangle){ 0, 0, light.width, light.height }, - (Rectangle){ 0, 0, 20, 20 }, - (Vector2){ 10.0, 10.0 }, - 0.0, - RED - ); - DrawTexturePro( - light, - (Rectangle){ 0, 0, light.width, light.height }, - (Rectangle){ 8, 4, 20, 20 }, - (Vector2){ 10.0, 10.0 }, - 0.0, - BLUE - ); - DrawTexturePro( - light, - (Rectangle){ 0, 0, light.width, light.height }, - (Rectangle){ 8, 8, 10, 10 }, - (Vector2){ 5.0, 5.0 }, - 0.0, - GREEN - ); - BeginBlendMode(BLEND_ALPHA); - EndTextureMode(); + BeginBlendMode(BLEND_ADDITIVE); + DrawTexturePro( + light, + (Rectangle){ 0, 0, light.width, light.height }, + (Rectangle){ 0, 0, 20, 20 }, + (Vector2){ 10.0, 10.0 }, + 0.0, + RED + ); + DrawTexturePro( + light, + (Rectangle){ 0, 0, light.width, light.height }, + (Rectangle){ 8, 4, 20, 20 }, + (Vector2){ 10.0, 10.0 }, + 0.0, + BLUE + ); + DrawTexturePro( + light, + (Rectangle){ 0, 0, light.width, light.height }, + (Rectangle){ 8, 8, 10, 10 }, + (Vector2){ 5.0, 5.0 }, + 0.0, + GREEN + ); + BeginBlendMode(BLEND_ALPHA); + EndTextureMode(); SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -139,26 +140,29 @@ int main(void) ClearBackground(RAYWHITE); BeginMode3D(camera); - DrawMesh(mesh, material, MatrixIdentity()); + DrawMesh(mesh, material, MatrixIdentity()); EndMode3D(); DrawFPS(10, 10); - DrawTexturePro( - lightmap.texture, - (Rectangle){ 0, 0, MAP_SIZE, MAP_SIZE }, - (Rectangle){ 0, 36, MAP_SIZE * 4, MAP_SIZE * 4 }, - (Vector2){ 0.0, 0.0 }, - 0.0, - WHITE - ); + DrawTexturePro( + lightmap.texture, + (Rectangle){ 0, 0, -MAP_SIZE, -MAP_SIZE }, + (Rectangle){ GetRenderWidth() - MAP_SIZE*8 - 10, 10, MAP_SIZE*8, MAP_SIZE*8 }, + (Vector2){ 0.0, 0.0 }, + 0.0, + WHITE); + + DrawText("lightmap", GetRenderWidth() - 66, 16 + MAP_SIZE*8, 10, GRAY); + DrawText("10x10 pixels", GetRenderWidth() - 76, 30 + MAP_SIZE*8, 10, GRAY); + EndDrawing(); //---------------------------------------------------------------------------------- } // De-Initialization //-------------------------------------------------------------------------------------- - UnloadMesh(mesh); // Unload the mesh + UnloadMesh(mesh); // Unload the mesh UnloadShader(shader); // Unload shader CloseWindow(); // Close window and OpenGL context diff --git a/examples/shaders/shaders_lightmap.png b/examples/shaders/shaders_lightmap.png index bdd8d3b91c8484ca4c8ac2edf156ee6988e95671..0fd67a6e06be36081d8add7d64815153fca4776d 100644 GIT binary patch literal 209671 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

?FvUOtu2^pu?+&9 zL;pw9nMWm+|NkGj2&f3+iYq9VRxT*!o`4!Ut+v~aOG=HI znpBmF{k{Yi>Fwy;{_AU0gNbVc*hv*nxDt7QeRf ze|Y^@Y+T7JBLd1a93)&?cM|VD3S1QI#fzNCd+K7vlD%B1C?}+Imm1~N;xA?;Rf0~1 z!PU*$seQC)ZLrmld1FGzaRH+{kIEE%Ti@VvIf33{_={95(;{? zcJ}gY2l?QIYz6EsLaUmdMfO$ z<;2mIx=trdI6vs0L=ozA=!2gfPH;2A?@^G#%x0k2sr~bKp+1;EoOnT%ba0|RFpRW* ziweLD#e#^-qmV4iwfzJ#^uuD&8$>bYzq&i<^cqKbd1Llp-A&Q1SKGmYd*>zG%H>D3 zFsxbjnNoT@(=qHvs%=7gbD;5&{A^Xr@(bCKBoC8z{{$*yW9ty8R_tAwba%ZP+!9WI zi~=5T;cdRo|FPgd56YQ&T!+={%WMia5y8jYK# znuEzqt&+2r&sAU~HD|OwV(qiyU=K4DP>$KTu`w(f98_bswRlAMvj* zNp)qgA;~E%oKr%Nc%D8>2i##WxejDkO56aO1pS8b6=7e9A(2Or>iy$v$lM#+`2JiG zI`j)5`Y8I-B3;@ii2?hur3Hhe$nbf84H?qYJ}YjCEV&h6p8hEltfwCCWx87AS&Xby zrwfASMOfnNU6H^`4Fi}~fh6nG;-pJ0sa50rUlEnYg%xw`C`isl7e!b-H?JDvKKpuJ zxEb|CC_7F4Py2VM+14-Vu;LoH;RZ09T`Ol{)5Dh!;u2g{YtY2( zT+=ODZW)0d)-q;#c~VREMZ>|9h{OqNJAWR1>)q_H^;0ZCHV4rf+E9C*PLboj*?T%b zkhO!%99uX&ztAZkH*q$>{F!<@ls}C%MTgB`M>C=?gpf!$o>E|0eKQ=ZKPhX@JC$7& zVW={Lg2oxvd!*n|zEgXgML&ywk`AoKmFZ7R_^?$%eM*M+fbI?Y6-j%9AL~5&0I3YO z6uu=^Uphq_46nYUVwpM=*;E`_~1<`@uMqmTZIVbbH1QPQ}?K zcqPd5tuojR#*{>nlL|ZijuuADeOuH%ul90k0tX^=lTusvf}NABmmHP{Fq|+@Km$hC zXT*UGM<;nJs@L2Z z#0aWL>C*=0wxB1|AR@JKrjq%r0SD<0zqiKQ-=;Q#f3Rl`a~+>IYou&6Tog0sKMuT( zD8xv4(C@b#o1f9fjj&DL=yWTt_oT|x+(Ee%&WV0$Y*|VR~E5l4gI~h^`S$JxR zTY{=MKKazzha8Hur#xwsncs<`HHH^J>iVGclJkJ$#=9FxH-^|eo={(|{gXXRum!n{ zI|0hlI^FI6kA425!Q~ozl;-4S-ejtjgdMkegw)78hSsF1<}HYz>tulHDBg!ZZL-lH zdwYEb#e>zTc!bgFMWTPQ?^+Po$`$3>r~X_|3~)DauT2N3JI0r(UsZI z@|jZ3b*fQq*y55|xsmo-a?RlU6s@Zi^;i12Z?M8R3(zv2QssXq5dZUn`Q6jy`*oI9rq^LAw`HWl&H((BF~hcgoRySCN2n z-AIk18hHdD?+-5^t)Els!K8FfyvUTAt(KpSThe1HNJji~r&1OB;UdDn6-#E;(`{8> znb*4nr?#t`9*}QkUU0b@{8PmRZ%+hBi>jg%yXHl&DezA8#&5J|$EBs@fm!b$&{N4T z?G!qrS6i#regyegWM$VKq~d4l0vzdqKj@9`n8uvm?f8{5bzJ@weQJUhLV}zMwrN2Z z(uvO=p)Pg3E_|(-HB0N*gA1FjKnq`Uxh^J|1udreg<7u}AXj=JjPnC<4{O@)1BttK zrq;VT5QgN=q)Lo!1@DHQ7L7*-bD*9@oD1VI_PDY$MeRNt)3>;zS*j=XebBZ1BB5l( zP`{jmh0o-KFV6=Zn8x&D&Qg?UITSPD5eEC!$@Cq^`NP*$2IA4RDG~NDf+OcxT zj%M{eKOic@{<3gtjC+fsXO0w&!tojlp>0wylLKAZG1O40c_}%<*J$WK(NpUUgVNto zU%S?=4r$(|aiJc+dJtB#4$wGzj4E@VW<&3?dk`X3za!Q8m~w_FlL>$0?B|udRXR(T z_|}qllLpBpx{`vQVvteacPP+1$kd&$Y|=7T*@UMzqCuM5JHqxA-Cx}L@bQe?6zA(3 zj9rFi(9Qlg$?h@qhZR$T+Jvpr!i-kU?8n*Gl=)Um(b{}88@0dTc_}tWIF*cw@O}eD zMEAl|=0Xozdp1{xN8-=mhLfWwEsXEV8`QuQTDtu$TYin4Jgg5&h35S1=p+v?h5Es` zqgI^4>~4HVbf0JPzntPb-|T9{@AXdy%15vnbETY0zfJKa`y2u-xd{z$2c-Er^6j(zU!1BsK5UWpA!pCvM!Wr zeMmRU@GV4dU7G&NFK|BEkxCm{M|7-qH3D|Pms^Iy8)6;LyhIdH5!2!)MVJ>giUcS6 zK0Arq(I%aDzxmzvEpEMHa7FKL-3C}JF1y|zDX<=4?>GT|7;b%S2M;$o(p}V@*6!0P zQ^_Te20VwlPJ6CEeeaXqQol&+^9u9csARl6h;)UUE9bJ>kmhU_UvqU>!8o=Bwa%Hp zBeu8V+Q;-BSh(NaWJ&jas<`eE0jCH(4OIBR)3 zQJWS{%Gb&zOXk=CN7J^f8T!T+WK?E1ePgO#w4we$FlCpI&z$N<{<@_7JyMdl%}9>w zp<>LcE$HoXxkJ00u=FqKPx-9h9(L$7GUyH^y%kBRP|^?RITbI0oOfF!dPz)1WIzN` zwy@AtlZDmW1q4Z6MV2DR?(*v6+F>locMG6;H{U?)@kH4SGTd=j=h>v=gczQn#F73q z$kT(F$apFkMVJV^H_tX}U^k99eje?>DdKHu%ih8`VO+-3y>=4zf(*#q8`9f30f)0U zWU)_4vW%+vROko6cfy4Gd-h>Gs`bnup;EwbOsx zLC+u+&X6FSx){H;6>H>${cMDr2>z-Vt{7fmS$3!4OhLTDodtZb^milf>APuLU%>W< z=Si>k&V616>HbG=YW>~t*K$J>czFozvm%Fc5pK!z8osY?-XP`!8jrG_0J%9fu4(~b z!b5bEBoP7R$6$LXGYI*OQE)~_Ry4VnlguDui2-Z$jD%WgB~(p#Vr5K6r4^;9?=ALkl|%-nK^hFR12J18_m_jZ1Z;1L|GPAls@h_;b=+`Vbm8lYLz& z!tQzB;Topu3E;Pm{d1m31VJ*g#!xukoAZGj`Ry$2<%~-BCV^Bp!WLZjsGCF^bAt7- z=G*LTatBg_jsk3G86sM|IO*t|_}4DQASTEIG;M{F>FDD_A&TUZI}+O&aiqlw6C+8l z;zwgoKuRbK;1LG%f$qEu;avWp{B_Qn(Cg&w#1)ZLr92f_67382nmypfmp#g<6~o3s zNsXq3TinkXLAZgWV5ud?Eu$!MQ%)Y4_s+7wYMZuKB%EuJ`C=YxljvGzFEw zv3CT@c0){%CtcB?ycvgvAd_XjvtwFdS1j*@3_YP5yJplOl`pIP?Ou?7t=NVO5)H5g zyn&CdihXcg~e z>qyWEH>lfG8!)(`y47jTqPi7b-z?hRkMilue}E$3m`$YY15vRqST4bX{JayZ{NCel zan)^#5iWl% zUP&|;2c#^Bi?{aoKgjML3WqP^t4M0~*C%K?BBbK!UYii6ySCP6sWfp9zg%a!Mjfh} zL<5BmT=-_62#|qJ@8Oz0Anb@$J2Rs)^l3H`#F3uQpv?bkBb3eXQ5`98cyvQb*3n_M zPQfn<({A%K(Hp*5=5R}icJeAW{c@SbfeBiqKujLsABpoS_?35)Kwdky5-9mAYwqx0Ww$~^m$IaiIL(O z8gG7u&d62od4dDN{$o_f6Ol{LxMS1KQ4%>PfRVv1ObWp}9tPapg5*4T7@+o#yn}OY z0%)8YQ01TD#6V`Z(8E}Ir=M@8Fa4BXT@Jea4}XEICZOoA&Ywq@q0tFGCDS}lI+3BL zJqpuBt6$}=Bz}JNB5f*{35WB7N|>Gu**cVfhfT_G_|>$AK>NqfQTUHfqkBH_v3=&m z9p}_`9MgJPPa4jKT{AHUn};Oy4^h)j)j$P;4QxxzUkBctpeWxFIa8Ww&%dF?IN{AS z=~$scp>KU;k1YyfE)Qh&vlc0q8#B6zVK)@zlv9Q3j02a@Rro!794{;nR8I5bB^v3HT7 znu-Ej@xP2lF#USd&)MJ3;-EJ+f`|yBzt768num2EkjLZJ6Btnu<`Fg@{Uf#NE;iJc zxUc!ucHxeEa~D}B1B$H&`ubiaDG_9%bEeqP(rNJ)T_|Ckmba%lH@Gg%Kuo~`%->3L zU&6meG966l;2ELm_<5_f`EFa@JBqN&Y|h`3M;QU{h>HE`=fta-^Fm|TGPT0?Z_JsD zn{m15&0{rIs!xPsxb1dlokcC96f{f3XZRX8kr}67fU;c za{~?DplDRY%7nioP>Fs??o=ut#K0C#|2l?|Ce)V`N_yq!xvZ z5q^K;V-hw?0WskUr+=S$U5}4F)Yax&e_h$_F>HF1NW@OvU`mBZ^LUZT2>rXXK7EUkEPCa;_rqmC$eNjGrEG70-oW`% zC9URyWM0x@LA_63p<`Q`J*nJfPCT%!X1|ECq+m{zF_ju=rd_qY2vI~cL^2w}qfY{>0Pv z_=5F1)14nP&?`FTBjke93;yfF_-nP<@;?_sKQ{egc~%&SzvgWl`K?qlY$~MvfZTSL z{11-G?1o1<8m_qNa|qLTuoKB3E54@vfmZk`72#1yJq5yiD zX{p#kv=uuaks-cR=@Lb)bxxp;_XzFr1HRiH%I~Sgwy~>UA-*5DC+IE~`4yzlf^;di4WEbaHb5w4WjoyD-w=wGuxqE-G-yfpcJoJ@f^h@-NxN=hX zymuPV3s#zTF50dL2MCt$Q0+I3`(L1*8GcBbzX9VsJ?yDFnccktY0$T9GErWfG*q4K z>IVU4oBrXm4l5W_Hjy~N&sp)fwb#J~f9ES8^ZB<+t?X~LTAp7DyMM7}9006w&BJouv8MofEi%Dvm+3P%&ec4T9A{OX6RNndH}z|K!zIoC5QR+pH%$g)he~nn zda!PDs`fs7lEkoDXMgy6w(!p&B6G!bxIZ@?7#?DvJ}&f%`GTFq09_fy=cQ zd#9+(C2c^yF_4LB0q2M0hF$4n)H(fLt#W;Ie1NPY7dD=j7Oho2h_CvvohU0*4P zJ{11RYHZBEHmVV7q_1%7@vu?6Q@IdiwdmjTR6BFk>^~Q+UPhf|AMl6%e44gai^SrO zVepn$N+s`ex<=6^P1)5MoC2?)-=LG}D+xi%HacbuVB1e#my?2F_jF*xi{Q&u>CKYU zYa2Wm^cc5=$0+iB(7f>sA~+kHz!Bm;EneITH%G{=@$pPsFL{ABubW3}Uq9sBUO7Fm zp^>t>2hQa8lO|ntv=TfwFR%^b>pdJ)@1wZGF+;+5Q`G`*lV+l!!Vcto#uf69O1_7j zrEn9V+vaii0&l3M=g2wufl{p=-r+#6h6pEFdot{a$|&B;?MR)-=touq^UE}dHU0pX-}1#*fz!szWia>PchSsK-O1_yD&GGu@8Ieo5`l0&24_jnA0$%QNC$X2vJsh+1i$>4U)3kk65DJFn2U#9Lx+6~n9cpmu=K z@z+s&j~z3S^EpEPK-5eAqp2iDcnj*=&HT=pA1|cb^(SljMP9e$h~}G~-$La!M7T;m z)K<5+Yht!&zMMk8j4QnMC0*aVHeb3)#xd#Swq4;w@_miFZ%rfSN&X5G)f;i*Kia_B zFiL>xo?;JfPWQA_Dzf5N0fBMX09E^^5kK-&o%*K7s3j~}!ONzn2Ctl8cmA#X+)hV1|MLQnw$ZnW%TBA5+|IkMqmcO zi(SbmLeuDBrDerFsB3kp&Gjtd^92qW`?AF- z+&I0bg>e5%0s2h^D)*h&QNtrA-H%?vI=yOu>%vCZPd3_1J|q8|mT%Wq0tncLZA57U zU3E@^uv6fOa{mYpm1isc4UaWABXU5CC-24UC#602Q0$?g`Tm%MGKV^?Cit~nWcE>W z1eS*faPCE%GNMS`%jCzcrKLJ9Rd-g&!x#K_J7vD!AbhNt^WS-24V?Oi)1ITJ*GKxJ zBVB5`XT_BerVih77s)ss>1e4>A1bHtf?6^*CpYA0xNZ!tZOE>i$iHHEPFKE%dlJeK zG9TsR63s$K_%S%M0c-u%o}`9&R|8U+7~~RKK*Drkie^ z3Cna${&EkTAODu|x{Gt7>95o1Gd~uZOeTr4EU){8_~)IttGyIy?|HTlU|DvipdOb; zpvsrXfIkS+H;RYM}xz{EG&4j^VfNhKl`GEw!*ulF(^|xWdH~D=KMn@Fy zM321Nc1=H!dcxJ|NQs#mFK~rAFkl)-lAKKRFvy0!W_T@mvl-+}q(+Yi1OI}!s{xE9 z^m!klNale`?8h}gyZwC84!UNbhdEIjC|UL=$1<18c)h94!04G=;$^>(!Q+a1L;(fCOXr z3S&e*y8#U-0|!0u9VyB9&g!&vcSC%^dDGoV*!6om>dd@KZ47~Lsa27IMx&Vs7BJkl zt5{(<*}%niEZyY_aBZ4Bhy5=GFNDIk%+3TTpUIz+0w9~$eD#xPOkjQefGnpnuVP|@ zhkHOQ5+kd<4C0Yai-!GAG^JV^*zQuH8(=5hNm^eY(M_Ie+n>31iNU1Gr@1Hdt@+)` zNvqy;Q*#g{5bVlwl3*n7&kL8r9%uiVm^}$H)BZr+XgV1iQDl~i{Tp{CA6PT)KQw*5 zq~T^Oz=;f}tu=E@pE^pRqeHN;3dgtwXWHTFSDg@3ROwGTtC*=b`G z;h|l~Bh=mtY91eOM~)a+e?3PTy1a|1TxAyNT$3is;qGTx&`heG_J>e*0>5YRg|qlgM8`bJpPK;ICkb;Fy_~SQv}u}e7m#V^ zmCWCeF+dnt2OSMTFG&xMR96otYRuCvzfUub3c%6-YTf63yRT=|#}L9BotP&I5*GX? zu`aBlbb`SmMfoj6mK2=fauEdKhzTaN12)&@RSyg|mNl9D!*s-fAlfQ*chjN)BdSrL z>k`I@T5pc+W5QaNavXp*;_|5y(<~hxeUh_cef6GvXOs?tFZhy5_*YsM)`HUa7aM}$ z9r6Z9Cr#u49SHs2X9Dti-!hrW^O(DHdO*!%_Nn0Rr!vi;=h2f+Oj!Ih^Om}xr!|p? zuo!6s`be)c=q`kG7cYymIgTTkYXj^j`Ivkyy;he>?xPd7xXmnPcrsNons&oO z^4agxW=@~FtF9)k?;vwt;cgl_2>l%d{gs(3?4hzuINZqVW_}$o5FxdlPgUwo&~g^U zR^)KRzuSH&7SN1}($dhUJ>r&6Pb*@0lHJy&XdRcC-rhF|hbfGH!B1wp=o1%AK|MHAG{}G>&qs(EfDH1R*1Gqy z)pXvVI!Fduer;l@^>bTX8E^q~c$@CRoj?cw>!@>Zv15#AKlRGYDUrTo=e{th1Ki`U z_Fu!7ukh&m>Brf(&VyYtb*_+HHBS#(*B!#o82E|EjU=i6!KXK;hyNw~?8sAsb24(ewHlkzK{hZYeKK^jj z3{qR#!O1NROyt)ab{x0aXTTW4Enj02dkOPrI@*~qAc!p26+|<&Us<4gGhZ7CKZ&}= zc{B*Rdl)@sP&b7CEAuGHm+kyVLx(R5A8z2(0auC8c}K&Joa!Y9ZNX~pESZ%w_y3Sy z=6<2VpW!Pr;;Xb`2{)>#0e2Q8A@zEPC33%~kMjLkkiDv9I0(GhRqkM)uqnpO%4T<*QEathY1;ds|Go`^mHi-9N}JL9~Lx z?;d#wba3ck)Yj~L>Ar0M+&!o0Lj-2I2-S?pJ4Mnz@m7@*%o&E~{P!^)UrcL9i&j8U z<23g!izTz<^?_h(7CLwcSLM|RSCP;5+L#%HUFU=0(+%=A|1>lOmoK~94oBhIlTE`b zdorRNuF_`#l<<_Ed3;b4;cSIl`!xA)18R01v~9DK>JL60Pk)ytT%}?^9Ar94w2PU4 z`fZ0cG%Gl%923upV_=;Q@=v?>PmAxHYJR%F#Z}!iQ~kL$$zu^wVA9Jy%m+mZD08)N z%1v@!HCL5%URdQ(?YpiTfTP9UL(n& zoe`)#U&w>$QM*K-O8J%ct)n7>A%{yCPteery;&amIc~1?{^c^tWxjklE6uwSeNE%7 ztwy(&el3X7d?SvZ80Hhb@=<)*34Iy@KJFs{wl#5$*Q1BDf=4+8>oxnreyJYWuB|J{dxK1%&~Gwq%)`iHA?^oHVnTn6+zJ_G$y zg1>?`?^nQw-r|{QMqKOd!Y^|iP>t*ow*wI4|E#&FDOm1FQ!x_3h<=HTgljKnPSHCO zIZXV(Z1M3lLpDUY;d9f6QMTpzh=8?NVZGOp z-+mEpM}H<`6S%NhLF#`gPE6@z81|&Iji~?YYL-&tzCe^@0|I5MSsvk7~H*BlVxPM0gntp{RBFKX~DW)xgA@j)uk;hDZuo*JjE zr2#qHHB%RtHN-W(E3{a&m86GJI4cy{npf8eZLB44ZORrXDjx5lKVCy`t0>W0P7XIv zsE-!{#6CQPY8cH5$JU#d7%8GM_Zc{|R8c-GO)!5wS3VlPQD@cy(~eU?Vsd z=63_~&TEj%QD36zwf%z@X8+|qi3#z0(n|PD2Stg=L{B(Y>1i3j(jbl(DpE7HN3%e1 z6CGrpfgEcfdxF>1g!ls6^=ABzt!ghP8yrw&8$S3AL3X{Ejug~X0W&(51xK)_Gb@6> z(w_DKeQpEXv0HipdUz0oxXnZZ8U@t(U^#@Di=d+ATK^hj1v!4ZLjM5v~HiK#1 zES=L22v|Mtlv5z2`axrK?LpQT`BIIB$m3uO6HZE7zFuVj|A^pws%hT01;g}&T6#@o zBSE)`2eoi61zUVkLnm?;Saa!}adY@`C&Cyk^9lFmTna@;@Tf9Gl%hc z4c~kY%1d(K%b8D*3c%D)U|9}qD0IW>+aN_4bnmK?owA&U*VseT7E98Oko+oL=Ma|V za&55p1-X-iIJf9e={xfOxel`Ogy@#zfo%(n(ueTHEcC+;YAiyDT3H+lry%Qb zl`b6XJ+(expfrz63dj}~JjQ>$wp1z9KmUd`hkQfV|5pBo@Yi1in6>XF3dR=1O*BPR z+Avz(F=P^d?Yt3~raxU5F2|{2z`gLVmwIfE@q22>(AdgB-_Jf>>1)kRQQFo`&lP~B|K@=%zN6(@iGhpptIp4BuP0e}bXUpeeAW3f5 z3;49sjJ3h8Ib<+(&0Cg}(tv&^?RkPDKPM`q9N9m%q1$cI1!_oG#vLlM)`osEc!`<< z_wP384sN3g-G&IhqXPg1)p+(75qm+KZ*-t&v%WDt;cNdgLn`U*{YGVq_WrFIe;7IX zq8|~~$a(74<2HNky@77`LA=0vfE{_Bkc}&>N?Z3Z7>E=Wd_AoIDrzW-!b@JMJ5Jxj z6X52=ifmc&Xlf9nfO5u>Ymfi;l^nZK*)*N6EYv#nID6}Xh8q=^@k?gKo7ZqY!?d*Q zeqPDWqCS!{b{}gx)vrm6?l4n*ea{YGf-@A|CO`iTeMgM$k|W}Dtxcl!D{h-QfxBkM z-G)`}vYk@%IdKO&s5x-1BU_-?bo-Lb4Iy8rK74C)lnc-^+6yEtYOG$5Ys zUWbQHm_|RfY&r%g>4yO6!|+sExOY&{hS%7!WVoJ-@4ygVH{*=KZPGL6T|6c-;i8 zzXVaJX93GgB96>x&mBL^ zl)qevKBHDogyPyA#B>lay%p=iz>H?VQ-#S0Sp3b9cVBEF+&OOM%);E?U{v-h&{KWL zpuw1uuUK}QZwl(o^cK^ecHEsDmO{tT(S@kHAT2B zFqSt`15JQ@FFJ0ibP@S3MZ+#K`ikhH&nc2N>AI1!*+^C$%89k^StsBN=QXsjRnkm9 zjg3ik*W3O5#P7PX$%tqN_F@GEp%vgvNV-srM2E13{m2%_fL75NmqbXQYnsV`Oo6?n zxt83sJs>2SO&{SVXew64xr6kXF0o!QQE)WS!;F}XTY5ACyJDS+MP{x*Dn1Q^eG|4W ziV6rYxHFC}Z^%5&9#7dtUf`n$%4 zr2!Jp1&i0HC@;jn4Ubq%e4};z4NfJXFO!-!wSgUOtA%*W90-}?z94T&zd$?f9d|=9 ztTY>^-P(uNiYV!09$u(iOlKNe{Ggp#EMG!+!<`m;!qGEAe#ruWgDSgK-CxhQ6Z{IV zAl27Lh;HwQXV(gX6&zi-PfXvyyY{&WoE}ez={A?<{~lmQIBr}XseY{3@)*KW8o*zz zA8>4AbL9p*0=}x|fZ~7T>kdiV{?94k?Fa&Ihnt^PG&n zLh)qg@&_De?ZYSstM@3EK#ui3w5Mf0n{xJf!*zxVhJ5@>s@Ck#&Kuy&c z>_`|&cYP^c;C-S3R}&h9nJuwhLzVQAbi*=S?2EohjauOgG`rUe?+L9);93N<4(EjM zL%@O#<*y|Yzr1yt!vpV8-&-^g9=%d3P-@cThidIF=)_k;1P$xJh!3psYg}+%*x>h` z%Y5<)?iKkpIpMa(4~@+|MwM2hcLUt$8L|{yGzKq-0@TiFCv8;p3ODCULgnQda)Bl> zajrFv>T+25J#@TiIS6^NwsNeh(;Uv`#Co6O$UhqHYWQ}(hx6wNj|-#eJ!+OlwYO*4 zwMCtr6{y2n{n7AczSYB6=flX!kyyVT;TnFJ)Q*#|_(($0#|`N z$tVlQMeX{4Hh-x3Yk_}G+L8^r6=&90Wb&}oeHU@OSG(svMfug1R8lI?@&a_P!#ABUuiy?4P;UKwNiM|>0j7uNN-f4-WeL8 zh=j15ghJma>x;gax~=&tO{Hg2D~CV~Vc2bE^~ zuzWT*not8%(^nc%3YO)UYDh3)Hv~T{Up@yDX<2z|WP@R^OmHBuZh;7GlkRI4M^^%tS*T(NZxIxe1HX&q*YKdkb#9+MHK zr=j3yg9VtCym1FG<~i$)niV*v_YtCd&~)XZCkR-$keV-$Z40Uf*=LRhDp@LOFK=Qt zRj09Vh|}b(>${8+K5zZN+j)7_%gOV?N2p%T6QcjNj_haqkQ~C!kb4>X4c25734fq^ z6E4iJQi}+_aeBum5Ik@@LK@=sH>!=XB*f32%r}*K{fxD(x~ARSTu;va-{_KWbk+PH zjXeCZzaP2gRva`NcY8ZOqHQe@7EZ3*JKRh

G`p5$B$cEW<;11>?uTi&y4F8~XT^>5!|*KtOiA;-`{;Wr%XJ&#$(zWol~HD$ zl(yjk`!gB6CGp50+y3un-5>sBgzIWvZ|z`c{8_)vMl)Av?_=K%pK}LL^*HR@85(5| z@1fbC$j32z?|zT@;Sp=P-TF!(=ykm11bmM#3f=Y6RaBCQ!))&fwi~+fDDuJ@#41B8 z!kzBG0BQ&oheLcC!sLCgGQejLGn4hqaBoSAJB{-Yt8ki6GCnXh{v2q74LqM6Y`kQN z4xf@>)OXMcM~HSkFofIwLKg@Pc~jpSM_<-EN?p?l#5CGHSHrbMHN+!@%|ALJYR3E_&<1QvIxt#Bh1c=gCl`vv~1zb-{vSxm8BYwS;4YvCuh3NMeP zitPr}I{gh|H*4Dsy!aBKC1SvbS`IQl3^LuD(IQ>OEnOm-ccWfAxN}!Lk7E{s)_g+N zdtU|`iYaMJGN>fy?4Y+2gxNqO=ZKm6{2J13!2oypnXEMpcue3o{}uNoHw#;lmAS!~ zyRAqookQZ#Q|;z~F`U`^TNoDHMMp^-e>A^CujJzs&^{OrHlLy+Vfy07X{6633vW&q&YQ<{Os8Q};O$Hyn~o#@RW z?ai3z2R#y++2?`PVW)QqNuItwC)Od*rBe&AQ`DA4RF-Z>QG&`r=3Rc6@`x~9e^>mzh_4)Xe}`wLHl zZ+~V_ftrBD@&kms23x>yOH3d6NR4$vTo_E&Ws9#FHl~Oef5v!StkrKEIwq-C{}LN7 z{Q-(gcn+Sb7ooR$j1OBrLw!qKVP=a3SNTGU(&Z|>ow#+&lLuBk5e#_Vn&ccYttDs` zW+*w@8?q}0VaHZZ0nGR!Kfy5RYzAGYNw{ijbps)2kaf!Qw7E|0Z9PnQPsv0*t_yAs zcUiqVBIf3)xRbb>_XYl*e^eo!S`zw27CSpGkj!xuqL_$@_=}Bw|8@8+awJr5yPa8& zR?#k39^;e)p??bW^H-G~@?l5lrg>RElO8OErgN`zcb?YiDw@vS>?4j9M_#e^b}YL+ zvbkujV3xTLMOSR*)^azc`kRyP_}9YM@0WPDzhMRP-cwFwU)^hYdt`3^kPr`!&;!Lk zabx5uzGxpZW_J^&0S*t$c&?=Jt7Tt z?Gs=E-#;jlOqL${?Ahx6mGwpM?8xZ_Ac}tiwGhLUS^ccN7HvevMXIjnpN%VsRwA7s zl4I#A}b&C=$d795M^lmnfoVu4~KaXA00aGJIhd4}@90QSa zz`-?LXxl@+r$64QvB&o@1}#$ImcC1_u&=OdqorGVMIlr4W;|oDaansuHnj0Lb|HBk z(VdCH`Xpa!X3mnS->TA{zIMKF5nz|8ov6S!EOsMpP1NBObnSza5;DDV`N-P%436D* z(V6zW^Dn~9v9;f``VI8b5TmTTa{-0%Mb?B@Q|so24n^vhuN6tiWrr#pXNt~j z1U)Yrs2@}I{Lr1n*2lo(K_@HNMPrR*DzmViV&|XnJ4jIHMHO4lL~NOFMi>`h0(_6u zF+JXpDxzrVFE@%qgJM3xEtRWHo zsUvcYRSB|_)-xz*L~ zZE6HkCEE_Ur%to!-UK5QrTIHeDZzEOaq-uI_IEfBf6eG6RykYz-y~rPW#FqDZ9iM# z2+HC5ZKWDz2=Wc{9prfB)bMW2Bi4Nk&98>fV0dl;f1rPw2mp?xY6p%?Zf`hPWrZrg zem}7oG?dbPn8ryo zZ@5ye3t?E9Rm^cT)ep5_=<)QL`LJ5Rpn?4mkUHB)hlt4yRbk zHrG3Fb3SMd>zgK^F$0pxRFki`@`gyGjnv`04;2Zn&3LZq&GW^Y0 zLfeh;F`wAePctPMHep}N;`fmWKe|Qdw!zm529^_kbdG)nWJntNzJxY+AnAN?y(QrX zT<1rXms9OxJCL}uwwiSeYVQePA20%6YHysssj*l+AzLEgZU1KG{KgiC`I(G42Ph1zY4Y1CCsgj6>KqiG{90roDL6r z3G+F!W=uKTKM&q5o_&3>n(v#Ug$b3GSwC$Q6srFmb6VP0Y z%ziQ>+q#Y>eP2mCRZtN5{)KX_h5N2r2i@jT|LZF1+RnvOv+3%#Jx1ZtFPN5r-piNL zJ~Y(L?JaJsr-JYLy_jj@N8}IK_Sm-BC3oJ8v(#X%)2~}S|Awm>K~tEXnx%&nU4^@p zH#t#yozGoJW7Mw*I|6DtVo)O9i_tbVW}zQ%!h_F-@{hEHElMl27NQE2%3U--3JwI@G{&;NBCtSLpMR#k7E1tZtA$%p^xo9B&&2lvd$pRNdli@M}GF3$98RaY|QP z6U1|~;~`Y)HGT2G@}6O0_3dQ8Wk3cHG*#N|9`?J&8}L&G1sTbY9*9qc#?0!ii>J~) zG(Ioyr}DM~@|{zkvoLKW1^qE_Z&vQMaMpLIcwnO7V_V`m1KzP9`}mAt5o`%0p->aDu+gLlWBv80na6=#yLbK|CVf@ zNAMFJz-zNU!ME?tjB^`4y<_t6L+MwFs+qVVX7NzEVJ|Fsj#uMUD}ZUjM|X?YYYW~v z_fF$@=|z%v)cBW7@mvvT?Rb-jy>`Z5NOCzxVdqs#e;erDUH3Ps*mZOH?FhX~12#hx zgCT92fVZv3s9rLSbJ)BeY-W?$s@5wv;m^7Wtwb!y?2IOA@U=(J#(hLoyy*dyxFE`G zCS~4`a6@WXKh7M$29-(hyBL(Gxdg`vNp<12wUm4t#P%lm&^!-oYt5h^xC|Z1yJvg7 zj6ZT)ISiXaYx8fUh6RRKS)W~WR5MIC1LRrYmjrzKah*Wc!f^?0S+HF}&y1g}MDD$& z$P4?+pD~E#oUX_>NsRW-RIhUaqSk{wORSo|0bQ~Z(ZAnOzy7|7F@n>0pn)<~&jHVm zY>u-8&4*_6H`7X=kM}8!YRTW{jn7Oriyj9 zJ|y?4!miLKG!1o^O?liBV79;bfO32h1g77SnB-^WgTNs{D8(L)5k?(Z9>~3?Dk=mG ziu}x1v-<8c4RQw2>nXSk*9?WDi3VlIB*B|g-pov}iKFSh zL@#`NHK-H}hD2LG1GGD0H?K?7{<2eUKw^8k~emjTG*YCQ3_AQ;6EXi}q*LBdUx zn`ct{4avG{?F0F{Ij()^BM0<`hH8@2bQ0oRINqQi^asZ^UuP-CZ>So-^yL(*4}8OY z7_U6^O6NSl*ux`aG>p97;{Q6h_IRk$KCaZTH8XB$IWsPytkoh0)0o_yA-z)X=Dtx_ zhum`+vcg!Gnu{bhuhMi}vbogmD7tCFm^HZ^tV&wUprM;VyRx$HW9N_a`OKX2oaZuU z=J$Pmzwh_^F7f@2C#8MJIQ~8NgKr4eR&(^Vn)?f9$q&2S+J|$Sv^Hy!&XImE3|CHX zOK}Hd6f~1>s2jGnT-xhalhQ(0x`r-vM%BeOPbg({c{>;ZzkO+ciFhYAsbF;(0`yfP5+LJJ|cPPsYMK^ji%5_Nq61&#@3Ki zNdVONaRFHi;~9uP|NYlLDqfsra|L$lv9L4@Cf&gY2$3}_e2i6qGzw@?+m_VCska21 z=Zn}QT$Rkdgt`=mG3Jw}Zmfpm`kZQP<%G^jrSh{#nij27T}Amy0|?3aT7%9a3|Yg! zU)j_iZvtw~bt`&u3ca#WwP(n=ehL$PvrdKZ8A!^4PCA)TVEsKsZHikBb^yxjUETvv z36i~rUvhSSjmLM{UB?-5VOdn7NUc$}>3X4b&K0k$gu9+cZYnG0$#XYE@WxUZVhG(s z6>gbpgZJZXA;yXf0Z7k~Fl@Yu;6BxD#eR&C3Gc-<{;T@m#d~DauKANW<*Cy>8V`s5 z0%tf`6q+i0oR0n4NaCPi7#>YFL3Z(s?V0noRngjq(Im=>Om+GRHsd!^Y#LONj%r)a zDBL9iAD)n0prM2WCa^^sAA4yfUV^VL!m{E-I;THJ{X%)el@gfLI?@877lMq;k8ta; zK(#TDbM$+P&ggxV744kM76>UTOPb;zQHA>}=5>!x6o}E)jR6EM795Js%iEnC^kGTdSX2*i!C;YTi8;gk-QjM2;q!K#wPAB-_BZ@j zyM}>3NQP?;+w$|m1il7j&IHDUXyLo_r71{aPK~evYpJZp#zu9)1oTLT4r>&zpyQiW01m znr?!?7`Q8^rHa(I(eb?q^8i=YZrIgW2hI3l(p9P5S0~Pd@V=~w?2k&C3VcF1eh*j< z%_U1iNC@GNHrJMFSQfL;quuz<9`c{={vBl6bf@gbO7w!h`x#q-XbM}}n|&$L7)mH- z_9iB@-4NqEg5pBiWm(7l-k72`?aG_Nv%>RPlSV(2jU{`6{Smfo2YJZqC+X4EQ zr_!A*EX>V2=N7Ov+myv;;MQ%62K)y$G?KMIdWv$EBl@z_N*OGbK=sGS}cx>^|3==X5ycf5|2u zFMuKsdRfM{VB3}8_DD)dQK-T;(SzLX?F7qWq)tUz-sjIO0H&bYq(!T!F^RP5iVvy(T0YI7EtG43h3%Ph)*gsV4Mg*#*5AGX3L5 z>yeBNT=z?P<;)9&5kcuM2-<$4E7cjf?8P841YYZ4!eFxw2$<)})8=x^BcCImsLQ=Q z|IYUmIwM1>2$yzt7RhrSTbnd7reO1c`8{VcwQEoX{ECS_3tGgOXm&RG@klke)3Ovl zEyX_XaFD96^g7OglmesGb^75>YxXRLA42 z-@>vuMD!9&OFnB~B4JokOb)E$?p2YS6?Tm@Lso6QQ_$_wa-qtwDu{I#cBZo;qeaLA zLLc4!r^R*OxplR2f#bcs=a=dBI?z98Mm&=09M-JIRoG2~1iGhg?13%}r!jTNF~DX7 z8Q0+67gD#;poFetjjVlDuq+-Cy^_a@2m4QVrbb+;WPQPEiiH~Ejwe6K5~n#AxF#eD zj&@%)VaT{A#CgI$CUv$&pSosr_EsSn%#@EV>p(21ZLP@Stt`(#ed%bDy>IGJ34l1Y z8}uWW|BSjdT_bUvMpj4%y)9rF(dGl;QtJe`EJYTd5;!{8pKkb!_g=%LYs%N`JjiV% zi$Mw^Mv3QFhsUEyJytbBU%gl4zB6tPA2@aeh6?);C?EZGmm5&Igt!oXU#%Rw^wzi0 zsUT>auu2psit)y+yC_gP_>TdK!waAiN6a?hIQ_aSxQGo$?lk%evPe=R+V)DH41P11^(`u{m}Quh2Cp~KkqDG zo6o`DEihjd%qE5g?}yI1b)3^{M$3(BH5@{gCKD}!2faB{Klw3+V#SHThVh_2nd7wD z8jkY-a;Ot(p9|i2B9nwDCvHCONup&Gyfisz!C<%^ppHu}V|42B(xlv3p*oktE(78l zT4DGU1uAqqo3TZ#VjNxEl_)7R-lvu~^foc(D|S980ebBx1E^?5EIf7D1}a>R8^DY4 zV$Yja7toY8lLd@QAuSI38GjN#ohnmf%-_C@CW)hLBCJf$h`CZe9BYBnZoDV?42t1Iu5vM!(XU6T588I~z zXRX!aQYyhFlhpXrr8VEZw_sV4yhjEMC|0JsG078OS@MnY22@d0)WZ*O1TrbBr72%> zgg&Pyx0q20up_Av&lLK4Euac2U7EC9aB^HhEgM=vJc^z43td^+fdM3b;iMt&=h_jnxDf)#BI=4TQJk@N8L?`I+Bx^~pLMNiNU36mTv$$8eQ7xnfl`X@8b9dBak~ z5~Rg}6#^)`3NE;P&fo7l^9l^e$%3&ovtZWtTd_Al%b?kqsb;2j{J+gKr!NBFBj6m) zoUOoc{m6cYF`fx9p$zX_N%bFE`hWT&4n&ikq_^*T+EZ^SLB}%wHjSRS^vpQg#kP#yd^gd8`HKQv!+tepx~D{EDwpH4F`Ew)LuK&>@0ssn*E{}9UgRH8fHbhu-)Q15~{}QT7gD-RZ^3Wt&5M#)6i})KS#*%!p9|fdE zSeB9~n%khx&f1=|c?zC;`M0mTz$ZQ7NSB#eMgpap`|rCjpkQ1~5%6jBr8&9FL6*(& zKzMuihGPh!a_Vw2qav7ah56&Ln0)9Plv4@D)D)#+v8A`S_i;I|ys6Vv+o?U!(dEdTDx+uqj{<#6f{iphssvtM9zO2Pu3>rq2V##Q!2kdN diff --git a/examples/models/models_loading_m3d.png b/examples/models/models_loading_m3d.png index 2214b8e0152194319b3cb88f763d85df9019e5a0..9220419ad1c9124b887dc019f0ecdf776eac1f31 100644 GIT binary patch literal 25881 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 l%o%Vu0{0rQ56G!+dTcYHd8`V{|m|TrIG*u literal 23682 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 Date: Wed, 30 Aug 2023 17:39:44 -0300 Subject: [PATCH 0230/1350] Remove a duplicated screenshot and add missing one (#3275) --- examples/audio/audio_sound_multi.png | Bin 0 -> 15106 bytes .../core/core_2d_camera_smooth_pixelperfect.png | Bin 15832 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/audio/audio_sound_multi.png delete mode 100644 examples/core/core_2d_camera_smooth_pixelperfect.png diff --git a/examples/audio/audio_sound_multi.png b/examples/audio/audio_sound_multi.png new file mode 100644 index 0000000000000000000000000000000000000000..d60138d0782e24f41049582c0554db33e293f090 GIT binary patch literal 15106 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYU_8XZ1{4ubUVDOp!Rn-^i(^PdT=Jh^Utia^ z1sX^(Emp)qlyEREyBHwH+jvnx0xQ8-)p{U5u-U`G)Ii)2=qSoXt^+2 zE{v87qvgVAxqxl_bF_3EPxC0)NIeA)ku!@kEtZ{d@rpT~vFZd#-q)a! zYQ{T?4zDz3u3Pu6gTt@raO#fN$|64v{$IA98zA-W$gb(vzVU?oIQVF4$nT3+FI&sG zc~zQ3e7>7;*9v5IgEfm=x`SKracs#3B*t*pDWQZd#RNF!%^;}gkfBnzWSzQNdCSy0 zA7pZVwM9K!7;Yt(`Lp%P%!wC2rEIw+2_C(Fsc2xuY&47A%{jq@1sm}~jA^m4!s5aXUP5#}vo)pV-6gl*`xitbF=B z!wzDF4a5p5cOtCl;9xBJUNOOHlV3)Qz}=NsjOO2Qad^jJyz`;w`@Lo@Z^44ZJZ%?sRwkf&NpSg$yRV(}|4*>`?2uV? zXliTwD<9@vyAvNW3#*$LJLK71&N`OAWfH`@yo_H2z@`f0o5$#I zUJ<2fEM+fecP-XpJ+OI@bk*lXCVI;Ycj`ja%( zsNa+0Ue1sE==x@!K+@ypYd@Kv^&J#CKjoZ6Z`drbC#JL?c)@cbLknm<_9>SI(HsVK ze}8@bpHb0|T(_YnRt8&05}k{4TIPZ-i<>nhwP8z)5YOF$L>Z0=Do}Pb!6m0?@CV|A zP@K*jbpj{?M?+yW6ktuX(X=p{7Dm$oa#c2(BSv$?XpR`o5uncMXef+^0$MTvb4Tll z(K-TFIEYM(c>tI%2erK$M9H)@bQCS~`xF qj-#dHXz7S36A`S@(h*uZGRzZIR1w_UTm{_Q%HZkh=d#Wzp$P!(&(8<| literal 0 HcmV?d00001 diff --git a/examples/core/core_2d_camera_smooth_pixelperfect.png b/examples/core/core_2d_camera_smooth_pixelperfect.png deleted file mode 100644 index ba8d89b7c1e7d68f069a4c25488a2e0a8a698d22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15832 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 Date: Wed, 30 Aug 2023 17:40:24 -0300 Subject: [PATCH 0231/1350] Add examples/shaders/shaders_lightmap.c to Makefiles (#3276) --- examples/Makefile | 1 + examples/Makefile.Web | 1 + 2 files changed, 2 insertions(+) diff --git a/examples/Makefile b/examples/Makefile index 9d0b7341b..7028a9542 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -529,6 +529,7 @@ SHADERS = \ shaders/shaders_simple_mask \ shaders/shaders_spotlight \ shaders/shaders_hot_reloading \ + shaders/shaders_lightmap \ shaders/shaders_mesh_instancing \ shaders/shaders_multi_sample2d \ shaders/shaders_write_depth \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index e65351e07..4574e928e 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -504,6 +504,7 @@ SHADERS = \ shaders/shaders_simple_mask \ shaders/shaders_spotlight \ shaders/shaders_hot_reloading \ + shaders/shaders_lightmap \ shaders/shaders_mesh_instancing \ shaders/shaders_multi_sample2d \ shaders/shaders_write_depth \ From fefe8fcda96db357ab4b93490d0453f4b3058cc6 Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Wed, 30 Aug 2023 17:40:49 -0300 Subject: [PATCH 0232/1350] Fix examples/others/easings_testbed.c help instructions and small tweak (#3277) --- examples/others/easings_testbed.c | 22 +++++++++++----------- examples/others/easings_testbed.png | Bin 17338 -> 17443 bytes 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/others/easings_testbed.c b/examples/others/easings_testbed.c index 227efd231..45f0efd0b 100644 --- a/examples/others/easings_testbed.c +++ b/examples/others/easings_testbed.c @@ -108,7 +108,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [easings] example - easings testbed"); - Vector2 ballPosition = { 100.0f, 200.0f }; + Vector2 ballPosition = { 100.0f, 100.0f }; float t = 0.0f; // Current time (in any unit measure, but same unit as duration) float d = 300.0f; // Total time it should take to complete (duration) @@ -180,8 +180,8 @@ int main(void) // Movement computation if (!paused && ((boundedT && t < d) || !boundedT)) { - ballPosition.x = Easings[easingX].func(t, 100.0f, 700.0f - 100.0f, d); - ballPosition.y = Easings[easingY].func(t, 100.0f, 400.0f - 100.0f, d); + ballPosition.x = Easings[easingX].func(t, 100.0f, 700.0f - 170.0f, d); + ballPosition.y = Easings[easingY].func(t, 100.0f, 400.0f - 170.0f, d); t += 1.0f; } //---------------------------------------------------------------------------------- @@ -193,15 +193,15 @@ int main(void) ClearBackground(RAYWHITE); // Draw information text - DrawText(TextFormat("Easing x: %s", Easings[easingX].name), 0, FONT_SIZE*2, FONT_SIZE, LIGHTGRAY); - DrawText(TextFormat("Easing y: %s", Easings[easingY].name), 0, FONT_SIZE*3, FONT_SIZE, LIGHTGRAY); - DrawText(TextFormat("t (%c) = %.2f d = %.2f", (boundedT == true)? 'b' : 'u', t, d), 0, FONT_SIZE*4, FONT_SIZE, LIGHTGRAY); + DrawText(TextFormat("Easing x: %s", Easings[easingX].name), 20, FONT_SIZE, FONT_SIZE, LIGHTGRAY); + DrawText(TextFormat("Easing y: %s", Easings[easingY].name), 20, FONT_SIZE*2, FONT_SIZE, LIGHTGRAY); + DrawText(TextFormat("t (%c) = %.2f d = %.2f", (boundedT == true)? 'b' : 'u', t, d), 20, FONT_SIZE*3, FONT_SIZE, LIGHTGRAY); // Draw instructions text - DrawText("Use ENTER to play or pause movement, use SPACE to restart", 0, GetScreenHeight() - FONT_SIZE*2, FONT_SIZE, LIGHTGRAY); - DrawText("Use D and W or A and S keys to change duration", 0, GetScreenHeight() - FONT_SIZE*3, FONT_SIZE, LIGHTGRAY); - DrawText("Use LEFT or RIGHT keys to choose easing for the x axis", 0, GetScreenHeight() - FONT_SIZE*4, FONT_SIZE, LIGHTGRAY); - DrawText("Use UP or DOWN keys to choose easing for the y axis", 0, GetScreenHeight() - FONT_SIZE*5, FONT_SIZE, LIGHTGRAY); + DrawText("Use ENTER to play or pause movement, use SPACE to restart", 20, GetScreenHeight() - FONT_SIZE*2, FONT_SIZE, LIGHTGRAY); + DrawText("Use Q and W or A and S keys to change duration", 20, GetScreenHeight() - FONT_SIZE*3, FONT_SIZE, LIGHTGRAY); + DrawText("Use LEFT or RIGHT keys to choose easing for the x axis", 20, GetScreenHeight() - FONT_SIZE*4, FONT_SIZE, LIGHTGRAY); + DrawText("Use UP or DOWN keys to choose easing for the y axis", 20, GetScreenHeight() - FONT_SIZE*5, FONT_SIZE, LIGHTGRAY); // Draw ball DrawCircleV(ballPosition, 16.0f, MAROON); @@ -226,4 +226,4 @@ static float NoEase(float t, float b, float c, float d) d += burn; return b; -} \ No newline at end of file +} diff --git a/examples/others/easings_testbed.png b/examples/others/easings_testbed.png index fb0538531057f5e78e25186ba6023d4956f1db0c..05f63acdda92d4497df752a7768e01c7fbcb781e 100644 GIT binary patch literal 17443 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 TZV3GO2gKT9xA`qoeCWRc4{Unk literal 17338 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 From 3d0d54e0708a71ad40bec3f312ebf26d5fc2a28f Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Wed, 30 Aug 2023 17:42:01 -0300 Subject: [PATCH 0233/1350] Fix examples/shaders/shaders_texture_outline.c help instructions (#3278) --- examples/shaders/shaders_texture_outline.c | 18 +++++++++--------- examples/shaders/shaders_texture_outline.png | Bin 264137 -> 264739 bytes 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/examples/shaders/shaders_texture_outline.c b/examples/shaders/shaders_texture_outline.c index a28ab80e4..09f625bb5 100644 --- a/examples/shaders/shaders_texture_outline.c +++ b/examples/shaders/shaders_texture_outline.c @@ -37,18 +37,18 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [shaders] example - Apply an outline to a texture"); Texture2D texture = LoadTexture("resources/fudesumi.png"); - + Shader shdrOutline = LoadShader(0, TextFormat("resources/shaders/glsl%i/outline.fs", GLSL_VERSION)); float outlineSize = 2.0f; - float outlineColor[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; // Normalized RED color + float outlineColor[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; // Normalized RED color float textureSize[2] = { (float)texture.width, (float)texture.height }; - + // Get shader locations int outlineSizeLoc = GetShaderLocation(shdrOutline, "outlineSize"); int outlineColorLoc = GetShaderLocation(shdrOutline, "outlineColor"); int textureSizeLoc = GetShaderLocation(shdrOutline, "textureSize"); - + // Set shader values (they can be changed later) SetShaderValue(shdrOutline, outlineSizeLoc, &outlineSize, SHADER_UNIFORM_FLOAT); SetShaderValue(shdrOutline, outlineColorLoc, outlineColor, SHADER_UNIFORM_VEC4); @@ -64,7 +64,7 @@ int main(void) //---------------------------------------------------------------------------------- outlineSize += GetMouseWheelMove(); if (outlineSize < 1.0f) outlineSize = 1.0f; - + SetShaderValue(shdrOutline, outlineSizeLoc, &outlineSize, SHADER_UNIFORM_FLOAT); //---------------------------------------------------------------------------------- @@ -75,13 +75,13 @@ int main(void) ClearBackground(RAYWHITE); BeginShaderMode(shdrOutline); - + DrawTexture(texture, GetScreenWidth()/2 - texture.width/2, -30, WHITE); - + EndShaderMode(); DrawText("Shader-based\ntexture\noutline", 10, 10, 20, GRAY); - + DrawText("Scroll mouse wheel to\nchange outline size", 10, 72, 20, GRAY); DrawText(TextFormat("Outline size: %i px", (int)outlineSize), 10, 120, 20, MAROON); DrawFPS(710, 10); @@ -99,4 +99,4 @@ int main(void) //-------------------------------------------------------------------------------------- return 0; -} \ No newline at end of file +} diff --git a/examples/shaders/shaders_texture_outline.png b/examples/shaders/shaders_texture_outline.png index 85c69b1808f756ca6969b53669d850293c53e799..badd388a5167830275b20fb07e75c95b449edb11 100644 GIT binary patch delta 240586 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

Mzx#iqzyz}QX7=)GsKx|Z5g8VIlc0YU`9vj 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�!-_I*1<4gQLPta_^w6XxqF8VMb!>JM)SF+D}a*Vk~f^EGN()uKr8z%sGp)l|qyNubo6CmkAq?Ty( zRp!P$_%Dxg8i?9rm5Z8MafXs8Pr#IB%kh%WO*ntBo)f~kYCeKC)BxW z(CVJl*ACQtah?O@lZx$qCp&t7c|qD{ZTcl23bXU3wdixCRrCRzz@E%=cJLS1Iwo!e z0`K!DjmhC0z~9nU55Js#=Z=o(_R-U#w{E;6SH8fpT0Gs;v@qWOhg9FHI~0qH86=!& zPclDq6PmabhZ8i*irndmPvPz!F#<|4sGR?fuK5&|NBZ;C7IJ%82QU-7r>Uq(oi3t` zTr2^qy(L7Mj#wJKxkv#mbyRRa;SgKj{|I8vXBpet%YLJ3?uX5cm`9f?h9^uc{655K z82>BYHdOnin%bzDx^?(YfGK5e*$q;f15uWO>V{>JHZGHl(738MPWvcA-KS0W#O)$CeO;UlimyzDP&tF zQA%`sP}~gDg90U_rhRXlo{{I~djQ|bfo!Dv$Law#3xq+Mf;xcc!)&F zn-K&{utyna%jJx(fnvnD{mZqN2*p%855<27Mqn|sQxaMK6pZAwl0$Gfj~0of#PNR? z!SH!lLHOv<8aQH(x4($`WF$N)pkr}HNi=j;0dM4_zNBc z9XuE*>ct(T`=y-+Jo5J%BY@+bBsr3sY7B$t&oLGgvR=F_8)NoCMYugeDEl`3DQvIV z2d9sjnyd!QizkW9j|;@aE9qWlo)xQ?k>UFkVklP5MS!TX@%;QYjrC6hV)6 z5O^-r+qP|kUEYL;M{p=QMu3@GZFL%-J)KeE1Lm>c|%LrV^sx0N3fjiv}usRAx08 zUQuek8@{eCQMn7?cD$>k0zV7C&ueh@_NQ<%5eGz?1X()XRh`x88`MS+edB(kA2in%~^s-zodV$n;s`rpGN%Z z(-Pjn0Fna&Z_kj_0P4)LbcQOL5xP6fmrz8(c?>i(B0Rny*!!tgPC=ti`0ZOOVsvDp zopO*SC zGY|5Q+V#TW2gKgr5b`I3?aErjmuj^mQCgA&_49a(;3{YdX@Bj97Ft@c!qYpr!_@&d zftE*VRY`ZTW!Iovybwg0z}uDc5;gBj+<>uWg7XI3z7a4cd;|gkCQ6RlR1&v5j}GK6 zqips{x68}P%i}_S;a+L+3DV|RR2L4Vh={m&n4NA5nZ<_~@>Ct%14fD`c4>-AfD&`HUO z!YL*t-k64|!&+GxgWh6W>$okEv`(+CKS#z^?Mz}0d_+bmKu=Vu>8?*~cMJee7aeno|s1~IxT)2a^~yhTGoW!vQZP1Trr&3&z|INtXB`|tmYoLrQnKY8*b zge2*JZDFe8a~<8ti7F#F0Fr#Tbeou)h*)Z;TzG$%twliJKnhEf>vjz>9nQ; zV0hH2;m)2tEtK51hrsK+;^ysO7PBmpWvSGjO#JdRkHSZUtbdqelFDFvG@nNp1gx!FwYD}sFc`FUdQedJq?WjS}Y2(0jV*B1L=J(nP( z3J}Lp4l)ry*D(Mpda5ANoKGeP8rak`00y%zP9?^RvZgFgTqT6Jz zX(Q6@jz{c}9rvz@7B?$X9YDXwN1Ay7A@F@4NZ%WxvqUqt+)c(R+y@sCHEO~~{(M-b zb2kixc_k8iU@xhH_Yj%2x%>`C6J7AT7Tx0lxb$pw$x*^D5!1}GGK@>}a9(rn`X@+a z+5q(9n5do9hi5srF0WuCJN`T{1U9e6))=s`ZW?9(tb-|XJlT}u>nQYyb7TM{TvbBG zdEwlLC1R@Hip4?^vgJ>cc(=tx>7FW`x6JR&XN8rr4#Gc1%>>f&v7X{P?=y-(@~0m(4V*$MhLnbHB)gTO$3>0e=2M`B6;l^@rb^yFY{uIZjDo? zeL;oPF@7GFvLpW>)V!X{V=Kt+5$bK>?N7C4=P#;fs}r(4Q*F2WeFZVvJcJ2(fHR!x zAMy`CJR`GrG&{u3g6$k9i*SY$z4<|#O>wkD_v1vE?^Jqb*hh z)xQP^s_*W?GCir$4%FyFRE@Mp(kZA(+^Yt<|2@3t+S&#XWmtz%@3bK)@xJa)rJ`!cGdPRC$#&pn^)R7*^Oqb&{C8pJL1+m})4&wsq#ZIZnu>RLaA3CZqM z7`aHdun@?M_mL+YSs2e-GsHE@doG$lYp`Drp*-UvWLVu>7%8#^9}&MEKD>ZNGsKNw zLc!%&qd2rLF5apsI(iGz>Vg)+C~b+*YyUR{#fYw$F6YOLcox)A96>~nB^bNw6H2`? z?Wd_%jZ-x&En$Vy$nik-7}^Jw1%y<{&{n*rZ8L^Hkc%w~ZmX%u=x^?XTnI!hJ4}{z z#nuEJyY`g+H|M26-?|4>=LgR)i~J5ROrdLZZEbJd(`oOPcASkWJ^bHQuU{Rd8z}jC z#H!IVjOn8Gflh=nok#Svj->m2SK;EJTH<<9lXwm2$7&}md?O`tEkc^@@o6_>osb$x$s0pyUkEg9AT;TuTLED#*7D}}8y0!wP) z_lS;I9iTO7JU5gaDY}YpKPIl$H$QE%4Uhf;s^R!3!;YjXF+58_tBrxbHnI=ygK_o+ zXv7HvuJc6bSE!A*LBCmBR}ZLL`lW`PEiEAc9nh+z?8%J2*xxn&5|%%U7UoLP=;%do zb-Wej;<8zEp9{w3{)kg=#Kf^7_ANzl>mp%24&u6A`|_UR4@!GP)*_-dqvvrfcYRr<>!sPL8txAlse2Ki0KOOQz89wp_ zLYiO7{@;A(kz*!6!J-*+L5lG`J}pgwm2$zXNFL(QB&}Vhuw|ZCR9F`gy5ah(5dA`1 zqkEoD_tme2bA1+MZMC0blMS2SqOCqXHudDQl=xFiwxYz@m#~beKG6o283vWNZ4*Hk zlO+S`h)J9ertKEnMmrrc_Qp@NfkYI*VRZVnVJ4{6uFI&iYT=6+r8h95oqb0M7i2Yj zL?R{-ay}wlMk^~v0^#NUI_foy$L#&bk7arij9QAKCv|WEN&!VjQtx|iB^bGWhVx@z znhgyHA?Gxv%+>`wFl|2kEb)%a#@Nwp`=+AM7FFDpk&*WUC!qh^=Oso+B;5w2*dPD8 zE&UT#JZwdnrb4qlv57yea?R|ZRSwd82~oZu6Mjw^ zl9s+#f>J#vCVtgLo)o>g`u=>5cAbPqJN~`)^J|>%gsY!N0dem+;3tr(7db7bcs7Fd zEJ5+(sL(#J1E#ugZ?j+Ka!0b}dQEp;oZbPYwQ#~n|Lq}ZKZP$oJOa74Z!9G&-`3DnwSRf|EthKiB!gp-kOU@5q@RO7@x@XlpN;kN{ENp+fLYe;EohKL*#-2ILr$_#$iUit{ZdcM1be+=?@ z0f*sO>!Kpmcs^_4mlC>wvfj`T`5FJ~%Do%bthYB460GOfFL=XxUcAmt+p*-tPw>)K zYTOzX1$-2ZI`yXh9j?dI7@3WWb+WcS$8EZ zwMva8+H2;eJp*wreh_ak>Btez!_-rqb@V`A1R=^nZ?)GNz}iO0>?4TY%{oTw36(UBZ7zsO#I@^+*a>?%e>vp=v{AQj0G(}l60pSs z0|nS~N$|HF+Rd1r$A|$6azWct0ex80S^-A=i9gYCqF^^5j*w7ur>WY5E^Lu~wT}={`g!?KEXyt;K@( zYqYt5ig~mm&HsYwIO;bK%`92XSIVX4@MI+$k2!8xfbYOKyHlg~Ov=)P+8p5Kr*A3W zyxUCTOtFcmm|`kIsSU$RSTfKk@0f^LMtKF!AXy7*=d}^M$?9}9lvRFH(_2&!t(Cik z1`)+8v|GDqmY2n7T*@;bKdZo}aOQ4MZ@uEE&d0;jQWhL2otBF=`iW}!#qOD%9oGjT z4Q%+*!sP>Cl02*h%nrU^u$f#S62VXM_5j3u|H! z#!p@LBq{jamd=boX>KQ!99>#In<%Yjbv4bih6II6#9$|Mth>IpfTAw-jU?-_`P`SM zq}5kX9@MAe`9IepSnZEeZ)@mH6zKU}UB2ME9WV%y%Z+hOOgwB!)MhE-7hseWk8vC-5%Lvw*zy`i_T zbod2zX6L7;ePUiibFrCS?9`<@u^%)OwW@&B!sRB_F@3 z-lC{@@RqVOfhRgA*&~#>OmyGzLq_DVMxZJ0zT-Pw?ML9T5`ue!KyXmX&fb$`5h-Q^SF(7Ir1fsuiEwHP-xp(;hia{78#xeWEU>vA zdNqiyFGyQ?=^=dMF$IX!%a6r(dZTRClSbSHZ{358?1SW#g@NwZR4`2tN3{{u-192t zn+5i-x zHta$hPwz)Y)@xH_gnAxuR;5mZwD>&Ua6=MxE{J`L$_Y&q|Hk(m#^q4;B(f`5b2Dl+ zGY^f)*`NHYqa8?~dvd6Q69S38;cH$zoCGCqt9-TJXDA&gw|B+Z3F&94*jB2#?OP`1 zVzr7c%58cLt}VXNB-=g;Y1fVFT9%PISXowcGxAMR~0Iz3GGb_g}U)aj&zXXV#GLyUDD$A?UN#Ctq4eNdtVoh#|aWYxCpK)G}!{FAp| zd{b}8A#+PeeQ%hkNh*r50)&Dml?gtHZ2`QT=xs`L26aMtAB^89Ncd|NQWRvBy~Y9x-j`>BWTBNg0PL6H`cbs4-yHcRIvBhDJ^P(<`WK&f?<~=-VmGxCs+SBj5$TL) z&x+&I&vIP1>__*m-1+M+Ty$b)J@X+VEY(OC$R742rKfMM;o{p!nszZLt1I&7Y{+2< z_HyCVhi^f}$39UX*3F^itQ8zT%scXEvJZ0P-i&vBg=(=pawOAqtm{lFva}87ujo%E zQ@DldBe#e0g!!>Fmo5;OBUf(RWTI|jwpH@v_p`s9T^{`N9s+;7__u0sI^}P<2H}yM z_45--Tz5}(?kWdOND_pxYP*Zgz3edm(19fnO7i`70t;=9W&lU z$4zK}tYu{nvFwfzU!)_OIW;eRDwwN!*_*BMopXz^>!LI=NUE%wg6GxSF^(t|3|0e( zjS2N&AKU5#SM^y@z}6zYgSKwcp|;lvW8dD zxXnsroQ`mfh2W2G_(^*|Yw@xM*OXb6L=gFI6?jTnf2O(s&E0m0ljG>#(Sr^t#(!;< z9i8T=F7jH&dC5~?6^ZZdF={~cj(E0+N;qNQX-!duMl?qQr{EVm zmcHF=tI;(~%jz=fLB`RSnuwsxaBz^?#{2fCmScsVaNs*KG!P%*MSb}^w4+R>tj7%i zI5Tt$(%xdmQ{!!J?clhSs;iH~IgrjS;q~GfysYVbntxX!kdACe7*me zKcKzx_lIx&=)WI6dDLVt->2+WP7A&7b3KgbzgPeI{^1{gki$4K?MA}&ULnFuPO2xQ zV4NkBYf;oMapHEos+dfi*4 zXWVyS%zsu(^$u5~D!Wnb&*=W~**4e{dsdf-uVC32M;y9zKW6m6auB91aQQ;yL9mnK z-S^TXKOs1m7YmbzwILJWFhXwOB|45gkriBYEa0A=u@w2%M)i>ml84wD*huCuI)-4( zJg(9RJ=g*o+@9rA>K%w6Ztyvr$1IVRZCr4AWol4AacOQ6?8V~H)>jZcDTvaG3as6? zNJWEp?mv>U!|qn+j}hA0fKowkXxlpLv5d5;)lqvyhFy|?%sEcH+9vw;GZci&qFr~LM#+8SJbg<=9iP(;F;AO?FB!*{GdjoLg zSVjFk+H)v<4^70RmAsNt0OC|8-th6F6u;En-JRXvHFK1}bG?*M209RuiYzx=fd$b# zLB`q%>){$PGio8sl_SsZ^7!y!fH2HaQH>usbkO-S^2HI&@O#Du++x4sI0&}`uO6+2eGz?BQ28!J&I&XCoVs{K&SQO7ljX3}K1JBp zIG*jSeUa%GZ1zkMs`n0a`ja%yDb_;hKqQOK#R*iS-O&og>n?-JS2~U{t+h^eRD$jL zC+cMYEq@V~#THy!kOz6lWFGg{Ne=ML1i8Xcj%4F6E$5N?unx1$mcnZ#JiT3ZJw3p- z&IhL%n#+s%1#3=;{up7IuTCZK_hVQ~GhpR@afG@bhj$U4=k`HnuxlG?>4#%*i50^b z07IQ-jBB2qaf5Y#M+Qm}co}knXH^72tQ@bU-ImxJXnnR{9?gKHg1I(eLks~_hKFE} z4CA9+fx?-et)~)OvR;}LdROK4w|?y=Y>khBqiOSSQ$6X9#x1dMqXtg&;DlhOJ)=ru zB&^ac4-j^27T$!k-LX2mx9@g#;C<)2>(|Bb5SR5UH@k(+A*m-kg%E>E>!=VJ==p6|+Tu;}> zmfn*L|MlhNq$8;^ZUxCFZoAem=pTd!uT65?Q0P#_3$A2NJIA$nU}z|iCE?{zN0>B! z^{T@x+ZG~^)aRu(;W!$(fS8UB;wF))QEDN5&n_T`4l)}CAV=#>q`SIK;LK!OBO0jN zjPq;|#(z*?JjyB0TvJo+w4*xfy^hPX!<>l~OzshGv&h7hb+(+6Z5x$=Y?sGqQQ=yG zL+2GD%jWijax<_$?1u50?7Qac*4_5eRXB08up&gG=A_W<&HJ_g%8AS|iAVq0(mnVA zUS}W|Mbkhvs2KU1?B3%KL_c!fGIk4_$YnpyzHL#111vtkuXXQn#yun5k1AJglMGi~ zLz!+oDv)&HeY5A?rx64=E8XA~1*W7jV@RWYmq3t;xB5U!-XAFXc2-QLM`7Z-{}PgA zoNip_8s!xsL*K?N56N)vKSMdWW^15>U#SG|w`i2% z1<QpdCYAz!rjJN)Y$ zHS>??AKK5@Zn3DZ2T&kdZgXz7^(!76Z#9sXNyaF*voIn;zW;mHjo zQGKQ{W!IKxa~j~I16Amt6I39d7S=BV!U(5iQxv(kA{xz?{ze8Z=BFfwI2JM3zba^c zdI2yh&uFnmm85G{(nHC$9GO!VLNm}SAnWTWJL5vWiwSNV7lA8sQsR}&SW+x*VrE{- zvuZ}NZ2G5_019=4;sC087_S*>Kyc8L;wxUcpw=^0c6!iK6t>e#Sa?eUU3jKeBFAQp zf&$iSD=Pz}WHDy>=&_!iyLKatuf#}B+-Cgsn4SJ^1>g2!8Q<#JXi|9W_4(@dc8Rj( z%UMV>=~-g1 z)x#1{T28;uxvs>x)R9|(hv$k4P;)%?tyk5SGu$_^D~YEFFJ2rJf~lCxw+Epeun;lk zE{#n0<%|s=b48KPVBM!|{o2Z7&wnWcrqn+^-*cM(Ec0&YAZ%1;g9G%qXZ&>0(B_eX zL+cL3VV-1!ethq<(>^Y{UwY6Hcj}Mr!t*h2gaJmpK$`+xL`Xu32`$A1n^U?(jL9}E zxIBT@kbO9V(VY=aJ!GxJfri=z=qd%mefIT%Gwg#E~O)5?+C|h~t@H zmGTay^;EjINrdldT=}gMo@C5|t|_t{5sdIDU8kQAMgGm{N~ED*Z1b=TTda3r7;mNs1zw)?7Cq# z@c+b_?!WFM#NgENqW1qC1!9Y_xaGwD&krXFW<9}#@9^tGL!6Se``~~xW^h(_9cK(g zzD6;i{(diK?RBJlM zVcIuKl2wcmK>xff(b7`j$5$d`DVWmkjjK*s$U> zx@^ml%QPicp^*x+=C}8VB6i-4D%_{+&Xo_tjF-rW7cRo?!>{|`& zs{;X8P4Z(90Csqz#K+(ZBzxxZ7NcbH9gS?UutJ#^;K_>yt30T&P-?`wOH3$oHf2;{ z3O@dt{&AAN;~U;77RWET*`?b4M7-}wWSJA%L5jp8h@O=Y*%a$Sg0sy&0&i&>wP9kF z`#%M)=1tH^ER7phO3V++Lm0nTKv~gg6jYI|BRxlL8+XBx^+~zlBevHOS(>HPA;g~= z#eMZdNeLgi<#FfC#~hi8)_%aHIsSSMpuj0EJl%6NeU@~^+25)x)lV)=KlQX<-Rq&K zq~O;x3H<|)QwW<-c7zakKfjdIAi;H=S_A?|eGDlSkcr3O-0Sxcjx|Aabw0|n{J?=f zwI3cp8lqZM*@27@l&wDRTZjSdDh+;Bf1wM4B)V#lHsvbeA8qxS6q|yN_+Oi~w1`FM zcW;;?;c48lV}IZ}4AyL@{PVy~Bhsvv9iUp))FFJiuCi!-OZq&CGy*U@daWCyH?!##1#I(8|1E};R2DpI74NqE+Ex764R(8=`N6dgrx+CvzUb;Qn@3q~ zaYXhWz4PP=Kvx`+;HMz(;XaG-5hU%o{mWRWZ6{lGi1$aQFhkz?a03@v?4#C*k8q$S z4@2S%cK+}i0#7~?{J7zr+Kj+{$8ZP{5Wqz}unlWPQPP_;arL5d{NGL|Sv8Ohmf|q}zU)mK`gkM00-+o$^Y0l-`MxdpO z4$Z2wehfa;g0y#xqJomNS22-e)bFSupIo%9c3ANT`2NA*)Nco2S5krQv_{7iCg(Et zL_0TdGu4;3q6mj*73Eh;Ih>fcH8}-;AZ(aW8VZ0Po~elat?5>_%?-)VmhQQPh}`#$ zO8zD0AVruM(%UI13>kEMTx z^d;)AoZPpM5H>rTulMP{L21^%_r>r3ynK*EP7ti8pZ>a>q{xnnW@uqU#IV^hj!MiilN5h%_8~N5U1%WTcwBN z+y&siR2BJn%XF?=J?d&%3Cij^*Yz+ed3Om??JDn^6R<= zFSH@P?rfHrXlIXMb41g)GRY-9X2yP>>u7HN%}q6K9cE_EBdtW9L#qAD$*RCItV6A` z^JT(^HOs;kP2LZ(J09Qjik=W`8-(l~o^6F{17cLd$y2m^aK59MYe}5 zYqO68EV5}=g(?ux{EGHgC&@w2%qZxTYv25Z{ohehqhR`l;~!YosBsb6t835j`F4b& zBTAwib!v(k%IW|&Fu5{j;T=NKF_QUkWOm#ZfKqZ28}cemO~i|Ja)tkcVIp5`2n}cn zYdRL@X9ra_glWI55??S3<<9>$6li#}AEs^Gw1Ew!8*s+J>=idkm_n$1Psy`G8s#mb zvX9IA{mtPNZLQKG@a^~nZO8Z?bf(P0%1`~C|MBJz{-0I{&FD)+FmGWMBY7(%UVuBq zUfO?E!?A_#cM9n}iW`Nckj74b{+Swd+ZNQJ8@(iPr}xSyIzsX)X7Pd6R1F!(%PTJN zipmo(;x_<`N3gob8mFViSCD!;1#ueg9$h6-j$Aa8th}LwV-qgCqOh<4P-d@Wd#?P> zsP3)4g*5Nf)6;uf6g;>d=SbI2lFUSSqbBXHNl23a-y7^7+RFdE&e$L1<|WnNJ4lYP z3Le}yg%}!kjWD*S_Dab2EwQnQcEWQ@Ya@A}THapx6)>rdpW-sc5u(Vh|W)HLWP zm#a%O;recf5rp|b4@9KYQV_Olie$Q);g5HzFFD*4F4povwM!I<#8dgjseewcPWJC8 zR=eF@iFRWw$~f#({_w*mA#xMFM0S$>&@e53U=8#~Y-4T|T_3{|zQ3>520ZU`+d48g zQrIB}#4E#EW!&G`vSrEe23)g)yRK@-1>f71q75~U&XG6`LoDdfR1d&1y?58f!!p15 z@DYMwqi2usk57CU1ceoftl14^@qrH1Rc`k6I>hm5v ziBSf73Z40OfIdtwi}-EXk+&7v3S!=qphK~AgxZfSZ%a_?*oC~m*mUOv>$m|cD5wFw~cL^?swU3g-q^RZ>q&yx}E}^IY*nB9V3ER`K<qesJg|{HSH@n(WIbPlY2CvX)wvu3 zl$Khc^rJ4Fh(lW?_u-Az_U}UhiY6cbog(tOS|uP~(Spe;etsH4neW8E31BAki!77n zl_`bnu}VbB;>=W}-(g9<&M|UXpVN+`(lLCswAt_H;9^eQ&*uZXDuCEOtuHJ%rkdtHE5R|63_yKy#CdnC$!XoP!;eo%(V<{ZFB z0gkiP5AWa84?hdDuSw2&3Ci&QBT?}i=I2=y^$z|`8adN+8>hq})Qe9~#kEQ$L$|K| z*X2^*N}twDeIIW3&!$LQhw1xH5vOL5lcEoJ1YCV4!8vi1u)NF{ z)*EUu!`01g+Unk7H6!qb75(1_{Dmq)bgPsEg&l|m&@vM1)t2fVg7`@W!sugs#9^w; zna27lZ=qiRt%MBmq<_L12*$}}$LY0>9ls0(_s?q+MpI+ZxiJ`zcUUu(SNo?HXQC5r zVZW0g+HjlhlF0N{zzIeym(R&CffF@s#g8>VZO^@^t-`%yIsMB{FpWd|uOEwmYGqHuN5l|m$%aL{$5v5b=io~?uk&O_6XQW>6QDV#kNNHZ17bbNK}7=%h0s;W97*|N%GC%eoQ8&*+9B_2UR z4emHQkGZ-3Jtj!~^?{Ii7wF=3nvRp;{2JlM|B-ZD*rYq@_;}STJm>yA$0We|sS02l zbT1HdH4qWX)DoqlZb(2&J_A5MMGDubIZ`5fIdL39ZEg$T z@yEz~Wd|N$CwEuD6&iC|c6cj3t%Ts!x=1*!b4z%%m6r81L;K_tlJ;+_ zW)g``Sr4{+SD&6YoI&N3V@|ZkzmaQSKpZoWAhMisxDFgMm1L`iMuXVBVCoI;Dmgq= zOMQZ;Q00J_^JLGSgKw1rPD>!ywHlS9h|M&WlS5oxMyK4s8Xh_~`(24;U&uGkE(ST#38r0MWaWU7A9-@g}E^mJ=h$bl_K>JcaAxq!u3OY28M z)=C(nFciMY9Y3tWG?r-m%5&brfFuGKX||1aa)zkvi1_g3)%f(B;^nu>B2b_sK|dDOU>abgXT_fU+C4mN3Uzp~^8+dJS1;=O2N72d!6%8EEC7;#%&zJD$+9dDyb93YH zU5@zyx~FyE%?8aPO_pQ433EN>o}Y&3;`yM3n$s!L1d=nuB%ykF7cXtv7%&q z8OCCSmX%%&Q7}BeP|m!uh;&q-i4nrq=mH(d@{yDb$>RHDKuPqH{hfl1X*O^ynv(Vi znOXc4o%jXzJ3`BP?xS{{=5n8!e9|4QZoWz81U=r>1oev8ej5XhG z8G-y$f-vps5uqKc#41zuiE#HI=|2&hz7=JbNsX~>{3h(WY1pO(CA_YLe)H1TyR^RBjX{?94+b}3<9g(!2nv6 zTMRC@8Plta3VsxSOC9-IBK0ax(5eFOowbe9q71FGrEF*fXRmMMJG6Ha6Z+KFbnuP* zhO$@`J6V<1pfx+7VqAm=9F@WTdiX-St2R@Wjr zZ^#_EjO*-+_sHfZAs3WNFD;9qj(wXYWTf^BE*)r$CR3B6JM^xmCOX_3M*4BzsPiAG zF~B5oTTwSBSa95*msSg_EpqyQPkgma1au7c;LbyC>5P7|XnL~?h(HiE^nkB3n^46$ zJ*QSCxuPagd^ATWB=pf9UPsme0;7T@&w9f|6|$uQ-*fV}WZBu}pI?j#JX>>mi3~j& zJgQ-~BMiD=Ut!5`D}|5j-MI=-03&9z&ow`mo5|#pSZceZ*+X{9pK=d7FEg$ zG)(EQ$vRn0+qcz{q<(!QgE&d@kaR9mo@_!)Ob%58K0!9|@<6=i|t1vw$kS1h{DvyXVH zqn%u3fx+n1heMOSK1{2<0qh))T8YYTG-D2G+`3_Bv}SmE1N0}}bgSwNFL6C16kQqm?T z?vu#R?s!8})VP+s@&GoEISua5)cheg|h~YqeUG;mddnAx*7b z$K^6FXAQk!*s6$vo?dGxRY!W_8j|~~z@x`=-nV3>7 zJ)8hTH`Y{mRka`u*DeVrk46V##H*B;D*Oh;49=1fH4XsV9MJ{+vYlU$ufAz~ULmlyq6fG51t0yAlzIQV6H`d{{S4k31dmoZMT< zY8G9g5m;6dCXffUL#(92x#`O};;|)PTr}d77EIiM6r) z`N@}@tmZybR#`WNJ%O8=i}Jf&MOUghAS_IH0zFYiGjLMoXk4a7SXimg;n$J6P97&v z=)(PoFa%>)Ms~KHQ=s#jnrZTH5;aF#54#2DenF+Zb4i38i4v~Lh|KIn*Ndybj@P~G zI==M;+om*>lVdJu83)w&?PC0-?Ep?QXSu&7nt7Z1fnzQr^)BtX=z8Crxqe8~JrBaT zXZt9EF7iLKQ#m5%JvsILy8rHY2pg=l|1&vJB7-d49yZwv>U!*yb=88BmeghZ{bXn@e$z0twJ45*(QuU{5>C;E$@{1`lt?}-#mWHB`JUT zvF~9aA(1#E*+|@Bs7B3fKqgE>cmA+9FxY>Y^oFm6y9#$5jTfdpqR#fdT1uc@%;V}f zG)wicKt|UuLH&&MgbAAuHzKy2`qAc%>!^$014e^3D4?waPYczx#}{FhtL1REp(w5a zE1Y_=575|b4GzN;YA}Hp;KQqTEO;+_mo^u}4rEe607F@{&pc4baG;MS@U`%Zc&iGh2`3+%7QXr74JoSbg7{8D6wQx={G5k+b!gi*iI{=gCaK@C zmM>nh@e)O7t36GPJGr4o_?CF^_-cT$czY2fGdARj4RH|H!OfzTImbjTxM&_R#^`~t zmY>}!SWQJ*EGBG9=iB2hpDuZ3!k7v(JB)B^F+)xql1D{u?FH$m>{s)R2;28kW{<#l z&3rR1Gj{L6y@9Od5y=`0;U3k3DcOmg@fxdIeEDn*R@BA6GHtkW%opP=-BE;-U&iUy z(GzC#AL<$}B82i~a8@73rC2-a0c66p-Kg!jsjZ^=Bul|_OF2CDpoV<45N1a8@jQ1HScW$O#bE{s1_IhFYv4&e6@x_&%-<&|(>WOhsKy`&8 z%Z&%VgbL!_AD0@(3_`{n*U?2=F+W-}@-6TJgCb3# zXgOLKzy*V3U;W037&V?85&B3G{)Pg@LJKV8uLQGYKaUiXNCaLk5NP)C<1?poto_@5r5$q z$U||dRek&wi60WCRL~l2QKe1T`XrPOJEF(JO%Gp?EIyA}OWdL%9bRZmLF?O6(8Rxy zN@n*P+`QQ7c^eU-M0}2y-lkv3m|Z#}2?o(SY!h`aTs7%AyTa*LDN36H)=xPky(U|U zbag8X*;87Y%_f4uWFcZ^^C~I zLS$y{-k{KaIQ{x9U!ZM6&0{tS9vw*%Yd3wQhTzXj7L+`=^Q4-&vS>kT*Mull1xDDh`-bsY8G`g!4Do_-CIe zu*V48s>8aTaql#AB?8^(X5s!sdek39D7AZFPos^06DUPhOQcb>MDJr7*$y2J(JHyA=wS*3;iFENK(2U7Q*9(uaEvUYb5 zNR&BJ5iCz=ld-7S7Abo6Xbes?8LN*-J!*8UvVnf`$vdI{|EfhAF^@EcrZ;vp<1ga+ z`yhFW7H@w!TmmW_(~z?>Zm1Ps;P(bu>Sx9L@hSPWF-JcN)LhJ{IY!RHLX-l!Ko{2~ zl=aw$&FrTK1}2Q>1hNgLeA!wvO@!{-M9)AhGj+tAC=^M{ZIKvDXk*AS z+0Kl~Qi-YV4OyD57InL&a!Vp3g|ba?i$WuctP`>`zfbpm@9+2h{R2I!IdkUodB0z; z=QExW8!v+m_s_nx3~TjaGbek5+m?R%v^fiFvN8c*@e~u~DpT*fL)jlW5O0e_Y{Vn$ z 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

$ueU;V`e22L2u2kzab_M1 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ⅇuDal7kq+3z9 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 S_5}!ht#|iYb7wU>@&5r6549%% delta 240155 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

Vna_0JF@gcVt_T)oBB(b@^c-!AE=l4Rk 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 a27o_@|3UK1k7EGz2X*Oe{M(h4{Qm%Ab|a1e From 927a030bbf419f518374449609a0f85ecf161596 Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Wed, 30 Aug 2023 17:43:19 -0300 Subject: [PATCH 0234/1350] Fix examples/shapes/shapes_collision_area.c help instructions (#3279) --- examples/shapes/shapes_collision_area.c | 5 ++++- examples/shapes/shapes_collision_area.png | Bin 15319 -> 15595 bytes 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/shapes/shapes_collision_area.c b/examples/shapes/shapes_collision_area.c index 34d04811d..752d4e79a 100644 --- a/examples/shapes/shapes_collision_area.c +++ b/examples/shapes/shapes_collision_area.c @@ -99,6 +99,9 @@ int main(void) DrawText(TextFormat("Collision Area: %i", (int)boxCollision.width*(int)boxCollision.height), GetScreenWidth()/2 - 100, screenUpperLimit + 10, 20, BLACK); } + // Draw help instructions + DrawText("Press SPACE to PAUSE/RESUME", 20, screenHeight - 35, 20, LIGHTGRAY); + DrawFPS(10, 10); EndDrawing(); @@ -111,4 +114,4 @@ int main(void) //---------------------------------------------------------- return 0; -} \ No newline at end of file +} diff --git a/examples/shapes/shapes_collision_area.png b/examples/shapes/shapes_collision_area.png index 760e1bc1d94f9f11f21993fa5193a9ec8850ba2e..049e6fb00213631953a26d5457e9768e64f77a77 100644 GIT binary patch literal 15595 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYU_8XZ1{4ubUVDOp!Df@Ei(^PdTyo8_%zyI} zMS9u}WN=_77-XFqGEJtqJ13a1xM3w4;uH;TF-rPoFIF^=V!}r7ES8;c(FKPjP{SLi zgc7zClfZsl&bXk<;%2R|xHA{08}cA-$iZekL=5Nz6WmVt193tqm7UNa!W4Af->Bh3 z#sWm}UQh%3Ml&0aZw@%E@eNvP-x2g{&1~j1eIZ|4c+{`i%&=Utt2yeI+vRuA=sMuk za3%0d`mARYJ_%be`Fm+BdfohybK2^FYLC$VJiV=Ve6@=?fBJVFDUk4ehzOf0tp{H4 zoXFsk;%&SrAc2Kg5Y4e+LY8n&T4O}*jw_R`7Bg<{5Bc4Bwf(g1lUwGq+MvmSA?}|; zx>-Rh$GVx5(ofmxjrIS&nI9YUm>6Wom#B_~{*VvIeNELL>b zE|4I}`x@b-Mqb9CbRM_I-CG1C?mjl0l>|$H1rQH($zX9i*grrIOt4{g%6QMV_Pfu7 zZ2O(nek*EcUC+2B2i5UVj_Js}f3E9i$`|=_O8c%v1kM&th%-&`ICH@*&4?%G3?{q^ zj+)!l`i?K@?ZWB%-9I0JMhnk3fdyae1fzcEm#C$f@b^N~^#cxw$rgA_mat{bHrWW% z!O(u-g{JS9UIr)g?fvt-%%W7F$%rA>8JuTYVJb25FEG!nsN)jbb-MkPT<68TvWs^X zfYMt-l%m6%l>rh@pMRa4V%a#M_J#7FBdgrQ+wF>-b?YC^5>&cg6Vup{t(H>amSyrD znjeU=PNGrS;lKOgYFx=0Sc1F|WLj*ju=r&TE;S7VGja(q74e?&zd&(T#F5|4A4t#(VDShheF<&5NU;xOnoDE9>L; zPkL%P!}i55Pf_)2MKRC#LiSwHp2Hr!%lW&x&c9Axey^*Kz1BDD*7i)B0WIVlGC6wA z0W--$)R;iz8Bm;nDhOyMD^hgGNNc_#SmVXqaA&62jJ;D{Ee~bk+g7lw)JfH(*)gN7 z>Bf&G<-cTfAHfRf4G;rn;VYm)Zh)qXL~*7a-|_@LX>9%0GhugnLtHgqhUmjDx3nE| zZ2I2KmC(HdGq|7)?6Olt8Z0QxbY$JbR;|sJmCw8L zq1i6;lr093YC%)cz>3*u)-^<(1+x}fSiBGe7as72NQMYgi`y3V*Du32JXmmDGvY|! zAO5Y9Oi7Q+m&`RYi+TX7^g)3sg{`86RaU5B!Cd?6;)@QmOwX>cYBx9kgHmMsUs0Ut zi&ZMebq=eA6HBw+hDvBISDLshyzNH8!f$fYL64JHyl0fsm4bS>Aq)}#+t9p?-~t1n z=YZfMnwJXTG9Gt`Nz_3cx(aVh0h@eB$S;^iod78wfR*iNM1YzcqoFVw3gF(-Xj%YO zTce>c8VayJ*l1c9O$(!G0nvdPEgeVOj);NPVNp8jEKY=tuqrY!q1Yh`)20Qq|qH@rKa@dlBdc^(N!#5S4EvKgIy0?w9>&cT7^j7LL( z)}a7gl)&)#`uh0){cYbds#HYgyr9hDmJVDnaub&thPzG)TYxck12LzG5b;r|w0i(?5&x62S+H|3c9*7R=$XKMsb#V@XQaf@a|7=G9i z(*MUfMgZ#nAMiW4Xd1t#tfy*e%ETz;iAVa@JlfPBX3TzNp5jH>wC}rkz7@!?C}BOl zf62L;178;Io*hx_(aZ03xIOBr$l5!$QQ>{adldtCz#F?ttdUD{gp;5<+g>I5zB!?! zSo*@4amgzA%*<(bCceM8$JWNF;nqk0cX6E;znGxu{{q>ScooGm%GP<==u@i-R5a4T8*o0E?Li=Bo)79-cKbHVW)M~C(r6L zPo0-BwcqXaHRymRcq4)Y4`f+AA4-Bm-Y5lH{~y1Tsx-Lq8<;({ZX9 lo!|f!ZKI(;Y$z}=6s6|I&Rdh)1l+{T;OXk;vd$@?2>_Q(o@D?4 literal 15319 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYU_8XZ1{4ubUVDOp!Fryji(^PdTyo8_%zyI} zMS9u}WN=_77-XFqGEJtqJ13a1xM3w4;uH;TF-rPoFIF^=V!}r7ES8;c(FKPjP{SLi zgc7zClfZsl&bXk<;%2R|xHA{08}cA-$iZekL=5Nz6WmVt193tqm7UNa!W4Af->Bh3 z#sWm}UQh%3Ml&0aZw@%E@eNvP-x2g{&1~j1eIZ|4c+{`i%&=Utt2yeI+vRuA=sMuk za3%0d`mARYJ_%be`Fm+BdfohybK2^FYLC$VJiV=Ve6@=?fBJVFDUk4ehzOf0tp{H4 zoXFsk;%&SrAc2Kg5Y4e+LY8n&T4O}*jw_R`7Bg<{5Bc4Bwf(g1lUwGq+MvmSA?}|; zx>-Rh$GVx5(ofmxjrIS&nI9YUm>6Wom#B_~{*VvIeNELL>b zE|4I}`x@b-Mqb9CbRM_I-CG1C?mjl0l>|$H1rQH($zX9i*grrIOt4{g%6QMV_Pfu7 zZ2O(nek*EcUC+2B2i5UVj_Js}f3E9i$`|=_O8c%v1kM&th%-&`ICH@*&4?%G3?{q^ zj+)!l`i?K@?ZWB%-9I0JMhnk3fdyae1fzcEm#C$f@b^N~^#cxw$rgA_mat{bHrWW% z!O(u-g{JS9UIr)g?fvt-%%W7F$%rA>8JuTYVJb25FEG!nsN)jbb-MkPT<68TvWs^X zfYMt-l%m6%l>rh@pMRa4V%a#M_J#7FBdgrQ+wF>-b?YC^5>&cg6Vup{t(H>amSyrD znjeU=PNGrS;lKOgYFx=0Sc1F|WLj*ju=r&TE;S7VGja(q74e?&zd&(T#F5|4A4t#(VDShheF<&5NU;xOnoDE9>L; zPkL%P!}i55Pf_)2MKRC#LiSwHp2Hr!%lW&x&c9C4<4@&do(TlmM;XD2iUMHH0hYsC z@CMjiV2uu`O`Y{vIL_uwuxj$pWSC%mnQ8O%pubzX4jdFax5aP8l5-Ukr0sTmxoCa2 zPplSJhz2McSTP&T`UWemF|yhVF>oe?R_Hfc8P@HUZn5!NYpZ;uFJ*E6j>)^j8$y0( zh(3JsYxa$bg(flXn|~qF1ySWF!UM``EHCzcn|DW2G@@m~tGKM+{da6Rm;9O?Q9gOs z^QKE5)TbD_eb4C;n+J8iLmWrXIbazw2Q{H0iWE?i1=XLhGKf*Cm0_V^QB?nh;8(gq z=`ts__TQMeH4=PSUF>wRTZ;RoWuv@$#q;`kf13?+qD-iPZP}2Zc#9_}7?cZIUi{CffO!)UPjD84Eu?uh z7nTI!GO%n-u!Wbf1?UYnoC&He9^%kfcpOTgm>G2fsF^Yv3ZtPgnkkXXr_mfSnj=PY z#AuE{A0Qho7e>p4(Q;w5To|F{0>kNy2ymB=XTQLMUB_&t)`Gjw7ETXlU%P&B1EP~E zaX>%<)T^z7jSgURe@BP(Kr<(!p)eW>unDTsv@n_$M$-avMKPKqMsoxebA-;~iB`zT zYYQLavD3er)hAs4yKrapzH2>U>lg3Lf{ucNW}Ai9T-;)atOL0BjofN=n@ss z7%ZeH8eJs=3X0KC7!8Hd9DzJ0J(?p%bHr$l7|jvL9hT7?F`6SrbHr$lK=d9EtkL$u tXz4gwI*yi(qopIFOhmBgP&zU&oM#T?66lzs0o<_5;OXk;vd$@?2>@k)q Date: Fri, 1 Sep 2023 23:23:01 +0200 Subject: [PATCH 0235/1350] RENAMED: LoadFont*() parameter names for consistency and coherence --- src/raylib.h | 12 ++++----- src/rtext.c | 72 ++++++++++++++++++++++++++-------------------------- 2 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index d932e3a34..9043a11e9 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1371,13 +1371,13 @@ RLAPI int GetPixelDataSize(int width, int height, int format); // G // Font loading/unloading functions RLAPI Font GetFontDefault(void); // Get the default Font RLAPI Font LoadFont(const char *fileName); // Load font from file into GPU memory (VRAM) -RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int glyphCount); // Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set +RLAPI Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character set RLAPI Font LoadFontFromImage(Image image, Color key, int firstChar); // Load font from Image (XNA style) -RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int glyphCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf' +RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount); // Load font from memory buffer, fileType refers to extension: i.e. '.ttf' RLAPI bool IsFontReady(Font font); // Check if a font is ready -RLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int glyphCount, int type); // Load font data for further use -RLAPI Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **recs, int glyphCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info -RLAPI void UnloadFontData(GlyphInfo *chars, int glyphCount); // Unload font chars info data (RAM) +RLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount, int type); // Load font data for further use +RLAPI Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyphCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info +RLAPI void UnloadFontData(GlyphInfo *glyphs, int glyphCount); // Unload font chars info data (RAM) RLAPI void UnloadFont(Font font); // Unload font from GPU memory (VRAM) RLAPI bool ExportFontAsCode(Font font, const char *fileName); // Export font as code file, returns true on success @@ -1387,7 +1387,7 @@ RLAPI void DrawText(const char *text, int posX, int posY, int fontSize, Color co RLAPI void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // Draw text using font and additional parameters RLAPI void DrawTextPro(Font font, const char *text, Vector2 position, Vector2 origin, float rotation, float fontSize, float spacing, Color tint); // Draw text using Font and pro parameters (rotation) RLAPI void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSize, Color tint); // Draw one character (codepoint) -RLAPI void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 position, float fontSize, float spacing, Color tint); // Draw multiple character (codepoint) +RLAPI void DrawTextCodepoints(Font font, const int *codepoints, int codepointCount, Vector2 position, float fontSize, float spacing, Color tint); // Draw multiple character (codepoint) // Text font info functions RLAPI void SetTextLineSpacing(int spacing); // Set vertical line spacing when drawing with line-breaks diff --git a/src/rtext.c b/src/rtext.c index 3fb2b8861..9017c8b26 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -358,7 +358,7 @@ Font LoadFont(const char *fileName) // Load Font from TTF font file with generation parameters // NOTE: You can pass an array with desired characters, those characters should be available in the font // if array is NULL, default char set is selected 32..126 -Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int glyphCount) +Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount) { Font font = { 0 }; @@ -369,7 +369,7 @@ Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int glyphCou if (fileData != NULL) { // Loading font from memory data - font = LoadFontFromMemory(GetFileExtension(fileName), fileData, fileSize, fontSize, fontChars, glyphCount); + font = LoadFontFromMemory(GetFileExtension(fileName), fileData, fileSize, fontSize, codepoints, codepointCount); UnloadFileData(fileData); } @@ -504,7 +504,7 @@ Font LoadFontFromImage(Image image, Color key, int firstChar) } // Load font from memory buffer, fileType refers to extension: i.e. ".ttf" -Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int glyphCount) +Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount) { Font font = { 0 }; @@ -516,9 +516,9 @@ Font LoadFontFromMemory(const char *fileType, const unsigned char *fileData, int TextIsEqual(fileExtLower, ".otf")) { font.baseSize = fontSize; - font.glyphCount = (glyphCount > 0)? glyphCount : 95; + font.glyphCount = (codepointCount > 0)? codepointCount : 95; font.glyphPadding = 0; - font.glyphs = LoadFontData(fileData, dataSize, font.baseSize, fontChars, font.glyphCount, FONT_DEFAULT); + font.glyphs = LoadFontData(fileData, dataSize, font.baseSize, codepoints, font.glyphCount, FONT_DEFAULT); if (font.glyphs != NULL) { @@ -562,7 +562,7 @@ bool IsFontReady(Font font) // Load font data for further use // NOTE: Requires TTF font memory data and can generate SDF data -GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *fontChars, int glyphCount, int type) +GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount, int type) { // NOTE: Using some SDF generation default values, // trades off precision with ability to handle *smaller* sizes @@ -600,25 +600,25 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap); // In case no chars count provided, default to 95 - glyphCount = (glyphCount > 0)? glyphCount : 95; + codepointCount = (codepointCount > 0)? codepointCount : 95; // Fill fontChars in case not provided externally // NOTE: By default we fill glyphCount consecutively, starting at 32 (Space) - if (fontChars == NULL) + if (codepoints == NULL) { - fontChars = (int *)RL_MALLOC(glyphCount*sizeof(int)); - for (int i = 0; i < glyphCount; i++) fontChars[i] = i + 32; + codepoints = (int *)RL_MALLOC(codepointCount*sizeof(int)); + for (int i = 0; i < codepointCount; i++) codepoints[i] = i + 32; genFontChars = true; } - chars = (GlyphInfo *)RL_MALLOC(glyphCount*sizeof(GlyphInfo)); + chars = (GlyphInfo *)RL_MALLOC(codepointCount*sizeof(GlyphInfo)); // NOTE: Using simple packaging, one char after another - for (int i = 0; i < glyphCount; i++) + for (int i = 0; i < codepointCount; i++) { int chw = 0, chh = 0; // Character width and height (on generation) - int ch = fontChars[i]; // Character value to get info for + int ch = codepoints[i]; // Character value to get info for chars[i].value = ch; // Render a unicode codepoint to a bitmap @@ -678,7 +678,7 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz } else TRACELOG(LOG_WARNING, "FONT: Failed to process TTF font data"); - if (genFontChars) RL_FREE(fontChars); + if (genFontChars) RL_FREE(codepoints); } #endif @@ -688,17 +688,17 @@ GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSiz // Generate image font atlas using chars info // NOTE: Packing method: 0-Default, 1-Skyline #if defined(SUPPORT_FILEFORMAT_TTF) -Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphCount, int fontSize, int padding, int packMethod) +Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyphCount, int fontSize, int padding, int packMethod) { Image atlas = { 0 }; - if (chars == NULL) + if (glyphs == NULL) { TRACELOG(LOG_WARNING, "FONT: Provided chars info not valid, returning empty image atlas"); return atlas; } - *charRecs = NULL; + *glyphRecs = NULL; // In case no chars count provided we suppose default of 95 glyphCount = (glyphCount > 0)? glyphCount : 95; @@ -712,8 +712,8 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC for (int i = 0; i < glyphCount; i++) { - if (chars[i].image.width > maxGlyphWidth) maxGlyphWidth = chars[i].image.width; - totalWidth += chars[i].image.width + 4*padding; + if (glyphs[i].image.width > maxGlyphWidth) maxGlyphWidth = glyphs[i].image.width; + totalWidth += glyphs[i].image.width + 4*padding; } //#define SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE @@ -764,7 +764,7 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC for (int i = 0; i < glyphCount; i++) { // Check remaining space for glyph - if (offsetX >= (atlas.width - chars[i].image.width - 2*padding)) + if (offsetX >= (atlas.width - glyphs[i].image.width - 2*padding)) { offsetX = padding; @@ -789,22 +789,22 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC } // Copy pixel data from fc.data to atlas - for (int y = 0; y < chars[i].image.height; y++) + for (int y = 0; y < glyphs[i].image.height; y++) { - for (int x = 0; x < chars[i].image.width; x++) + for (int x = 0; x < glyphs[i].image.width; x++) { - ((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = ((unsigned char *)chars[i].image.data)[y*chars[i].image.width + x]; + ((unsigned char *)atlas.data)[(offsetY + y)*atlas.width + (offsetX + x)] = ((unsigned char *)glyphs[i].image.data)[y*glyphs[i].image.width + x]; } } // Fill chars rectangles in atlas info recs[i].x = (float)offsetX; recs[i].y = (float)offsetY; - recs[i].width = (float)chars[i].image.width; - recs[i].height = (float)chars[i].image.height; + recs[i].width = (float)glyphs[i].image.width; + recs[i].height = (float)glyphs[i].image.height; // Move atlas position X for next character drawing - offsetX += (chars[i].image.width + 2*padding); + offsetX += (glyphs[i].image.width + 2*padding); } } else if (packMethod == 1) // Use Skyline rect packing algorithm (stb_pack_rect) @@ -819,8 +819,8 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC for (int i = 0; i < glyphCount; i++) { rects[i].id = i; - rects[i].w = chars[i].image.width + 2*padding; - rects[i].h = chars[i].image.height + 2*padding; + rects[i].w = glyphs[i].image.width + 2*padding; + rects[i].h = glyphs[i].image.height + 2*padding; } // Package rectangles into atlas @@ -831,17 +831,17 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC // It returns char rectangles in atlas recs[i].x = rects[i].x + (float)padding; recs[i].y = rects[i].y + (float)padding; - recs[i].width = (float)chars[i].image.width; - recs[i].height = (float)chars[i].image.height; + recs[i].width = (float)glyphs[i].image.width; + recs[i].height = (float)glyphs[i].image.height; if (rects[i].was_packed) { // Copy pixel data from fc.data to atlas - for (int y = 0; y < chars[i].image.height; y++) + for (int y = 0; y < glyphs[i].image.height; y++) { - for (int x = 0; x < chars[i].image.width; x++) + for (int x = 0; x < glyphs[i].image.width; x++) { - ((unsigned char *)atlas.data)[(rects[i].y + padding + y)*atlas.width + (rects[i].x + padding + x)] = ((unsigned char *)chars[i].image.data)[y*chars[i].image.width + x]; + ((unsigned char *)atlas.data)[(rects[i].y + padding + y)*atlas.width + (rects[i].x + padding + x)] = ((unsigned char *)glyphs[i].image.data)[y*glyphs[i].image.width + x]; } } } @@ -879,7 +879,7 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC atlas.data = dataGrayAlpha; atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA; - *charRecs = recs; + *glyphRecs = recs; return atlas; } @@ -1176,14 +1176,14 @@ void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float fontSiz } // Draw multiple character (codepoints) -void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 position, float fontSize, float spacing, Color tint) +void DrawTextCodepoints(Font font, const int *codepoints, int codepointCount, Vector2 position, float fontSize, float spacing, Color tint) { int textOffsetY = 0; // Offset between lines (on linebreak '\n') float textOffsetX = 0.0f; // Offset X to next character to draw float scaleFactor = fontSize/font.baseSize; // Character quad scaling factor - for (int i = 0; i < count; i++) + for (int i = 0; i < codepointCount; i++) { int index = GetGlyphIndex(font, codepoints[i]); From 0f39051562909875d97a0156eef5d760f34d6390 Mon Sep 17 00:00:00 2001 From: Johannes Barthelmes <615914+jbarthelmes@users.noreply.github.com> Date: Sat, 2 Sep 2023 11:41:33 +0200 Subject: [PATCH 0236/1350] Fix uninitialized thread-locals in stbi #3282 (#3283) --- src/rtextures.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rtextures.c b/src/rtextures.c index b88aeb154..a97cb0b37 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -147,6 +147,8 @@ #define STBI_FREE RL_FREE #define STBI_REALLOC RL_REALLOC + #define STBI_NO_THREAD_LOCALS + #define STB_IMAGE_IMPLEMENTATION #include "external/stb_image.h" // Required for: stbi_load_from_file() // NOTE: Used to read image data (multiple formats support) From d41e3141f194d82ad0f6667aadac2d96d69bb770 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Sep 2023 11:48:19 +0200 Subject: [PATCH 0237/1350] REVIEWED: Added `SetTextLineSpacing()` to multiline examples --- examples/text/text_codepoints_loading.c | 2 ++ examples/text/text_font_loading.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/examples/text/text_codepoints_loading.c b/examples/text/text_codepoints_loading.c index 1f3db0cf3..376b4972a 100644 --- a/examples/text/text_codepoints_loading.c +++ b/examples/text/text_codepoints_loading.c @@ -52,6 +52,8 @@ int main(void) // Set bilinear scale filter for better font scaling SetTextureFilter(font.texture, TEXTURE_FILTER_BILINEAR); + SetTextLineSpacing(54); // Set line spacing for multiline text (when line breaks are included '\n') + // Free codepoints, atlas has already been generated free(codepointsNoDups); diff --git a/examples/text/text_font_loading.c b/examples/text/text_font_loading.c index 2ded64f09..a3ff988e9 100644 --- a/examples/text/text_font_loading.c +++ b/examples/text/text_font_loading.c @@ -47,6 +47,8 @@ int main(void) // NOTE: We define a font base size of 32 pixels tall and up-to 250 characters Font fontTtf = LoadFontEx("resources/pixantiqua.ttf", 32, 0, 250); + SetTextLineSpacing(48); // Set line spacing for multiline text (when line breaks are included '\n') + bool useTtf = false; SetTargetFPS(60); // Set our game to run at 60 frames-per-second From 0f447f1fb6aeed5e3e807d123978b7feca240d1a Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Sep 2023 12:05:34 +0200 Subject: [PATCH 0238/1350] REVIEWED: Data size type consistency between functions #3168 --- src/raudio.c | 22 ++++++++++---------- src/raylib.h | 8 ++++---- src/rmodels.c | 54 +++++++++++++++++++++++++------------------------ src/rtext.c | 6 +++--- src/rtextures.c | 10 ++++----- src/utils.c | 32 ++++++++++++++--------------- 6 files changed, 67 insertions(+), 65 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 9d1683a1f..4627cdf5c 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -413,8 +413,8 @@ static void MixAudioFrames(float *framesOut, const float *framesIn, ma_uint32 fr static bool IsFileExtension(const char *fileName, const char *ext); // Check file extension static const char *GetFileExtension(const char *fileName); // Get pointer to extension for a filename string (includes the dot: .png) -static unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead); // Load file data as byte array (read) -static bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite); // Save data to file from byte array (write) +static unsigned char *LoadFileData(const char *fileName, int *dataSize); // Load file data as byte array (read) +static bool SaveFileData(const char *fileName, void *data, int dataSize); // Save data to file from byte array (write) static bool SaveFileText(const char *fileName, char *text); // Save text data to file (write), string must be '\0' terminated #endif @@ -732,11 +732,11 @@ Wave LoadWave(const char *fileName) Wave wave = { 0 }; // Loading file to memory - unsigned int fileSize = 0; - unsigned char *fileData = LoadFileData(fileName, &fileSize); + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileName, &dataSize); // Loading wave from memory data - if (fileData != NULL) wave = LoadWaveFromMemory(GetFileExtension(fileName), fileData, fileSize); + if (fileData != NULL) wave = LoadWaveFromMemory(GetFileExtension(fileName), fileData, dataSize); RL_FREE(fileData); @@ -2619,10 +2619,10 @@ static const char *GetFileExtension(const char *fileName) } // Load data from file into a buffer -static unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead) +static unsigned char *LoadFileData(const char *fileName, int *dataSize) { unsigned char *data = NULL; - *bytesRead = 0; + *dataSize = 0; if (fileName != NULL) { @@ -2642,7 +2642,7 @@ static unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements] unsigned int count = (unsigned int)fread(data, sizeof(unsigned char), size, file); - *bytesRead = count; + *dataSize = count; if (count != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded", fileName); else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName); @@ -2659,7 +2659,7 @@ static unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead } // Save data to file from buffer -static bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite) +static bool SaveFileData(const char *fileName, void *data, int dataSize) { if (fileName != NULL) { @@ -2667,10 +2667,10 @@ static bool SaveFileData(const char *fileName, void *data, unsigned int bytesToW if (file != NULL) { - unsigned int count = (unsigned int)fwrite(data, sizeof(unsigned char), bytesToWrite, file); + unsigned int count = (unsigned int)fwrite(data, sizeof(unsigned char), dataSize, file); if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write file", fileName); - else if (count != bytesToWrite) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName); + else if (count != dataSize) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName); else TRACELOG(LOG_INFO, "FILEIO: [%s] File saved successfully", fileName); fclose(file); diff --git a/src/raylib.h b/src/raylib.h index 9043a11e9..084fb4dc0 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1075,10 +1075,10 @@ RLAPI void SetLoadFileTextCallback(LoadFileTextCallback callback); // Set custom RLAPI void SetSaveFileTextCallback(SaveFileTextCallback callback); // Set custom file text data saver // Files management functions -RLAPI unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead); // Load file data as byte array (read) +RLAPI unsigned char *LoadFileData(const char *fileName, int *dataSize); // Load file data as byte array (read) RLAPI void UnloadFileData(unsigned char *data); // Unload file data allocated by LoadFileData() -RLAPI bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite); // Save data to file from byte array (write), returns true on success -RLAPI bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char *fileName); // Export data to code (.h), returns true on success +RLAPI bool SaveFileData(const char *fileName, void *data, int dataSize); // Save data to file from byte array (write), returns true on success +RLAPI bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName); // Export data to code (.h), returns true on success RLAPI char *LoadFileText(const char *fileName); // Load text data from file (read), returns a '\0' terminated string RLAPI void UnloadFileText(char *text); // Unload file text data allocated by LoadFileText() RLAPI bool SaveFileText(const char *fileName, char *text); // Save text data to file (write), string must be '\0' terminated, returns true on success @@ -1506,7 +1506,7 @@ RLAPI void SetMaterialTexture(Material *material, int mapType, Texture2D texture RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); // Set material for a mesh // Model animations loading/unloading functions -RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, unsigned int *animCount); // Load model animations from file +RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount); // Load model animations from file RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data RLAPI void UnloadModelAnimations(ModelAnimation *animations, unsigned int count); // Unload animation array data diff --git a/src/rmodels.c b/src/rmodels.c index ebb454e4a..66de97273 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -151,18 +151,18 @@ static Model LoadOBJ(const char *fileName); // Load OBJ mesh data #endif #if defined(SUPPORT_FILEFORMAT_IQM) static Model LoadIQM(const char *fileName); // Load IQM mesh data -static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, unsigned int *animCount); // Load IQM animation data +static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCount); // Load IQM animation data #endif #if defined(SUPPORT_FILEFORMAT_GLTF) static Model LoadGLTF(const char *fileName); // Load GLTF mesh data -static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned int *animCount); // Load GLTF animation data +static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCount); // Load GLTF animation data #endif #if defined(SUPPORT_FILEFORMAT_VOX) static Model LoadVOX(const char *filename); // Load VOX mesh data #endif #if defined(SUPPORT_FILEFORMAT_M3D) static Model LoadM3D(const char *filename); // Load M3D mesh data -static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int *animCount); // Load M3D animation data +static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCount); // Load M3D animation data #endif #if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL) static void ProcessMaterialsOBJ(Material *rayMaterials, tinyobj_material_t *materials, int materialCount); // Process obj materials @@ -1979,7 +1979,7 @@ void SetModelMeshMaterial(Model *model, int meshId, int materialId) } // Load model animations from file -ModelAnimation *LoadModelAnimations(const char *fileName, unsigned int *animCount) +ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount) { ModelAnimation *animations = NULL; @@ -4081,8 +4081,8 @@ static Model LoadIQM(const char *fileName) #define MESH_NAME_LENGTH 32 // Mesh name string length #define MATERIAL_NAME_LENGTH 32 // Material name string length - unsigned int fileSize = 0; - unsigned char *fileData = LoadFileData(fileName, &fileSize); + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileName, &dataSize); unsigned char *fileDataPtr = fileData; // IQM file structs @@ -4090,7 +4090,7 @@ static Model LoadIQM(const char *fileName) typedef struct IQMHeader { char magic[16]; unsigned int version; - unsigned int filesize; + unsigned int dataSize; unsigned int flags; unsigned int num_text, ofs_text; unsigned int num_meshes, ofs_meshes; @@ -4443,19 +4443,19 @@ static Model LoadIQM(const char *fileName) } // Load IQM animation data -static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, unsigned int *animCount) +static ModelAnimation *LoadModelAnimationsIQM(const char *fileName, int *animCount) { #define IQM_MAGIC "INTERQUAKEMODEL" // IQM file magic number #define IQM_VERSION 2 // only IQM version 2 supported - unsigned int fileSize = 0; - unsigned char *fileData = LoadFileData(fileName, &fileSize); + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileName, &dataSize); unsigned char *fileDataPtr = fileData; typedef struct IQMHeader { char magic[16]; unsigned int version; - unsigned int filesize; + unsigned int dataSize; unsigned int flags; unsigned int num_text, ofs_text; unsigned int num_meshes, ofs_meshes; @@ -4815,7 +4815,7 @@ static Model LoadGLTF(const char *fileName) Model model = { 0 }; // glTF file loading - unsigned int dataSize = 0; + int dataSize = 0; unsigned char *fileData = LoadFileData(fileName, &dataSize); if (fileData == NULL) return model; @@ -5295,10 +5295,10 @@ static bool GetPoseAtTimeGLTF(cgltf_accessor *input, cgltf_accessor *output, flo #define GLTF_ANIMDELAY 17 // Animation frames delay, (~1000 ms/60 FPS = 16.666666* ms) -static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned int *animCount) +static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, int *animCount) { // glTF file loading - unsigned int dataSize = 0; + int dataSize = 0; unsigned char *fileData = LoadFileData(fileName, &dataSize); ModelAnimation *animations = NULL; @@ -5466,11 +5466,11 @@ static Model LoadVOX(const char *fileName) int nbvertices = 0; int meshescount = 0; - unsigned int fileSize = 0; - unsigned char *fileData = NULL; - + // Read vox file into buffer - fileData = LoadFileData(fileName, &fileSize); + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileName, &dataSize); + if (fileData == 0) { TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load VOX file", fileName); @@ -5479,7 +5479,7 @@ static Model LoadVOX(const char *fileName) // Read and build voxarray description VoxArray3D voxarray = { 0 }; - int ret = Vox_LoadFromMemory(fileData, fileSize, &voxarray); + int ret = Vox_LoadFromMemory(fileData, dataSize, &voxarray); if (ret != VOX_SUCCESS) { @@ -5574,9 +5574,10 @@ static Model LoadM3D(const char *fileName) m3d_t *m3d = NULL; m3dp_t *prop = NULL; - unsigned int bytesRead = 0; - unsigned char *fileData = LoadFileData(fileName, &bytesRead); int i, j, k, l, n, mi = -2, vcolor = 0; + + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileName, &dataSize); if (fileData != NULL) { @@ -5878,16 +5879,17 @@ static Model LoadM3D(const char *fileName) #define M3D_ANIMDELAY 17 // Animation frames delay, (~1000 ms/60 FPS = 16.666666* ms) // Load M3D animation data -static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, unsigned int *animCount) +static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCount) { - m3d_t *m3d = NULL; - unsigned int bytesRead = 0; - unsigned char *fileData = LoadFileData(fileName, &bytesRead); ModelAnimation *animations = NULL; + + m3d_t *m3d = NULL; int i = 0, j = 0; - *animCount = 0; + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileName, &dataSize); + if (fileData != NULL) { m3d = m3d_load(fileData, m3d_loaderhook, m3d_freehook, NULL); diff --git a/src/rtext.c b/src/rtext.c index 9017c8b26..facf24c24 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -363,13 +363,13 @@ Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepoi Font font = { 0 }; // Loading file to memory - unsigned int fileSize = 0; - unsigned char *fileData = LoadFileData(fileName, &fileSize); + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileName, &dataSize); if (fileData != NULL) { // Loading font from memory data - font = LoadFontFromMemory(GetFileExtension(fileName), fileData, fileSize, fontSize, codepoints, codepointCount); + font = LoadFontFromMemory(GetFileExtension(fileName), fileData, dataSize, fontSize, codepoints, codepointCount); UnloadFileData(fileData); } diff --git a/src/rtextures.c b/src/rtextures.c index a97cb0b37..566371112 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -271,11 +271,11 @@ Image LoadImage(const char *fileName) #endif // Loading file to memory - unsigned int fileSize = 0; - unsigned char *fileData = LoadFileData(fileName, &fileSize); + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileName, &dataSize); // Loading image from memory data - if (fileData != NULL) image = LoadImageFromMemory(GetFileExtension(fileName), fileData, fileSize); + if (fileData != NULL) image = LoadImageFromMemory(GetFileExtension(fileName), fileData, dataSize); RL_FREE(fileData); @@ -287,7 +287,7 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int { Image image = { 0 }; - unsigned int dataSize = 0; + int dataSize = 0; unsigned char *fileData = LoadFileData(fileName, &dataSize); if (fileData != NULL) @@ -323,7 +323,7 @@ Image LoadImageAnim(const char *fileName, int *frames) #if defined(SUPPORT_FILEFORMAT_GIF) if (IsFileExtension(fileName, ".gif")) { - unsigned int dataSize = 0; + int dataSize = 0; unsigned char *fileData = LoadFileData(fileName, &dataSize); if (fileData != NULL) diff --git a/src/utils.c b/src/utils.c index ac57a2157..27747b757 100644 --- a/src/utils.c +++ b/src/utils.c @@ -180,16 +180,16 @@ void MemFree(void *ptr) } // Load data from file into a buffer -unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead) +unsigned char *LoadFileData(const char *fileName, int *dataSize) { unsigned char *data = NULL; - *bytesRead = 0; + *dataSize = 0; if (fileName != NULL) { if (loadFileData) { - data = loadFileData(fileName, bytesRead); + data = loadFileData(fileName, dataSize); return data; } #if defined(SUPPORT_STANDARD_FILEIO) @@ -211,7 +211,7 @@ unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead) { // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements] unsigned int count = (unsigned int)fread(data, sizeof(unsigned char), size, file); - *bytesRead = count; + *dataSize = count; if (count != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded", fileName); else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName); @@ -239,7 +239,7 @@ void UnloadFileData(unsigned char *data) } // Save data to file from buffer -bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite) +bool SaveFileData(const char *fileName, void *data, int dataSize) { bool success = false; @@ -247,17 +247,17 @@ bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite) { if (saveFileData) { - return saveFileData(fileName, data, bytesToWrite); + return saveFileData(fileName, data, dataSize); } #if defined(SUPPORT_STANDARD_FILEIO) FILE *file = fopen(fileName, "wb"); if (file != NULL) { - unsigned int count = (unsigned int)fwrite(data, sizeof(unsigned char), bytesToWrite, file); + unsigned int count = (unsigned int)fwrite(data, sizeof(unsigned char), dataSize, file); if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write file", fileName); - else if (count != bytesToWrite) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName); + else if (count != dataSize) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName); else TRACELOG(LOG_INFO, "FILEIO: [%s] File saved successfully", fileName); int result = fclose(file); @@ -274,7 +274,7 @@ bool SaveFileData(const char *fileName, void *data, unsigned int bytesToWrite) } // Export data to code (.h), returns true on success -bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char *fileName) +bool ExportDataAsCode(const unsigned char *data, int dataSize, const char *fileName) { bool success = false; @@ -284,7 +284,7 @@ bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char * // NOTE: Text data buffer size is estimated considering raw data size in bytes // and requiring 6 char bytes for every byte: "0x00, " - char *txtData = (char *)RL_CALLOC(size*6 + 2000, sizeof(char)); + char *txtData = (char *)RL_CALLOC(dataSize*6 + 2000, sizeof(char)); int byteCount = 0; byteCount += sprintf(txtData + byteCount, "////////////////////////////////////////////////////////////////////////////////////////\n"); @@ -303,11 +303,11 @@ bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char * strcpy(varFileName, GetFileNameWithoutExt(fileName)); for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; } - byteCount += sprintf(txtData + byteCount, "#define %s_DATA_SIZE %i\n\n", varFileName, size); + byteCount += sprintf(txtData + byteCount, "#define %s_DATA_SIZE %i\n\n", varFileName, dataSize); byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%s_DATA_SIZE] = { ", varFileName, varFileName); - for (unsigned int i = 0; i < size - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), data[i]); - byteCount += sprintf(txtData + byteCount, "0x%x };\n", data[size - 1]); + for (int i = 0; i < (dataSize - 1); i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%x,\n" : "0x%x, "), data[i]); + byteCount += sprintf(txtData + byteCount, "0x%x };\n", data[dataSize - 1]); // NOTE: Text data size exported is determined by '\0' (NULL) character success = SaveFileText(fileName, txtData); @@ -465,12 +465,12 @@ FILE *android_fopen(const char *fileName, const char *mode) // Module specific Functions Definition //---------------------------------------------------------------------------------- #if defined(PLATFORM_ANDROID) -static int android_read(void *cookie, char *buf, int size) +static int android_read(void *cookie, char *data, int dataSize) { - return AAsset_read((AAsset *)cookie, buf, size); + return AAsset_read((AAsset *)cookie, data, dataSize); } -static int android_write(void *cookie, const char *buf, int size) +static int android_write(void *cookie, const char *data, int dataSize) { TRACELOG(LOG_WARNING, "ANDROID: Failed to provide write access to APK"); From 6e18d96e7ad45e63c15b5e1f6f2ca52d2e9fab4a Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Sep 2023 12:54:36 +0200 Subject: [PATCH 0239/1350] Some tweaks --- src/raylib.h | 2 +- src/rcore.c | 2 +- src/rmodels.c | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 084fb4dc0..6f67d839e 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1509,7 +1509,7 @@ RLAPI void SetModelMeshMaterial(Model *model, int meshId, int materialId); RLAPI ModelAnimation *LoadModelAnimations(const char *fileName, int *animCount); // Load model animations from file RLAPI void UpdateModelAnimation(Model model, ModelAnimation anim, int frame); // Update model animation pose RLAPI void UnloadModelAnimation(ModelAnimation anim); // Unload animation data -RLAPI void UnloadModelAnimations(ModelAnimation *animations, unsigned int count); // Unload animation array data +RLAPI void UnloadModelAnimations(ModelAnimation *animations, int animCount); // Unload animation array data RLAPI bool IsModelAnimationValid(Model model, ModelAnimation anim); // Check model animation skeleton match // Collision detection functions diff --git a/src/rcore.c b/src/rcore.c index 2c53eb5e6..912f85c0d 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -601,7 +601,7 @@ static const char *autoEventTypeName[] = { "ACTION_SETTARGETFPS" }; -// Automation Event (24 bytes) +// Automation event (24 bytes) typedef struct AutomationEvent { unsigned int frame; // Event frame unsigned int type; // Event type (AutomationEventType) diff --git a/src/rmodels.c b/src/rmodels.c index 66de97273..5a6d98ac6 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2099,9 +2099,9 @@ void UpdateModelAnimation(Model model, ModelAnimation anim, int frame) } // Unload animation array data -void UnloadModelAnimations(ModelAnimation *animations, unsigned int count) +void UnloadModelAnimations(ModelAnimation *animations, int animCount) { - for (unsigned int i = 0; i < count; i++) UnloadModelAnimation(animations[i]); + for (int i = 0; i < animCount; i++) UnloadModelAnimation(animations[i]); RL_FREE(animations); } From 75e5cd86d7d1c4c6e0f266f3ae68f3af34fa78ac Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Sep 2023 12:58:47 +0200 Subject: [PATCH 0240/1350] Use internal default allocators, instead of user-exposed ones --- src/rmodels.c | 4 ++-- src/rtext.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 5a6d98ac6..9616f0ceb 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1904,7 +1904,7 @@ Material *LoadMaterials(const char *fileName, int *materialCount) int result = tinyobj_parse_mtl_file(&mats, &count, fileName); if (result != TINYOBJ_SUCCESS) TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to parse materials file", fileName); - materials = MemAlloc(count*sizeof(Material)); + materials = RL_MALLOC(count*sizeof(Material)); ProcessMaterialsOBJ(materials, mats, count); tinyobj_materials_free(mats, count); @@ -4704,7 +4704,7 @@ static Image LoadImageFromCgltfImage(cgltf_image *cgltfImage, const char *texPat if (result == cgltf_result_success) { image = LoadImageFromMemory(".png", (unsigned char *)data, outSize); - MemFree(data); + RL_FREE(data); } } } diff --git a/src/rtext.c b/src/rtext.c index facf24c24..5ca97d06d 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -975,7 +975,7 @@ bool ExportFontAsCode(Font font, const char *fileName) byteCount += sprintf(txtData + byteCount, "static unsigned char fontData_%s[COMPRESSED_DATA_SIZE_FONT_%s] = { ", fileNamePascal, TextToUpper(fileNamePascal)); for (int i = 0; i < compDataSize - 1; i++) byteCount += sprintf(txtData + byteCount, ((i%TEXT_BYTES_PER_LINE == 0)? "0x%02x,\n " : "0x%02x, "), compData[i]); byteCount += sprintf(txtData + byteCount, "0x%02x };\n\n", compData[compDataSize - 1]); - MemFree(compData); + RL_FREE(compData); #else // Save font image data (uncompressed) byteCount += sprintf(txtData + byteCount, "// Font image pixels data\n"); From c03ab03627232047c63b11c27567ef3d0a74ca3c Mon Sep 17 00:00:00 2001 From: bXi Date: Sat, 2 Sep 2023 07:00:18 -0400 Subject: [PATCH 0241/1350] Added rudimentary SVG support. (#2738) * Added rudimentary SVG support. Added 2 functions ImageLoadSvg and ImageLoadSvgWithSize. * Added an example on how to use ImageLoadSvgWithSize and adjusted Makefiles accordingly. * Added actual correct example file. * Reviewed the code to keep the raylib coding conventions in mind. Moved the LoadImageSvg() code into LoadImage() guarded by SUPPORT_FILEFORMAT_SVG. Renamed LoadImageSvgWithSize() to LoadImageSvg(). Added a LoadImageSvgFromString() function to parse the loaded SVG into an actual image. This does the bulk of the work. * Fixed typo. --------- Co-authored-by: Ray --- examples/Makefile | 3 +- examples/Makefile.Web | 3 +- examples/textures/resources/test.svg | 1 + examples/textures/textures_svg_loading.c | 72 + examples/textures/textures_svg_loading.png | Bin 0 -> 13850 bytes src/config.h | 30 +- src/external/nanosvg.h | 3053 ++++++++++++++++++++ src/external/nanosvgrast.h | 1458 ++++++++++ src/raylib.h | 2 + src/rtextures.c | 95 + 10 files changed, 4700 insertions(+), 17 deletions(-) create mode 100644 examples/textures/resources/test.svg create mode 100644 examples/textures/textures_svg_loading.c create mode 100644 examples/textures/textures_svg_loading.png create mode 100644 src/external/nanosvg.h create mode 100644 src/external/nanosvgrast.h diff --git a/examples/Makefile b/examples/Makefile index 7028a9542..4ba4f1720 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -475,7 +475,8 @@ TEXTURES = \ textures/textures_draw_tiled \ textures/textures_polygon \ textures/textures_gif_player \ - textures/textures_fog_of_war + textures/textures_fog_of_war \ + textures/textures_svg_loading TEXT = \ text/text_raylib_fonts \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 4574e928e..ccb4550f9 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -450,7 +450,8 @@ TEXTURES = \ textures/textures_draw_tiled \ textures/textures_polygon \ textures/textures_gif_player \ - textures/textures_fog_of_war + textures/textures_fog_of_war \ + textures/textures_svg_loading TEXT = \ text/text_raylib_fonts \ diff --git a/examples/textures/resources/test.svg b/examples/textures/resources/test.svg new file mode 100644 index 000000000..3d9bbb113 --- /dev/null +++ b/examples/textures/resources/test.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/examples/textures/textures_svg_loading.c b/examples/textures/textures_svg_loading.c new file mode 100644 index 000000000..434ec7602 --- /dev/null +++ b/examples/textures/textures_svg_loading.c @@ -0,0 +1,72 @@ +/******************************************************************************************* +* +* raylib [textures] example - SVG loading and texture creation +* +* NOTE: Images are loaded in CPU memory (RAM); textures are loaded in GPU memory (VRAM) +* +* Example originally created with raylib 4.2, last time updated with raylib 4.2 +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2022 Dennis Meinen (@bixxy#4258 on Discord) +* +********************************************************************************************/ + +#include "raylib.h" + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [textures] example - svg loading"); + + // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) + + Image image = LoadImageSvg("resources/test.svg", 400, 350); // Loaded in CPU memory (RAM) + Texture2D texture = LoadTextureFromImage(image); // Image converted to texture, GPU memory (VRAM) + UnloadImage(image); // Once image has been converted to texture and uploaded to VRAM, it can be unloaded from RAM + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //--------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + // TODO: Update your variables here + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawTexture(texture, screenWidth/2 - texture.width/2, screenHeight/2 - texture.height/2, WHITE); + + //Red border to illustrate how the SVG is centered within the specified dimensions + DrawRectangleLines((screenWidth / 2 - texture.width / 2) - 1, (screenHeight / 2 - texture.height / 2) - 1, texture.width + 2, texture.height + 2, RED); + + DrawText("this IS a texture loaded from an SVG file!", 300, 410, 10, GRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadTexture(texture); // Texture unloading + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/textures/textures_svg_loading.png b/examples/textures/textures_svg_loading.png new file mode 100644 index 0000000000000000000000000000000000000000..fa98605fa8508a068537defae8a8fc960a6a6c08 GIT binary patch literal 13850 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, image->height); + // Use... + for (NSVGshape *shape = image->shapes; shape != NULL; shape = shape->next) { + for (NSVGpath *path = shape->paths; path != NULL; path = path->next) { + for (int i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); + } + } + } + // Delete + nsvgDelete(image); +*/ + +enum NSVGpaintType { + NSVG_PAINT_NONE = 0, + NSVG_PAINT_COLOR = 1, + NSVG_PAINT_LINEAR_GRADIENT = 2, + NSVG_PAINT_RADIAL_GRADIENT = 3 +}; + +enum NSVGspreadType { + NSVG_SPREAD_PAD = 0, + NSVG_SPREAD_REFLECT = 1, + NSVG_SPREAD_REPEAT = 2 +}; + +enum NSVGlineJoin { + NSVG_JOIN_MITER = 0, + NSVG_JOIN_ROUND = 1, + NSVG_JOIN_BEVEL = 2 +}; + +enum NSVGlineCap { + NSVG_CAP_BUTT = 0, + NSVG_CAP_ROUND = 1, + NSVG_CAP_SQUARE = 2 +}; + +enum NSVGfillRule { + NSVG_FILLRULE_NONZERO = 0, + NSVG_FILLRULE_EVENODD = 1 +}; + +enum NSVGflags { + NSVG_FLAGS_VISIBLE = 0x01 +}; + +typedef struct NSVGgradientStop { + unsigned int color; + float offset; +} NSVGgradientStop; + +typedef struct NSVGgradient { + float xform[6]; + char spread; + float fx, fy; + int nstops; + NSVGgradientStop stops[1]; +} NSVGgradient; + +typedef struct NSVGpaint { + char type; + union { + unsigned int color; + NSVGgradient* gradient; + }; +} NSVGpaint; + +typedef struct NSVGpath +{ + float* pts; // Cubic bezier points: x0,y0, [cpx1,cpx1,cpx2,cpy2,x1,y1], ... + int npts; // Total number of bezier points. + char closed; // Flag indicating if shapes should be treated as closed. + float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. + struct NSVGpath* next; // Pointer to next path, or NULL if last element. +} NSVGpath; + +typedef struct NSVGshape +{ + char id[64]; // Optional 'id' attr of the shape or its group + NSVGpaint fill; // Fill paint + NSVGpaint stroke; // Stroke paint + float opacity; // Opacity of the shape. + float strokeWidth; // Stroke width (scaled). + float strokeDashOffset; // Stroke dash offset (scaled). + float strokeDashArray[8]; // Stroke dash array (scaled). + char strokeDashCount; // Number of dash values in dash array. + char strokeLineJoin; // Stroke join type. + char strokeLineCap; // Stroke cap type. + float miterLimit; // Miter limit + char fillRule; // Fill rule, see NSVGfillRule. + unsigned char flags; // Logical or of NSVG_FLAGS_* flags + float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. + NSVGpath* paths; // Linked list of paths in the image. + struct NSVGshape* next; // Pointer to next shape, or NULL if last element. +} NSVGshape; + +typedef struct NSVGimage +{ + float width; // Width of the image. + float height; // Height of the image. + NSVGshape* shapes; // Linked list of shapes in the image. +} NSVGimage; + +// Parses SVG file from a file, returns SVG image as paths. +NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi); + +// Parses SVG file from a null terminated string, returns SVG image as paths. +// Important note: changes the string. +NSVGimage* nsvgParse(char* input, const char* units, float dpi); + +// Duplicates a path. +NSVGpath* nsvgDuplicatePath(NSVGpath* p); + +// Deletes an image. +void nsvgDelete(NSVGimage* image); + +#ifndef NANOSVG_CPLUSPLUS +#ifdef __cplusplus +} +#endif +#endif + +#ifdef NANOSVG_IMPLEMENTATION + +#include +#include +#include +#include + +#define NSVG_PI (3.14159265358979323846264338327f) +#define NSVG_KAPPA90 (0.5522847493f) // Length proportional to radius of a cubic bezier handle for 90deg arcs. + +#define NSVG_ALIGN_MIN 0 +#define NSVG_ALIGN_MID 1 +#define NSVG_ALIGN_MAX 2 +#define NSVG_ALIGN_NONE 0 +#define NSVG_ALIGN_MEET 1 +#define NSVG_ALIGN_SLICE 2 + +#define NSVG_NOTUSED(v) do { (void)(1 ? (void)0 : ( (void)(v) ) ); } while(0) +#define NSVG_RGB(r, g, b) (((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16)) + +#ifdef _MSC_VER + #pragma warning (disable: 4996) // Switch off security warnings + #pragma warning (disable: 4100) // Switch off unreferenced formal parameter warnings + #ifdef __cplusplus + #define NSVG_INLINE inline + #else + #define NSVG_INLINE + #endif +#else + #define NSVG_INLINE inline +#endif + + +static int nsvg__isspace(char c) +{ + return strchr(" \t\n\v\f\r", c) != 0; +} + +static int nsvg__isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static NSVG_INLINE float nsvg__minf(float a, float b) { return a < b ? a : b; } +static NSVG_INLINE float nsvg__maxf(float a, float b) { return a > b ? a : b; } + + +// Simple XML parser + +#define NSVG_XML_TAG 1 +#define NSVG_XML_CONTENT 2 +#define NSVG_XML_MAX_ATTRIBS 256 + +static void nsvg__parseContent(char* s, + void (*contentCb)(void* ud, const char* s), + void* ud) +{ + // Trim start white spaces + while (*s && nsvg__isspace(*s)) s++; + if (!*s) return; + + if (contentCb) + (*contentCb)(ud, s); +} + +static void nsvg__parseElement(char* s, + void (*startelCb)(void* ud, const char* el, const char** attr), + void (*endelCb)(void* ud, const char* el), + void* ud) +{ + const char* attr[NSVG_XML_MAX_ATTRIBS]; + int nattr = 0; + char* name; + int start = 0; + int end = 0; + char quote; + + // Skip white space after the '<' + while (*s && nsvg__isspace(*s)) s++; + + // Check if the tag is end tag + if (*s == '/') { + s++; + end = 1; + } else { + start = 1; + } + + // Skip comments, data and preprocessor stuff. + if (!*s || *s == '?' || *s == '!') + return; + + // Get tag name + name = s; + while (*s && !nsvg__isspace(*s)) s++; + if (*s) { *s++ = '\0'; } + + // Get attribs + while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) { + char* name = NULL; + char* value = NULL; + + // Skip white space before the attrib name + while (*s && nsvg__isspace(*s)) s++; + if (!*s) break; + if (*s == '/') { + end = 1; + break; + } + name = s; + // Find end of the attrib name. + while (*s && !nsvg__isspace(*s) && *s != '=') s++; + if (*s) { *s++ = '\0'; } + // Skip until the beginning of the value. + while (*s && *s != '\"' && *s != '\'') s++; + if (!*s) break; + quote = *s; + s++; + // Store value and find the end of it. + value = s; + while (*s && *s != quote) s++; + if (*s) { *s++ = '\0'; } + + // Store only well formed attributes + if (name && value) { + attr[nattr++] = name; + attr[nattr++] = value; + } + } + + // List terminator + attr[nattr++] = 0; + attr[nattr++] = 0; + + // Call callbacks. + if (start && startelCb) + (*startelCb)(ud, name, attr); + if (end && endelCb) + (*endelCb)(ud, name); +} + +int nsvg__parseXML(char* input, + void (*startelCb)(void* ud, const char* el, const char** attr), + void (*endelCb)(void* ud, const char* el), + void (*contentCb)(void* ud, const char* s), + void* ud) +{ + char* s = input; + char* mark = s; + int state = NSVG_XML_CONTENT; + while (*s) { + if (*s == '<' && state == NSVG_XML_CONTENT) { + // Start of a tag + *s++ = '\0'; + nsvg__parseContent(mark, contentCb, ud); + mark = s; + state = NSVG_XML_TAG; + } else if (*s == '>' && state == NSVG_XML_TAG) { + // Start of a content or new tag. + *s++ = '\0'; + nsvg__parseElement(mark, startelCb, endelCb, ud); + mark = s; + state = NSVG_XML_CONTENT; + } else { + s++; + } + } + + return 1; +} + + +/* Simple SVG parser. */ + +#define NSVG_MAX_ATTR 128 + +enum NSVGgradientUnits { + NSVG_USER_SPACE = 0, + NSVG_OBJECT_SPACE = 1 +}; + +#define NSVG_MAX_DASHES 8 + +enum NSVGunits { + NSVG_UNITS_USER, + NSVG_UNITS_PX, + NSVG_UNITS_PT, + NSVG_UNITS_PC, + NSVG_UNITS_MM, + NSVG_UNITS_CM, + NSVG_UNITS_IN, + NSVG_UNITS_PERCENT, + NSVG_UNITS_EM, + NSVG_UNITS_EX +}; + +typedef struct NSVGcoordinate { + float value; + int units; +} NSVGcoordinate; + +typedef struct NSVGlinearData { + NSVGcoordinate x1, y1, x2, y2; +} NSVGlinearData; + +typedef struct NSVGradialData { + NSVGcoordinate cx, cy, r, fx, fy; +} NSVGradialData; + +typedef struct NSVGgradientData +{ + char id[64]; + char ref[64]; + char type; + union { + NSVGlinearData linear; + NSVGradialData radial; + }; + char spread; + char units; + float xform[6]; + int nstops; + NSVGgradientStop* stops; + struct NSVGgradientData* next; +} NSVGgradientData; + +typedef struct NSVGattrib +{ + char id[64]; + float xform[6]; + unsigned int fillColor; + unsigned int strokeColor; + float opacity; + float fillOpacity; + float strokeOpacity; + char fillGradient[64]; + char strokeGradient[64]; + float strokeWidth; + float strokeDashOffset; + float strokeDashArray[NSVG_MAX_DASHES]; + int strokeDashCount; + char strokeLineJoin; + char strokeLineCap; + float miterLimit; + char fillRule; + float fontSize; + unsigned int stopColor; + float stopOpacity; + float stopOffset; + char hasFill; + char hasStroke; + char visible; +} NSVGattrib; + +typedef struct NSVGparser +{ + NSVGattrib attr[NSVG_MAX_ATTR]; + int attrHead; + float* pts; + int npts; + int cpts; + NSVGpath* plist; + NSVGimage* image; + NSVGgradientData* gradients; + NSVGshape* shapesTail; + float viewMinx, viewMiny, viewWidth, viewHeight; + int alignX, alignY, alignType; + float dpi; + char pathFlag; + char defsFlag; +} NSVGparser; + +static void nsvg__xformIdentity(float* t) +{ + t[0] = 1.0f; t[1] = 0.0f; + t[2] = 0.0f; t[3] = 1.0f; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetTranslation(float* t, float tx, float ty) +{ + t[0] = 1.0f; t[1] = 0.0f; + t[2] = 0.0f; t[3] = 1.0f; + t[4] = tx; t[5] = ty; +} + +static void nsvg__xformSetScale(float* t, float sx, float sy) +{ + t[0] = sx; t[1] = 0.0f; + t[2] = 0.0f; t[3] = sy; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetSkewX(float* t, float a) +{ + t[0] = 1.0f; t[1] = 0.0f; + t[2] = tanf(a); t[3] = 1.0f; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetSkewY(float* t, float a) +{ + t[0] = 1.0f; t[1] = tanf(a); + t[2] = 0.0f; t[3] = 1.0f; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformSetRotation(float* t, float a) +{ + float cs = cosf(a), sn = sinf(a); + t[0] = cs; t[1] = sn; + t[2] = -sn; t[3] = cs; + t[4] = 0.0f; t[5] = 0.0f; +} + +static void nsvg__xformMultiply(float* t, float* s) +{ + float t0 = t[0] * s[0] + t[1] * s[2]; + float t2 = t[2] * s[0] + t[3] * s[2]; + float t4 = t[4] * s[0] + t[5] * s[2] + s[4]; + t[1] = t[0] * s[1] + t[1] * s[3]; + t[3] = t[2] * s[1] + t[3] * s[3]; + t[5] = t[4] * s[1] + t[5] * s[3] + s[5]; + t[0] = t0; + t[2] = t2; + t[4] = t4; +} + +static void nsvg__xformInverse(float* inv, float* t) +{ + double invdet, det = (double)t[0] * t[3] - (double)t[2] * t[1]; + if (det > -1e-6 && det < 1e-6) { + nsvg__xformIdentity(t); + return; + } + invdet = 1.0 / det; + inv[0] = (float)(t[3] * invdet); + inv[2] = (float)(-t[2] * invdet); + inv[4] = (float)(((double)t[2] * t[5] - (double)t[3] * t[4]) * invdet); + inv[1] = (float)(-t[1] * invdet); + inv[3] = (float)(t[0] * invdet); + inv[5] = (float)(((double)t[1] * t[4] - (double)t[0] * t[5]) * invdet); +} + +static void nsvg__xformPremultiply(float* t, float* s) +{ + float s2[6]; + memcpy(s2, s, sizeof(float)*6); + nsvg__xformMultiply(s2, t); + memcpy(t, s2, sizeof(float)*6); +} + +static void nsvg__xformPoint(float* dx, float* dy, float x, float y, float* t) +{ + *dx = x*t[0] + y*t[2] + t[4]; + *dy = x*t[1] + y*t[3] + t[5]; +} + +static void nsvg__xformVec(float* dx, float* dy, float x, float y, float* t) +{ + *dx = x*t[0] + y*t[2]; + *dy = x*t[1] + y*t[3]; +} + +#define NSVG_EPSILON (1e-12) + +static int nsvg__ptInBounds(float* pt, float* bounds) +{ + return pt[0] >= bounds[0] && pt[0] <= bounds[2] && pt[1] >= bounds[1] && pt[1] <= bounds[3]; +} + + +static double nsvg__evalBezier(double t, double p0, double p1, double p2, double p3) +{ + double it = 1.0-t; + return it*it*it*p0 + 3.0*it*it*t*p1 + 3.0*it*t*t*p2 + t*t*t*p3; +} + +static void nsvg__curveBounds(float* bounds, float* curve) +{ + int i, j, count; + double roots[2], a, b, c, b2ac, t, v; + float* v0 = &curve[0]; + float* v1 = &curve[2]; + float* v2 = &curve[4]; + float* v3 = &curve[6]; + + // Start the bounding box by end points + bounds[0] = nsvg__minf(v0[0], v3[0]); + bounds[1] = nsvg__minf(v0[1], v3[1]); + bounds[2] = nsvg__maxf(v0[0], v3[0]); + bounds[3] = nsvg__maxf(v0[1], v3[1]); + + // Bezier curve fits inside the convex hull of it's control points. + // If control points are inside the bounds, we're done. + if (nsvg__ptInBounds(v1, bounds) && nsvg__ptInBounds(v2, bounds)) + return; + + // Add bezier curve inflection points in X and Y. + for (i = 0; i < 2; i++) { + a = -3.0 * v0[i] + 9.0 * v1[i] - 9.0 * v2[i] + 3.0 * v3[i]; + b = 6.0 * v0[i] - 12.0 * v1[i] + 6.0 * v2[i]; + c = 3.0 * v1[i] - 3.0 * v0[i]; + count = 0; + if (fabs(a) < NSVG_EPSILON) { + if (fabs(b) > NSVG_EPSILON) { + t = -c / b; + if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) + roots[count++] = t; + } + } else { + b2ac = b*b - 4.0*c*a; + if (b2ac > NSVG_EPSILON) { + t = (-b + sqrt(b2ac)) / (2.0 * a); + if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) + roots[count++] = t; + t = (-b - sqrt(b2ac)) / (2.0 * a); + if (t > NSVG_EPSILON && t < 1.0-NSVG_EPSILON) + roots[count++] = t; + } + } + for (j = 0; j < count; j++) { + v = nsvg__evalBezier(roots[j], v0[i], v1[i], v2[i], v3[i]); + bounds[0+i] = nsvg__minf(bounds[0+i], (float)v); + bounds[2+i] = nsvg__maxf(bounds[2+i], (float)v); + } + } +} + +static NSVGparser* nsvg__createParser(void) +{ + NSVGparser* p; + p = (NSVGparser*)malloc(sizeof(NSVGparser)); + if (p == NULL) goto error; + memset(p, 0, sizeof(NSVGparser)); + + p->image = (NSVGimage*)malloc(sizeof(NSVGimage)); + if (p->image == NULL) goto error; + memset(p->image, 0, sizeof(NSVGimage)); + + // Init style + nsvg__xformIdentity(p->attr[0].xform); + memset(p->attr[0].id, 0, sizeof p->attr[0].id); + p->attr[0].fillColor = NSVG_RGB(0,0,0); + p->attr[0].strokeColor = NSVG_RGB(0,0,0); + p->attr[0].opacity = 1; + p->attr[0].fillOpacity = 1; + p->attr[0].strokeOpacity = 1; + p->attr[0].stopOpacity = 1; + p->attr[0].strokeWidth = 1; + p->attr[0].strokeLineJoin = NSVG_JOIN_MITER; + p->attr[0].strokeLineCap = NSVG_CAP_BUTT; + p->attr[0].miterLimit = 4; + p->attr[0].fillRule = NSVG_FILLRULE_NONZERO; + p->attr[0].hasFill = 1; + p->attr[0].visible = 1; + + return p; + +error: + if (p) { + if (p->image) free(p->image); + free(p); + } + return NULL; +} + +static void nsvg__deletePaths(NSVGpath* path) +{ + while (path) { + NSVGpath *next = path->next; + if (path->pts != NULL) + free(path->pts); + free(path); + path = next; + } +} + +static void nsvg__deletePaint(NSVGpaint* paint) +{ + if (paint->type == NSVG_PAINT_LINEAR_GRADIENT || paint->type == NSVG_PAINT_RADIAL_GRADIENT) + free(paint->gradient); +} + +static void nsvg__deleteGradientData(NSVGgradientData* grad) +{ + NSVGgradientData* next; + while (grad != NULL) { + next = grad->next; + free(grad->stops); + free(grad); + grad = next; + } +} + +static void nsvg__deleteParser(NSVGparser* p) +{ + if (p != NULL) { + nsvg__deletePaths(p->plist); + nsvg__deleteGradientData(p->gradients); + nsvgDelete(p->image); + free(p->pts); + free(p); + } +} + +static void nsvg__resetPath(NSVGparser* p) +{ + p->npts = 0; +} + +static void nsvg__addPoint(NSVGparser* p, float x, float y) +{ + if (p->npts+1 > p->cpts) { + p->cpts = p->cpts ? p->cpts*2 : 8; + p->pts = (float*)realloc(p->pts, p->cpts*2*sizeof(float)); + if (!p->pts) return; + } + p->pts[p->npts*2+0] = x; + p->pts[p->npts*2+1] = y; + p->npts++; +} + +static void nsvg__moveTo(NSVGparser* p, float x, float y) +{ + if (p->npts > 0) { + p->pts[(p->npts-1)*2+0] = x; + p->pts[(p->npts-1)*2+1] = y; + } else { + nsvg__addPoint(p, x, y); + } +} + +static void nsvg__lineTo(NSVGparser* p, float x, float y) +{ + float px,py, dx,dy; + if (p->npts > 0) { + px = p->pts[(p->npts-1)*2+0]; + py = p->pts[(p->npts-1)*2+1]; + dx = x - px; + dy = y - py; + nsvg__addPoint(p, px + dx/3.0f, py + dy/3.0f); + nsvg__addPoint(p, x - dx/3.0f, y - dy/3.0f); + nsvg__addPoint(p, x, y); + } +} + +static void nsvg__cubicBezTo(NSVGparser* p, float cpx1, float cpy1, float cpx2, float cpy2, float x, float y) +{ + if (p->npts > 0) { + nsvg__addPoint(p, cpx1, cpy1); + nsvg__addPoint(p, cpx2, cpy2); + nsvg__addPoint(p, x, y); + } +} + +static NSVGattrib* nsvg__getAttr(NSVGparser* p) +{ + return &p->attr[p->attrHead]; +} + +static void nsvg__pushAttr(NSVGparser* p) +{ + if (p->attrHead < NSVG_MAX_ATTR-1) { + p->attrHead++; + memcpy(&p->attr[p->attrHead], &p->attr[p->attrHead-1], sizeof(NSVGattrib)); + } +} + +static void nsvg__popAttr(NSVGparser* p) +{ + if (p->attrHead > 0) + p->attrHead--; +} + +static float nsvg__actualOrigX(NSVGparser* p) +{ + return p->viewMinx; +} + +static float nsvg__actualOrigY(NSVGparser* p) +{ + return p->viewMiny; +} + +static float nsvg__actualWidth(NSVGparser* p) +{ + return p->viewWidth; +} + +static float nsvg__actualHeight(NSVGparser* p) +{ + return p->viewHeight; +} + +static float nsvg__actualLength(NSVGparser* p) +{ + float w = nsvg__actualWidth(p), h = nsvg__actualHeight(p); + return sqrtf(w*w + h*h) / sqrtf(2.0f); +} + +static float nsvg__convertToPixels(NSVGparser* p, NSVGcoordinate c, float orig, float length) +{ + NSVGattrib* attr = nsvg__getAttr(p); + switch (c.units) { + case NSVG_UNITS_USER: return c.value; + case NSVG_UNITS_PX: return c.value; + case NSVG_UNITS_PT: return c.value / 72.0f * p->dpi; + case NSVG_UNITS_PC: return c.value / 6.0f * p->dpi; + case NSVG_UNITS_MM: return c.value / 25.4f * p->dpi; + case NSVG_UNITS_CM: return c.value / 2.54f * p->dpi; + case NSVG_UNITS_IN: return c.value * p->dpi; + case NSVG_UNITS_EM: return c.value * attr->fontSize; + case NSVG_UNITS_EX: return c.value * attr->fontSize * 0.52f; // x-height of Helvetica. + case NSVG_UNITS_PERCENT: return orig + c.value / 100.0f * length; + default: return c.value; + } + return c.value; +} + +static NSVGgradientData* nsvg__findGradientData(NSVGparser* p, const char* id) +{ + NSVGgradientData* grad = p->gradients; + if (id == NULL || *id == '\0') + return NULL; + while (grad != NULL) { + if (strcmp(grad->id, id) == 0) + return grad; + grad = grad->next; + } + return NULL; +} + +static NSVGgradient* nsvg__createGradient(NSVGparser* p, const char* id, const float* localBounds, char* paintType) +{ + NSVGattrib* attr = nsvg__getAttr(p); + NSVGgradientData* data = NULL; + NSVGgradientData* ref = NULL; + NSVGgradientStop* stops = NULL; + NSVGgradient* grad; + float ox, oy, sw, sh, sl; + int nstops = 0; + int refIter; + + data = nsvg__findGradientData(p, id); + if (data == NULL) return NULL; + + // TODO: use ref to fill in all unset values too. + ref = data; + refIter = 0; + while (ref != NULL) { + NSVGgradientData* nextRef = NULL; + if (stops == NULL && ref->stops != NULL) { + stops = ref->stops; + nstops = ref->nstops; + break; + } + nextRef = nsvg__findGradientData(p, ref->ref); + if (nextRef == ref) break; // prevent infite loops on malformed data + ref = nextRef; + refIter++; + if (refIter > 32) break; // prevent infite loops on malformed data + } + if (stops == NULL) return NULL; + + grad = (NSVGgradient*)malloc(sizeof(NSVGgradient) + sizeof(NSVGgradientStop)*(nstops-1)); + if (grad == NULL) return NULL; + + // The shape width and height. + if (data->units == NSVG_OBJECT_SPACE) { + ox = localBounds[0]; + oy = localBounds[1]; + sw = localBounds[2] - localBounds[0]; + sh = localBounds[3] - localBounds[1]; + } else { + ox = nsvg__actualOrigX(p); + oy = nsvg__actualOrigY(p); + sw = nsvg__actualWidth(p); + sh = nsvg__actualHeight(p); + } + sl = sqrtf(sw*sw + sh*sh) / sqrtf(2.0f); + + if (data->type == NSVG_PAINT_LINEAR_GRADIENT) { + float x1, y1, x2, y2, dx, dy; + x1 = nsvg__convertToPixels(p, data->linear.x1, ox, sw); + y1 = nsvg__convertToPixels(p, data->linear.y1, oy, sh); + x2 = nsvg__convertToPixels(p, data->linear.x2, ox, sw); + y2 = nsvg__convertToPixels(p, data->linear.y2, oy, sh); + // Calculate transform aligned to the line + dx = x2 - x1; + dy = y2 - y1; + grad->xform[0] = dy; grad->xform[1] = -dx; + grad->xform[2] = dx; grad->xform[3] = dy; + grad->xform[4] = x1; grad->xform[5] = y1; + } else { + float cx, cy, fx, fy, r; + cx = nsvg__convertToPixels(p, data->radial.cx, ox, sw); + cy = nsvg__convertToPixels(p, data->radial.cy, oy, sh); + fx = nsvg__convertToPixels(p, data->radial.fx, ox, sw); + fy = nsvg__convertToPixels(p, data->radial.fy, oy, sh); + r = nsvg__convertToPixels(p, data->radial.r, 0, sl); + // Calculate transform aligned to the circle + grad->xform[0] = r; grad->xform[1] = 0; + grad->xform[2] = 0; grad->xform[3] = r; + grad->xform[4] = cx; grad->xform[5] = cy; + grad->fx = fx / r; + grad->fy = fy / r; + } + + nsvg__xformMultiply(grad->xform, data->xform); + nsvg__xformMultiply(grad->xform, attr->xform); + + grad->spread = data->spread; + memcpy(grad->stops, stops, nstops*sizeof(NSVGgradientStop)); + grad->nstops = nstops; + + *paintType = data->type; + + return grad; +} + +static float nsvg__getAverageScale(float* t) +{ + float sx = sqrtf(t[0]*t[0] + t[2]*t[2]); + float sy = sqrtf(t[1]*t[1] + t[3]*t[3]); + return (sx + sy) * 0.5f; +} + +static void nsvg__getLocalBounds(float* bounds, NSVGshape *shape, float* xform) +{ + NSVGpath* path; + float curve[4*2], curveBounds[4]; + int i, first = 1; + for (path = shape->paths; path != NULL; path = path->next) { + nsvg__xformPoint(&curve[0], &curve[1], path->pts[0], path->pts[1], xform); + for (i = 0; i < path->npts-1; i += 3) { + nsvg__xformPoint(&curve[2], &curve[3], path->pts[(i+1)*2], path->pts[(i+1)*2+1], xform); + nsvg__xformPoint(&curve[4], &curve[5], path->pts[(i+2)*2], path->pts[(i+2)*2+1], xform); + nsvg__xformPoint(&curve[6], &curve[7], path->pts[(i+3)*2], path->pts[(i+3)*2+1], xform); + nsvg__curveBounds(curveBounds, curve); + if (first) { + bounds[0] = curveBounds[0]; + bounds[1] = curveBounds[1]; + bounds[2] = curveBounds[2]; + bounds[3] = curveBounds[3]; + first = 0; + } else { + bounds[0] = nsvg__minf(bounds[0], curveBounds[0]); + bounds[1] = nsvg__minf(bounds[1], curveBounds[1]); + bounds[2] = nsvg__maxf(bounds[2], curveBounds[2]); + bounds[3] = nsvg__maxf(bounds[3], curveBounds[3]); + } + curve[0] = curve[6]; + curve[1] = curve[7]; + } + } +} + +static void nsvg__addShape(NSVGparser* p) +{ + NSVGattrib* attr = nsvg__getAttr(p); + float scale = 1.0f; + NSVGshape* shape; + NSVGpath* path; + int i; + + if (p->plist == NULL) + return; + + shape = (NSVGshape*)malloc(sizeof(NSVGshape)); + if (shape == NULL) goto error; + memset(shape, 0, sizeof(NSVGshape)); + + memcpy(shape->id, attr->id, sizeof shape->id); + scale = nsvg__getAverageScale(attr->xform); + shape->strokeWidth = attr->strokeWidth * scale; + shape->strokeDashOffset = attr->strokeDashOffset * scale; + shape->strokeDashCount = (char)attr->strokeDashCount; + for (i = 0; i < attr->strokeDashCount; i++) + shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale; + shape->strokeLineJoin = attr->strokeLineJoin; + shape->strokeLineCap = attr->strokeLineCap; + shape->miterLimit = attr->miterLimit; + shape->fillRule = attr->fillRule; + shape->opacity = attr->opacity; + + shape->paths = p->plist; + p->plist = NULL; + + // Calculate shape bounds + shape->bounds[0] = shape->paths->bounds[0]; + shape->bounds[1] = shape->paths->bounds[1]; + shape->bounds[2] = shape->paths->bounds[2]; + shape->bounds[3] = shape->paths->bounds[3]; + for (path = shape->paths->next; path != NULL; path = path->next) { + shape->bounds[0] = nsvg__minf(shape->bounds[0], path->bounds[0]); + shape->bounds[1] = nsvg__minf(shape->bounds[1], path->bounds[1]); + shape->bounds[2] = nsvg__maxf(shape->bounds[2], path->bounds[2]); + shape->bounds[3] = nsvg__maxf(shape->bounds[3], path->bounds[3]); + } + + // Set fill + if (attr->hasFill == 0) { + shape->fill.type = NSVG_PAINT_NONE; + } else if (attr->hasFill == 1) { + shape->fill.type = NSVG_PAINT_COLOR; + shape->fill.color = attr->fillColor; + shape->fill.color |= (unsigned int)(attr->fillOpacity*255) << 24; + } else if (attr->hasFill == 2) { + float inv[6], localBounds[4]; + nsvg__xformInverse(inv, attr->xform); + nsvg__getLocalBounds(localBounds, shape, inv); + shape->fill.gradient = nsvg__createGradient(p, attr->fillGradient, localBounds, &shape->fill.type); + if (shape->fill.gradient == NULL) { + shape->fill.type = NSVG_PAINT_NONE; + } + } + + // Set stroke + if (attr->hasStroke == 0) { + shape->stroke.type = NSVG_PAINT_NONE; + } else if (attr->hasStroke == 1) { + shape->stroke.type = NSVG_PAINT_COLOR; + shape->stroke.color = attr->strokeColor; + shape->stroke.color |= (unsigned int)(attr->strokeOpacity*255) << 24; + } else if (attr->hasStroke == 2) { + float inv[6], localBounds[4]; + nsvg__xformInverse(inv, attr->xform); + nsvg__getLocalBounds(localBounds, shape, inv); + shape->stroke.gradient = nsvg__createGradient(p, attr->strokeGradient, localBounds, &shape->stroke.type); + if (shape->stroke.gradient == NULL) + shape->stroke.type = NSVG_PAINT_NONE; + } + + // Set flags + shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00); + + // Add to tail + if (p->image->shapes == NULL) + p->image->shapes = shape; + else + p->shapesTail->next = shape; + p->shapesTail = shape; + + return; + +error: + if (shape) free(shape); +} + +static void nsvg__addPath(NSVGparser* p, char closed) +{ + NSVGattrib* attr = nsvg__getAttr(p); + NSVGpath* path = NULL; + float bounds[4]; + float* curve; + int i; + + if (p->npts < 4) + return; + + if (closed) + nsvg__lineTo(p, p->pts[0], p->pts[1]); + + // Expect 1 + N*3 points (N = number of cubic bezier segments). + if ((p->npts % 3) != 1) + return; + + path = (NSVGpath*)malloc(sizeof(NSVGpath)); + if (path == NULL) goto error; + memset(path, 0, sizeof(NSVGpath)); + + path->pts = (float*)malloc(p->npts*2*sizeof(float)); + if (path->pts == NULL) goto error; + path->closed = closed; + path->npts = p->npts; + + // Transform path. + for (i = 0; i < p->npts; ++i) + nsvg__xformPoint(&path->pts[i*2], &path->pts[i*2+1], p->pts[i*2], p->pts[i*2+1], attr->xform); + + // Find bounds + for (i = 0; i < path->npts-1; i += 3) { + curve = &path->pts[i*2]; + nsvg__curveBounds(bounds, curve); + if (i == 0) { + path->bounds[0] = bounds[0]; + path->bounds[1] = bounds[1]; + path->bounds[2] = bounds[2]; + path->bounds[3] = bounds[3]; + } else { + path->bounds[0] = nsvg__minf(path->bounds[0], bounds[0]); + path->bounds[1] = nsvg__minf(path->bounds[1], bounds[1]); + path->bounds[2] = nsvg__maxf(path->bounds[2], bounds[2]); + path->bounds[3] = nsvg__maxf(path->bounds[3], bounds[3]); + } + } + + path->next = p->plist; + p->plist = path; + + return; + +error: + if (path != NULL) { + if (path->pts != NULL) free(path->pts); + free(path); + } +} + +// We roll our own string to float because the std library one uses locale and messes things up. +static double nsvg__atof(const char* s) +{ + char* cur = (char*)s; + char* end = NULL; + double res = 0.0, sign = 1.0; + long long intPart = 0, fracPart = 0; + char hasIntPart = 0, hasFracPart = 0; + + // Parse optional sign + if (*cur == '+') { + cur++; + } else if (*cur == '-') { + sign = -1; + cur++; + } + + // Parse integer part + if (nsvg__isdigit(*cur)) { + // Parse digit sequence + intPart = strtoll(cur, &end, 10); + if (cur != end) { + res = (double)intPart; + hasIntPart = 1; + cur = end; + } + } + + // Parse fractional part. + if (*cur == '.') { + cur++; // Skip '.' + if (nsvg__isdigit(*cur)) { + // Parse digit sequence + fracPart = strtoll(cur, &end, 10); + if (cur != end) { + res += (double)fracPart / pow(10.0, (double)(end - cur)); + hasFracPart = 1; + cur = end; + } + } + } + + // A valid number should have integer or fractional part. + if (!hasIntPart && !hasFracPart) + return 0.0; + + // Parse optional exponent + if (*cur == 'e' || *cur == 'E') { + long expPart = 0; + cur++; // skip 'E' + expPart = strtol(cur, &end, 10); // Parse digit sequence with sign + if (cur != end) { + res *= pow(10.0, (double)expPart); + } + } + + return res * sign; +} + + +static const char* nsvg__parseNumber(const char* s, char* it, const int size) +{ + const int last = size-1; + int i = 0; + + // sign + if (*s == '-' || *s == '+') { + if (i < last) it[i++] = *s; + s++; + } + // integer part + while (*s && nsvg__isdigit(*s)) { + if (i < last) it[i++] = *s; + s++; + } + if (*s == '.') { + // decimal point + if (i < last) it[i++] = *s; + s++; + // fraction part + while (*s && nsvg__isdigit(*s)) { + if (i < last) it[i++] = *s; + s++; + } + } + // exponent + if ((*s == 'e' || *s == 'E') && (s[1] != 'm' && s[1] != 'x')) { + if (i < last) it[i++] = *s; + s++; + if (*s == '-' || *s == '+') { + if (i < last) it[i++] = *s; + s++; + } + while (*s && nsvg__isdigit(*s)) { + if (i < last) it[i++] = *s; + s++; + } + } + it[i] = '\0'; + + return s; +} + +static const char* nsvg__getNextPathItem(const char* s, char* it) +{ + it[0] = '\0'; + // Skip white spaces and commas + while (*s && (nsvg__isspace(*s) || *s == ',')) s++; + if (!*s) return s; + if (*s == '-' || *s == '+' || *s == '.' || nsvg__isdigit(*s)) { + s = nsvg__parseNumber(s, it, 64); + } else { + // Parse command + it[0] = *s++; + it[1] = '\0'; + return s; + } + + return s; +} + +static unsigned int nsvg__parseColorHex(const char* str) +{ + unsigned int r=0, g=0, b=0; + if (sscanf(str, "#%2x%2x%2x", &r, &g, &b) == 3 ) // 2 digit hex + return NSVG_RGB(r, g, b); + if (sscanf(str, "#%1x%1x%1x", &r, &g, &b) == 3 ) // 1 digit hex, e.g. #abc -> 0xccbbaa + return NSVG_RGB(r*17, g*17, b*17); // same effect as (r<<4|r), (g<<4|g), .. + return NSVG_RGB(128, 128, 128); +} + +// Parse rgb color. The pointer 'str' must point at "rgb(" (4+ characters). +// This function returns gray (rgb(128, 128, 128) == '#808080') on parse errors +// for backwards compatibility. Note: other image viewers return black instead. + +static unsigned int nsvg__parseColorRGB(const char* str) +{ + int i; + unsigned int rgbi[3]; + float rgbf[3]; + // try decimal integers first + if (sscanf(str, "rgb(%u, %u, %u)", &rgbi[0], &rgbi[1], &rgbi[2]) != 3) { + // integers failed, try percent values (float, locale independent) + const char delimiter[3] = {',', ',', ')'}; + str += 4; // skip "rgb(" + for (i = 0; i < 3; i++) { + while (*str && (nsvg__isspace(*str))) str++; // skip leading spaces + if (*str == '+') str++; // skip '+' (don't allow '-') + if (!*str) break; + rgbf[i] = nsvg__atof(str); + + // Note 1: it would be great if nsvg__atof() returned how many + // bytes it consumed but it doesn't. We need to skip the number, + // the '%' character, spaces, and the delimiter ',' or ')'. + + // Note 2: The following code does not allow values like "33.%", + // i.e. a decimal point w/o fractional part, but this is consistent + // with other image viewers, e.g. firefox, chrome, eog, gimp. + + while (*str && nsvg__isdigit(*str)) str++; // skip integer part + if (*str == '.') { + str++; + if (!nsvg__isdigit(*str)) break; // error: no digit after '.' + while (*str && nsvg__isdigit(*str)) str++; // skip fractional part + } + if (*str == '%') str++; else break; + while (nsvg__isspace(*str)) str++; + if (*str == delimiter[i]) str++; + else break; + } + if (i == 3) { + rgbi[0] = roundf(rgbf[0] * 2.55f); + rgbi[1] = roundf(rgbf[1] * 2.55f); + rgbi[2] = roundf(rgbf[2] * 2.55f); + } else { + rgbi[0] = rgbi[1] = rgbi[2] = 128; + } + } + // clip values as the CSS spec requires + for (i = 0; i < 3; i++) { + if (rgbi[i] > 255) rgbi[i] = 255; + } + return NSVG_RGB(rgbi[0], rgbi[1], rgbi[2]); +} + +typedef struct NSVGNamedColor { + const char* name; + unsigned int color; +} NSVGNamedColor; + +NSVGNamedColor nsvg__colors[] = { + + { "red", NSVG_RGB(255, 0, 0) }, + { "green", NSVG_RGB( 0, 128, 0) }, + { "blue", NSVG_RGB( 0, 0, 255) }, + { "yellow", NSVG_RGB(255, 255, 0) }, + { "cyan", NSVG_RGB( 0, 255, 255) }, + { "magenta", NSVG_RGB(255, 0, 255) }, + { "black", NSVG_RGB( 0, 0, 0) }, + { "grey", NSVG_RGB(128, 128, 128) }, + { "gray", NSVG_RGB(128, 128, 128) }, + { "white", NSVG_RGB(255, 255, 255) }, + +#ifdef NANOSVG_ALL_COLOR_KEYWORDS + { "aliceblue", NSVG_RGB(240, 248, 255) }, + { "antiquewhite", NSVG_RGB(250, 235, 215) }, + { "aqua", NSVG_RGB( 0, 255, 255) }, + { "aquamarine", NSVG_RGB(127, 255, 212) }, + { "azure", NSVG_RGB(240, 255, 255) }, + { "beige", NSVG_RGB(245, 245, 220) }, + { "bisque", NSVG_RGB(255, 228, 196) }, + { "blanchedalmond", NSVG_RGB(255, 235, 205) }, + { "blueviolet", NSVG_RGB(138, 43, 226) }, + { "brown", NSVG_RGB(165, 42, 42) }, + { "burlywood", NSVG_RGB(222, 184, 135) }, + { "cadetblue", NSVG_RGB( 95, 158, 160) }, + { "chartreuse", NSVG_RGB(127, 255, 0) }, + { "chocolate", NSVG_RGB(210, 105, 30) }, + { "coral", NSVG_RGB(255, 127, 80) }, + { "cornflowerblue", NSVG_RGB(100, 149, 237) }, + { "cornsilk", NSVG_RGB(255, 248, 220) }, + { "crimson", NSVG_RGB(220, 20, 60) }, + { "darkblue", NSVG_RGB( 0, 0, 139) }, + { "darkcyan", NSVG_RGB( 0, 139, 139) }, + { "darkgoldenrod", NSVG_RGB(184, 134, 11) }, + { "darkgray", NSVG_RGB(169, 169, 169) }, + { "darkgreen", NSVG_RGB( 0, 100, 0) }, + { "darkgrey", NSVG_RGB(169, 169, 169) }, + { "darkkhaki", NSVG_RGB(189, 183, 107) }, + { "darkmagenta", NSVG_RGB(139, 0, 139) }, + { "darkolivegreen", NSVG_RGB( 85, 107, 47) }, + { "darkorange", NSVG_RGB(255, 140, 0) }, + { "darkorchid", NSVG_RGB(153, 50, 204) }, + { "darkred", NSVG_RGB(139, 0, 0) }, + { "darksalmon", NSVG_RGB(233, 150, 122) }, + { "darkseagreen", NSVG_RGB(143, 188, 143) }, + { "darkslateblue", NSVG_RGB( 72, 61, 139) }, + { "darkslategray", NSVG_RGB( 47, 79, 79) }, + { "darkslategrey", NSVG_RGB( 47, 79, 79) }, + { "darkturquoise", NSVG_RGB( 0, 206, 209) }, + { "darkviolet", NSVG_RGB(148, 0, 211) }, + { "deeppink", NSVG_RGB(255, 20, 147) }, + { "deepskyblue", NSVG_RGB( 0, 191, 255) }, + { "dimgray", NSVG_RGB(105, 105, 105) }, + { "dimgrey", NSVG_RGB(105, 105, 105) }, + { "dodgerblue", NSVG_RGB( 30, 144, 255) }, + { "firebrick", NSVG_RGB(178, 34, 34) }, + { "floralwhite", NSVG_RGB(255, 250, 240) }, + { "forestgreen", NSVG_RGB( 34, 139, 34) }, + { "fuchsia", NSVG_RGB(255, 0, 255) }, + { "gainsboro", NSVG_RGB(220, 220, 220) }, + { "ghostwhite", NSVG_RGB(248, 248, 255) }, + { "gold", NSVG_RGB(255, 215, 0) }, + { "goldenrod", NSVG_RGB(218, 165, 32) }, + { "greenyellow", NSVG_RGB(173, 255, 47) }, + { "honeydew", NSVG_RGB(240, 255, 240) }, + { "hotpink", NSVG_RGB(255, 105, 180) }, + { "indianred", NSVG_RGB(205, 92, 92) }, + { "indigo", NSVG_RGB( 75, 0, 130) }, + { "ivory", NSVG_RGB(255, 255, 240) }, + { "khaki", NSVG_RGB(240, 230, 140) }, + { "lavender", NSVG_RGB(230, 230, 250) }, + { "lavenderblush", NSVG_RGB(255, 240, 245) }, + { "lawngreen", NSVG_RGB(124, 252, 0) }, + { "lemonchiffon", NSVG_RGB(255, 250, 205) }, + { "lightblue", NSVG_RGB(173, 216, 230) }, + { "lightcoral", NSVG_RGB(240, 128, 128) }, + { "lightcyan", NSVG_RGB(224, 255, 255) }, + { "lightgoldenrodyellow", NSVG_RGB(250, 250, 210) }, + { "lightgray", NSVG_RGB(211, 211, 211) }, + { "lightgreen", NSVG_RGB(144, 238, 144) }, + { "lightgrey", NSVG_RGB(211, 211, 211) }, + { "lightpink", NSVG_RGB(255, 182, 193) }, + { "lightsalmon", NSVG_RGB(255, 160, 122) }, + { "lightseagreen", NSVG_RGB( 32, 178, 170) }, + { "lightskyblue", NSVG_RGB(135, 206, 250) }, + { "lightslategray", NSVG_RGB(119, 136, 153) }, + { "lightslategrey", NSVG_RGB(119, 136, 153) }, + { "lightsteelblue", NSVG_RGB(176, 196, 222) }, + { "lightyellow", NSVG_RGB(255, 255, 224) }, + { "lime", NSVG_RGB( 0, 255, 0) }, + { "limegreen", NSVG_RGB( 50, 205, 50) }, + { "linen", NSVG_RGB(250, 240, 230) }, + { "maroon", NSVG_RGB(128, 0, 0) }, + { "mediumaquamarine", NSVG_RGB(102, 205, 170) }, + { "mediumblue", NSVG_RGB( 0, 0, 205) }, + { "mediumorchid", NSVG_RGB(186, 85, 211) }, + { "mediumpurple", NSVG_RGB(147, 112, 219) }, + { "mediumseagreen", NSVG_RGB( 60, 179, 113) }, + { "mediumslateblue", NSVG_RGB(123, 104, 238) }, + { "mediumspringgreen", NSVG_RGB( 0, 250, 154) }, + { "mediumturquoise", NSVG_RGB( 72, 209, 204) }, + { "mediumvioletred", NSVG_RGB(199, 21, 133) }, + { "midnightblue", NSVG_RGB( 25, 25, 112) }, + { "mintcream", NSVG_RGB(245, 255, 250) }, + { "mistyrose", NSVG_RGB(255, 228, 225) }, + { "moccasin", NSVG_RGB(255, 228, 181) }, + { "navajowhite", NSVG_RGB(255, 222, 173) }, + { "navy", NSVG_RGB( 0, 0, 128) }, + { "oldlace", NSVG_RGB(253, 245, 230) }, + { "olive", NSVG_RGB(128, 128, 0) }, + { "olivedrab", NSVG_RGB(107, 142, 35) }, + { "orange", NSVG_RGB(255, 165, 0) }, + { "orangered", NSVG_RGB(255, 69, 0) }, + { "orchid", NSVG_RGB(218, 112, 214) }, + { "palegoldenrod", NSVG_RGB(238, 232, 170) }, + { "palegreen", NSVG_RGB(152, 251, 152) }, + { "paleturquoise", NSVG_RGB(175, 238, 238) }, + { "palevioletred", NSVG_RGB(219, 112, 147) }, + { "papayawhip", NSVG_RGB(255, 239, 213) }, + { "peachpuff", NSVG_RGB(255, 218, 185) }, + { "peru", NSVG_RGB(205, 133, 63) }, + { "pink", NSVG_RGB(255, 192, 203) }, + { "plum", NSVG_RGB(221, 160, 221) }, + { "powderblue", NSVG_RGB(176, 224, 230) }, + { "purple", NSVG_RGB(128, 0, 128) }, + { "rosybrown", NSVG_RGB(188, 143, 143) }, + { "royalblue", NSVG_RGB( 65, 105, 225) }, + { "saddlebrown", NSVG_RGB(139, 69, 19) }, + { "salmon", NSVG_RGB(250, 128, 114) }, + { "sandybrown", NSVG_RGB(244, 164, 96) }, + { "seagreen", NSVG_RGB( 46, 139, 87) }, + { "seashell", NSVG_RGB(255, 245, 238) }, + { "sienna", NSVG_RGB(160, 82, 45) }, + { "silver", NSVG_RGB(192, 192, 192) }, + { "skyblue", NSVG_RGB(135, 206, 235) }, + { "slateblue", NSVG_RGB(106, 90, 205) }, + { "slategray", NSVG_RGB(112, 128, 144) }, + { "slategrey", NSVG_RGB(112, 128, 144) }, + { "snow", NSVG_RGB(255, 250, 250) }, + { "springgreen", NSVG_RGB( 0, 255, 127) }, + { "steelblue", NSVG_RGB( 70, 130, 180) }, + { "tan", NSVG_RGB(210, 180, 140) }, + { "teal", NSVG_RGB( 0, 128, 128) }, + { "thistle", NSVG_RGB(216, 191, 216) }, + { "tomato", NSVG_RGB(255, 99, 71) }, + { "turquoise", NSVG_RGB( 64, 224, 208) }, + { "violet", NSVG_RGB(238, 130, 238) }, + { "wheat", NSVG_RGB(245, 222, 179) }, + { "whitesmoke", NSVG_RGB(245, 245, 245) }, + { "yellowgreen", NSVG_RGB(154, 205, 50) }, +#endif +}; + +static unsigned int nsvg__parseColorName(const char* str) +{ + int i, ncolors = sizeof(nsvg__colors) / sizeof(NSVGNamedColor); + + for (i = 0; i < ncolors; i++) { + if (strcmp(nsvg__colors[i].name, str) == 0) { + return nsvg__colors[i].color; + } + } + + return NSVG_RGB(128, 128, 128); +} + +static unsigned int nsvg__parseColor(const char* str) +{ + size_t len = 0; + while(*str == ' ') ++str; + len = strlen(str); + if (len >= 1 && *str == '#') + return nsvg__parseColorHex(str); + else if (len >= 4 && str[0] == 'r' && str[1] == 'g' && str[2] == 'b' && str[3] == '(') + return nsvg__parseColorRGB(str); + return nsvg__parseColorName(str); +} + +static float nsvg__parseOpacity(const char* str) +{ + float val = nsvg__atof(str); + if (val < 0.0f) val = 0.0f; + if (val > 1.0f) val = 1.0f; + return val; +} + +static float nsvg__parseMiterLimit(const char* str) +{ + float val = nsvg__atof(str); + if (val < 0.0f) val = 0.0f; + return val; +} + +static int nsvg__parseUnits(const char* units) +{ + if (units[0] == 'p' && units[1] == 'x') + return NSVG_UNITS_PX; + else if (units[0] == 'p' && units[1] == 't') + return NSVG_UNITS_PT; + else if (units[0] == 'p' && units[1] == 'c') + return NSVG_UNITS_PC; + else if (units[0] == 'm' && units[1] == 'm') + return NSVG_UNITS_MM; + else if (units[0] == 'c' && units[1] == 'm') + return NSVG_UNITS_CM; + else if (units[0] == 'i' && units[1] == 'n') + return NSVG_UNITS_IN; + else if (units[0] == '%') + return NSVG_UNITS_PERCENT; + else if (units[0] == 'e' && units[1] == 'm') + return NSVG_UNITS_EM; + else if (units[0] == 'e' && units[1] == 'x') + return NSVG_UNITS_EX; + return NSVG_UNITS_USER; +} + +static int nsvg__isCoordinate(const char* s) +{ + // optional sign + if (*s == '-' || *s == '+') + s++; + // must have at least one digit, or start by a dot + return (nsvg__isdigit(*s) || *s == '.'); +} + +static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str) +{ + NSVGcoordinate coord = {0, NSVG_UNITS_USER}; + char buf[64]; + coord.units = nsvg__parseUnits(nsvg__parseNumber(str, buf, 64)); + coord.value = nsvg__atof(buf); + return coord; +} + +static NSVGcoordinate nsvg__coord(float v, int units) +{ + NSVGcoordinate coord = {v, units}; + return coord; +} + +static float nsvg__parseCoordinate(NSVGparser* p, const char* str, float orig, float length) +{ + NSVGcoordinate coord = nsvg__parseCoordinateRaw(str); + return nsvg__convertToPixels(p, coord, orig, length); +} + +static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int* na) +{ + const char* end; + const char* ptr; + char it[64]; + + *na = 0; + ptr = str; + while (*ptr && *ptr != '(') ++ptr; + if (*ptr == 0) + return 1; + end = ptr; + while (*end && *end != ')') ++end; + if (*end == 0) + return 1; + + while (ptr < end) { + if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) { + if (*na >= maxNa) return 0; + ptr = nsvg__parseNumber(ptr, it, 64); + args[(*na)++] = (float)nsvg__atof(it); + } else { + ++ptr; + } + } + return (int)(end - str); +} + + +static int nsvg__parseMatrix(float* xform, const char* str) +{ + float t[6]; + int na = 0; + int len = nsvg__parseTransformArgs(str, t, 6, &na); + if (na != 6) return len; + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseTranslate(float* xform, const char* str) +{ + float args[2]; + float t[6]; + int na = 0; + int len = nsvg__parseTransformArgs(str, args, 2, &na); + if (na == 1) args[1] = 0.0; + + nsvg__xformSetTranslation(t, args[0], args[1]); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseScale(float* xform, const char* str) +{ + float args[2]; + int na = 0; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 2, &na); + if (na == 1) args[1] = args[0]; + nsvg__xformSetScale(t, args[0], args[1]); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseSkewX(float* xform, const char* str) +{ + float args[1]; + int na = 0; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 1, &na); + nsvg__xformSetSkewX(t, args[0]/180.0f*NSVG_PI); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseSkewY(float* xform, const char* str) +{ + float args[1]; + int na = 0; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 1, &na); + nsvg__xformSetSkewY(t, args[0]/180.0f*NSVG_PI); + memcpy(xform, t, sizeof(float)*6); + return len; +} + +static int nsvg__parseRotate(float* xform, const char* str) +{ + float args[3]; + int na = 0; + float m[6]; + float t[6]; + int len = nsvg__parseTransformArgs(str, args, 3, &na); + if (na == 1) + args[1] = args[2] = 0.0f; + nsvg__xformIdentity(m); + + if (na > 1) { + nsvg__xformSetTranslation(t, -args[1], -args[2]); + nsvg__xformMultiply(m, t); + } + + nsvg__xformSetRotation(t, args[0]/180.0f*NSVG_PI); + nsvg__xformMultiply(m, t); + + if (na > 1) { + nsvg__xformSetTranslation(t, args[1], args[2]); + nsvg__xformMultiply(m, t); + } + + memcpy(xform, m, sizeof(float)*6); + + return len; +} + +static void nsvg__parseTransform(float* xform, const char* str) +{ + float t[6]; + int len; + nsvg__xformIdentity(xform); + while (*str) + { + if (strncmp(str, "matrix", 6) == 0) + len = nsvg__parseMatrix(t, str); + else if (strncmp(str, "translate", 9) == 0) + len = nsvg__parseTranslate(t, str); + else if (strncmp(str, "scale", 5) == 0) + len = nsvg__parseScale(t, str); + else if (strncmp(str, "rotate", 6) == 0) + len = nsvg__parseRotate(t, str); + else if (strncmp(str, "skewX", 5) == 0) + len = nsvg__parseSkewX(t, str); + else if (strncmp(str, "skewY", 5) == 0) + len = nsvg__parseSkewY(t, str); + else{ + ++str; + continue; + } + if (len != 0) { + str += len; + } else { + ++str; + continue; + } + + nsvg__xformPremultiply(xform, t); + } +} + +static void nsvg__parseUrl(char* id, const char* str) +{ + int i = 0; + str += 4; // "url("; + if (*str && *str == '#') + str++; + while (i < 63 && *str && *str != ')') { + id[i] = *str++; + i++; + } + id[i] = '\0'; +} + +static char nsvg__parseLineCap(const char* str) +{ + if (strcmp(str, "butt") == 0) + return NSVG_CAP_BUTT; + else if (strcmp(str, "round") == 0) + return NSVG_CAP_ROUND; + else if (strcmp(str, "square") == 0) + return NSVG_CAP_SQUARE; + // TODO: handle inherit. + return NSVG_CAP_BUTT; +} + +static char nsvg__parseLineJoin(const char* str) +{ + if (strcmp(str, "miter") == 0) + return NSVG_JOIN_MITER; + else if (strcmp(str, "round") == 0) + return NSVG_JOIN_ROUND; + else if (strcmp(str, "bevel") == 0) + return NSVG_JOIN_BEVEL; + // TODO: handle inherit. + return NSVG_JOIN_MITER; +} + +static char nsvg__parseFillRule(const char* str) +{ + if (strcmp(str, "nonzero") == 0) + return NSVG_FILLRULE_NONZERO; + else if (strcmp(str, "evenodd") == 0) + return NSVG_FILLRULE_EVENODD; + // TODO: handle inherit. + return NSVG_FILLRULE_NONZERO; +} + +static const char* nsvg__getNextDashItem(const char* s, char* it) +{ + int n = 0; + it[0] = '\0'; + // Skip white spaces and commas + while (*s && (nsvg__isspace(*s) || *s == ',')) s++; + // Advance until whitespace, comma or end. + while (*s && (!nsvg__isspace(*s) && *s != ',')) { + if (n < 63) + it[n++] = *s; + s++; + } + it[n++] = '\0'; + return s; +} + +static int nsvg__parseStrokeDashArray(NSVGparser* p, const char* str, float* strokeDashArray) +{ + char item[64]; + int count = 0, i; + float sum = 0.0f; + + // Handle "none" + if (str[0] == 'n') + return 0; + + // Parse dashes + while (*str) { + str = nsvg__getNextDashItem(str, item); + if (!*item) break; + if (count < NSVG_MAX_DASHES) + strokeDashArray[count++] = fabsf(nsvg__parseCoordinate(p, item, 0.0f, nsvg__actualLength(p))); + } + + for (i = 0; i < count; i++) + sum += strokeDashArray[i]; + if (sum <= 1e-6f) + count = 0; + + return count; +} + +static void nsvg__parseStyle(NSVGparser* p, const char* str); + +static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value) +{ + float xform[6]; + NSVGattrib* attr = nsvg__getAttr(p); + if (!attr) return 0; + + if (strcmp(name, "style") == 0) { + nsvg__parseStyle(p, value); + } else if (strcmp(name, "display") == 0) { + if (strcmp(value, "none") == 0) + attr->visible = 0; + // Don't reset ->visible on display:inline, one display:none hides the whole subtree + + } else if (strcmp(name, "fill") == 0) { + if (strcmp(value, "none") == 0) { + attr->hasFill = 0; + } else if (strncmp(value, "url(", 4) == 0) { + attr->hasFill = 2; + nsvg__parseUrl(attr->fillGradient, value); + } else { + attr->hasFill = 1; + attr->fillColor = nsvg__parseColor(value); + } + } else if (strcmp(name, "opacity") == 0) { + attr->opacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "fill-opacity") == 0) { + attr->fillOpacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "stroke") == 0) { + if (strcmp(value, "none") == 0) { + attr->hasStroke = 0; + } else if (strncmp(value, "url(", 4) == 0) { + attr->hasStroke = 2; + nsvg__parseUrl(attr->strokeGradient, value); + } else { + attr->hasStroke = 1; + attr->strokeColor = nsvg__parseColor(value); + } + } else if (strcmp(name, "stroke-width") == 0) { + attr->strokeWidth = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); + } else if (strcmp(name, "stroke-dasharray") == 0) { + attr->strokeDashCount = nsvg__parseStrokeDashArray(p, value, attr->strokeDashArray); + } else if (strcmp(name, "stroke-dashoffset") == 0) { + attr->strokeDashOffset = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); + } else if (strcmp(name, "stroke-opacity") == 0) { + attr->strokeOpacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "stroke-linecap") == 0) { + attr->strokeLineCap = nsvg__parseLineCap(value); + } else if (strcmp(name, "stroke-linejoin") == 0) { + attr->strokeLineJoin = nsvg__parseLineJoin(value); + } else if (strcmp(name, "stroke-miterlimit") == 0) { + attr->miterLimit = nsvg__parseMiterLimit(value); + } else if (strcmp(name, "fill-rule") == 0) { + attr->fillRule = nsvg__parseFillRule(value); + } else if (strcmp(name, "font-size") == 0) { + attr->fontSize = nsvg__parseCoordinate(p, value, 0.0f, nsvg__actualLength(p)); + } else if (strcmp(name, "transform") == 0) { + nsvg__parseTransform(xform, value); + nsvg__xformPremultiply(attr->xform, xform); + } else if (strcmp(name, "stop-color") == 0) { + attr->stopColor = nsvg__parseColor(value); + } else if (strcmp(name, "stop-opacity") == 0) { + attr->stopOpacity = nsvg__parseOpacity(value); + } else if (strcmp(name, "offset") == 0) { + attr->stopOffset = nsvg__parseCoordinate(p, value, 0.0f, 1.0f); + } else if (strcmp(name, "id") == 0) { + strncpy(attr->id, value, 63); + attr->id[63] = '\0'; + } else { + return 0; + } + return 1; +} + +static int nsvg__parseNameValue(NSVGparser* p, const char* start, const char* end) +{ + const char* str; + const char* val; + char name[512]; + char value[512]; + int n; + + str = start; + while (str < end && *str != ':') ++str; + + val = str; + + // Right Trim + while (str > start && (*str == ':' || nsvg__isspace(*str))) --str; + ++str; + + n = (int)(str - start); + if (n > 511) n = 511; + if (n) memcpy(name, start, n); + name[n] = 0; + + while (val < end && (*val == ':' || nsvg__isspace(*val))) ++val; + + n = (int)(end - val); + if (n > 511) n = 511; + if (n) memcpy(value, val, n); + value[n] = 0; + + return nsvg__parseAttr(p, name, value); +} + +static void nsvg__parseStyle(NSVGparser* p, const char* str) +{ + const char* start; + const char* end; + + while (*str) { + // Left Trim + while(*str && nsvg__isspace(*str)) ++str; + start = str; + while(*str && *str != ';') ++str; + end = str; + + // Right Trim + while (end > start && (*end == ';' || nsvg__isspace(*end))) --end; + ++end; + + nsvg__parseNameValue(p, start, end); + if (*str) ++str; + } +} + +static void nsvg__parseAttribs(NSVGparser* p, const char** attr) +{ + int i; + for (i = 0; attr[i]; i += 2) + { + if (strcmp(attr[i], "style") == 0) + nsvg__parseStyle(p, attr[i + 1]); + else + nsvg__parseAttr(p, attr[i], attr[i + 1]); + } +} + +static int nsvg__getArgsPerElement(char cmd) +{ + switch (cmd) { + case 'v': + case 'V': + case 'h': + case 'H': + return 1; + case 'm': + case 'M': + case 'l': + case 'L': + case 't': + case 'T': + return 2; + case 'q': + case 'Q': + case 's': + case 'S': + return 4; + case 'c': + case 'C': + return 6; + case 'a': + case 'A': + return 7; + case 'z': + case 'Z': + return 0; + } + return -1; +} + +static void nsvg__pathMoveTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) { + *cpx += args[0]; + *cpy += args[1]; + } else { + *cpx = args[0]; + *cpy = args[1]; + } + nsvg__moveTo(p, *cpx, *cpy); +} + +static void nsvg__pathLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) { + *cpx += args[0]; + *cpy += args[1]; + } else { + *cpx = args[0]; + *cpy = args[1]; + } + nsvg__lineTo(p, *cpx, *cpy); +} + +static void nsvg__pathHLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) + *cpx += args[0]; + else + *cpx = args[0]; + nsvg__lineTo(p, *cpx, *cpy); +} + +static void nsvg__pathVLineTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + if (rel) + *cpy += args[0]; + else + *cpy = args[0]; + nsvg__lineTo(p, *cpx, *cpy); +} + +static void nsvg__pathCubicBezTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x2, y2, cx1, cy1, cx2, cy2; + + if (rel) { + cx1 = *cpx + args[0]; + cy1 = *cpy + args[1]; + cx2 = *cpx + args[2]; + cy2 = *cpy + args[3]; + x2 = *cpx + args[4]; + y2 = *cpy + args[5]; + } else { + cx1 = args[0]; + cy1 = args[1]; + cx2 = args[2]; + cy2 = args[3]; + x2 = args[4]; + y2 = args[5]; + } + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx2; + *cpy2 = cy2; + *cpx = x2; + *cpy = y2; +} + +static void nsvg__pathCubicBezShortTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x1, y1, x2, y2, cx1, cy1, cx2, cy2; + + x1 = *cpx; + y1 = *cpy; + if (rel) { + cx2 = *cpx + args[0]; + cy2 = *cpy + args[1]; + x2 = *cpx + args[2]; + y2 = *cpy + args[3]; + } else { + cx2 = args[0]; + cy2 = args[1]; + x2 = args[2]; + y2 = args[3]; + } + + cx1 = 2*x1 - *cpx2; + cy1 = 2*y1 - *cpy2; + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx2; + *cpy2 = cy2; + *cpx = x2; + *cpy = y2; +} + +static void nsvg__pathQuadBezTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x1, y1, x2, y2, cx, cy; + float cx1, cy1, cx2, cy2; + + x1 = *cpx; + y1 = *cpy; + if (rel) { + cx = *cpx + args[0]; + cy = *cpy + args[1]; + x2 = *cpx + args[2]; + y2 = *cpy + args[3]; + } else { + cx = args[0]; + cy = args[1]; + x2 = args[2]; + y2 = args[3]; + } + + // Convert to cubic bezier + cx1 = x1 + 2.0f/3.0f*(cx - x1); + cy1 = y1 + 2.0f/3.0f*(cy - y1); + cx2 = x2 + 2.0f/3.0f*(cx - x2); + cy2 = y2 + 2.0f/3.0f*(cy - y2); + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx; + *cpy2 = cy; + *cpx = x2; + *cpy = y2; +} + +static void nsvg__pathQuadBezShortTo(NSVGparser* p, float* cpx, float* cpy, + float* cpx2, float* cpy2, float* args, int rel) +{ + float x1, y1, x2, y2, cx, cy; + float cx1, cy1, cx2, cy2; + + x1 = *cpx; + y1 = *cpy; + if (rel) { + x2 = *cpx + args[0]; + y2 = *cpy + args[1]; + } else { + x2 = args[0]; + y2 = args[1]; + } + + cx = 2*x1 - *cpx2; + cy = 2*y1 - *cpy2; + + // Convert to cubix bezier + cx1 = x1 + 2.0f/3.0f*(cx - x1); + cy1 = y1 + 2.0f/3.0f*(cy - y1); + cx2 = x2 + 2.0f/3.0f*(cx - x2); + cy2 = y2 + 2.0f/3.0f*(cy - y2); + + nsvg__cubicBezTo(p, cx1,cy1, cx2,cy2, x2,y2); + + *cpx2 = cx; + *cpy2 = cy; + *cpx = x2; + *cpy = y2; +} + +static float nsvg__sqr(float x) { return x*x; } +static float nsvg__vmag(float x, float y) { return sqrtf(x*x + y*y); } + +static float nsvg__vecrat(float ux, float uy, float vx, float vy) +{ + return (ux*vx + uy*vy) / (nsvg__vmag(ux,uy) * nsvg__vmag(vx,vy)); +} + +static float nsvg__vecang(float ux, float uy, float vx, float vy) +{ + float r = nsvg__vecrat(ux,uy, vx,vy); + if (r < -1.0f) r = -1.0f; + if (r > 1.0f) r = 1.0f; + return ((ux*vy < uy*vx) ? -1.0f : 1.0f) * acosf(r); +} + +static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, int rel) +{ + // Ported from canvg (https://code.google.com/p/canvg/) + float rx, ry, rotx; + float x1, y1, x2, y2, cx, cy, dx, dy, d; + float x1p, y1p, cxp, cyp, s, sa, sb; + float ux, uy, vx, vy, a1, da; + float x, y, tanx, tany, a, px = 0, py = 0, ptanx = 0, ptany = 0, t[6]; + float sinrx, cosrx; + int fa, fs; + int i, ndivs; + float hda, kappa; + + rx = fabsf(args[0]); // y radius + ry = fabsf(args[1]); // x radius + rotx = args[2] / 180.0f * NSVG_PI; // x rotation angle + fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc + fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction + x1 = *cpx; // start point + y1 = *cpy; + if (rel) { // end point + x2 = *cpx + args[5]; + y2 = *cpy + args[6]; + } else { + x2 = args[5]; + y2 = args[6]; + } + + dx = x1 - x2; + dy = y1 - y2; + d = sqrtf(dx*dx + dy*dy); + if (d < 1e-6f || rx < 1e-6f || ry < 1e-6f) { + // The arc degenerates to a line + nsvg__lineTo(p, x2, y2); + *cpx = x2; + *cpy = y2; + return; + } + + sinrx = sinf(rotx); + cosrx = cosf(rotx); + + // Convert to center point parameterization. + // http://www.w3.org/TR/SVG11/implnote.html#ArcImplementationNotes + // 1) Compute x1', y1' + x1p = cosrx * dx / 2.0f + sinrx * dy / 2.0f; + y1p = -sinrx * dx / 2.0f + cosrx * dy / 2.0f; + d = nsvg__sqr(x1p)/nsvg__sqr(rx) + nsvg__sqr(y1p)/nsvg__sqr(ry); + if (d > 1) { + d = sqrtf(d); + rx *= d; + ry *= d; + } + // 2) Compute cx', cy' + s = 0.0f; + sa = nsvg__sqr(rx)*nsvg__sqr(ry) - nsvg__sqr(rx)*nsvg__sqr(y1p) - nsvg__sqr(ry)*nsvg__sqr(x1p); + sb = nsvg__sqr(rx)*nsvg__sqr(y1p) + nsvg__sqr(ry)*nsvg__sqr(x1p); + if (sa < 0.0f) sa = 0.0f; + if (sb > 0.0f) + s = sqrtf(sa / sb); + if (fa == fs) + s = -s; + cxp = s * rx * y1p / ry; + cyp = s * -ry * x1p / rx; + + // 3) Compute cx,cy from cx',cy' + cx = (x1 + x2)/2.0f + cosrx*cxp - sinrx*cyp; + cy = (y1 + y2)/2.0f + sinrx*cxp + cosrx*cyp; + + // 4) Calculate theta1, and delta theta. + ux = (x1p - cxp) / rx; + uy = (y1p - cyp) / ry; + vx = (-x1p - cxp) / rx; + vy = (-y1p - cyp) / ry; + a1 = nsvg__vecang(1.0f,0.0f, ux,uy); // Initial angle + da = nsvg__vecang(ux,uy, vx,vy); // Delta angle + +// if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI; +// if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0; + + if (fs == 0 && da > 0) + da -= 2 * NSVG_PI; + else if (fs == 1 && da < 0) + da += 2 * NSVG_PI; + + // Approximate the arc using cubic spline segments. + t[0] = cosrx; t[1] = sinrx; + t[2] = -sinrx; t[3] = cosrx; + t[4] = cx; t[5] = cy; + + // Split arc into max 90 degree segments. + // The loop assumes an iteration per end point (including start and end), this +1. + ndivs = (int)(fabsf(da) / (NSVG_PI*0.5f) + 1.0f); + hda = (da / (float)ndivs) / 2.0f; + // Fix for ticket #179: division by 0: avoid cotangens around 0 (infinite) + if ((hda < 1e-3f) && (hda > -1e-3f)) + hda *= 0.5f; + else + hda = (1.0f - cosf(hda)) / sinf(hda); + kappa = fabsf(4.0f / 3.0f * hda); + if (da < 0.0f) + kappa = -kappa; + + for (i = 0; i <= ndivs; i++) { + a = a1 + da * ((float)i/(float)ndivs); + dx = cosf(a); + dy = sinf(a); + nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position + nsvg__xformVec(&tanx, &tany, -dy*rx * kappa, dx*ry * kappa, t); // tangent + if (i > 0) + nsvg__cubicBezTo(p, px+ptanx,py+ptany, x-tanx, y-tany, x, y); + px = x; + py = y; + ptanx = tanx; + ptany = tany; + } + + *cpx = x2; + *cpy = y2; +} + +static void nsvg__parsePath(NSVGparser* p, const char** attr) +{ + const char* s = NULL; + char cmd = '\0'; + float args[10]; + int nargs; + int rargs = 0; + char initPoint; + float cpx, cpy, cpx2, cpy2; + const char* tmp[4]; + char closedFlag; + int i; + char item[64]; + + for (i = 0; attr[i]; i += 2) { + if (strcmp(attr[i], "d") == 0) { + s = attr[i + 1]; + } else { + tmp[0] = attr[i]; + tmp[1] = attr[i + 1]; + tmp[2] = 0; + tmp[3] = 0; + nsvg__parseAttribs(p, tmp); + } + } + + if (s) { + nsvg__resetPath(p); + cpx = 0; cpy = 0; + cpx2 = 0; cpy2 = 0; + initPoint = 0; + closedFlag = 0; + nargs = 0; + + while (*s) { + s = nsvg__getNextPathItem(s, item); + if (!*item) break; + if (cmd != '\0' && nsvg__isCoordinate(item)) { + if (nargs < 10) + args[nargs++] = (float)nsvg__atof(item); + if (nargs >= rargs) { + switch (cmd) { + case 'm': + case 'M': + nsvg__pathMoveTo(p, &cpx, &cpy, args, cmd == 'm' ? 1 : 0); + // Moveto can be followed by multiple coordinate pairs, + // which should be treated as linetos. + cmd = (cmd == 'm') ? 'l' : 'L'; + rargs = nsvg__getArgsPerElement(cmd); + cpx2 = cpx; cpy2 = cpy; + initPoint = 1; + break; + case 'l': + case 'L': + nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + case 'H': + case 'h': + nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + case 'V': + case 'v': + nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + case 'C': + case 'c': + nsvg__pathCubicBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'c' ? 1 : 0); + break; + case 'S': + case 's': + nsvg__pathCubicBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 's' ? 1 : 0); + break; + case 'Q': + case 'q': + nsvg__pathQuadBezTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 'q' ? 1 : 0); + break; + case 'T': + case 't': + nsvg__pathQuadBezShortTo(p, &cpx, &cpy, &cpx2, &cpy2, args, cmd == 't' ? 1 : 0); + break; + case 'A': + case 'a': + nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0); + cpx2 = cpx; cpy2 = cpy; + break; + default: + if (nargs >= 2) { + cpx = args[nargs-2]; + cpy = args[nargs-1]; + cpx2 = cpx; cpy2 = cpy; + } + break; + } + nargs = 0; + } + } else { + cmd = item[0]; + if (cmd == 'M' || cmd == 'm') { + // Commit path. + if (p->npts > 0) + nsvg__addPath(p, closedFlag); + // Start new subpath. + nsvg__resetPath(p); + closedFlag = 0; + nargs = 0; + } else if (initPoint == 0) { + // Do not allow other commands until initial point has been set (moveTo called once). + cmd = '\0'; + } + if (cmd == 'Z' || cmd == 'z') { + closedFlag = 1; + // Commit path. + if (p->npts > 0) { + // Move current point to first point + cpx = p->pts[0]; + cpy = p->pts[1]; + cpx2 = cpx; cpy2 = cpy; + nsvg__addPath(p, closedFlag); + } + // Start new subpath. + nsvg__resetPath(p); + nsvg__moveTo(p, cpx, cpy); + closedFlag = 0; + nargs = 0; + } + rargs = nsvg__getArgsPerElement(cmd); + if (rargs == -1) { + // Command not recognized + cmd = '\0'; + rargs = 0; + } + } + } + // Commit path. + if (p->npts) + nsvg__addPath(p, closedFlag); + } + + nsvg__addShape(p); +} + +static void nsvg__parseRect(NSVGparser* p, const char** attr) +{ + float x = 0.0f; + float y = 0.0f; + float w = 0.0f; + float h = 0.0f; + float rx = -1.0f; // marks not set + float ry = -1.0f; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "x") == 0) x = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "y") == 0) y = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "width") == 0) w = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p)); + if (strcmp(attr[i], "height") == 0) h = nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p)); + if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); + if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); + } + } + + if (rx < 0.0f && ry > 0.0f) rx = ry; + if (ry < 0.0f && rx > 0.0f) ry = rx; + if (rx < 0.0f) rx = 0.0f; + if (ry < 0.0f) ry = 0.0f; + if (rx > w/2.0f) rx = w/2.0f; + if (ry > h/2.0f) ry = h/2.0f; + + if (w != 0.0f && h != 0.0f) { + nsvg__resetPath(p); + + if (rx < 0.00001f || ry < 0.0001f) { + nsvg__moveTo(p, x, y); + nsvg__lineTo(p, x+w, y); + nsvg__lineTo(p, x+w, y+h); + nsvg__lineTo(p, x, y+h); + } else { + // Rounded rectangle + nsvg__moveTo(p, x+rx, y); + nsvg__lineTo(p, x+w-rx, y); + nsvg__cubicBezTo(p, x+w-rx*(1-NSVG_KAPPA90), y, x+w, y+ry*(1-NSVG_KAPPA90), x+w, y+ry); + nsvg__lineTo(p, x+w, y+h-ry); + nsvg__cubicBezTo(p, x+w, y+h-ry*(1-NSVG_KAPPA90), x+w-rx*(1-NSVG_KAPPA90), y+h, x+w-rx, y+h); + nsvg__lineTo(p, x+rx, y+h); + nsvg__cubicBezTo(p, x+rx*(1-NSVG_KAPPA90), y+h, x, y+h-ry*(1-NSVG_KAPPA90), x, y+h-ry); + nsvg__lineTo(p, x, y+ry); + nsvg__cubicBezTo(p, x, y+ry*(1-NSVG_KAPPA90), x+rx*(1-NSVG_KAPPA90), y, x+rx, y); + } + + nsvg__addPath(p, 1); + + nsvg__addShape(p); + } +} + +static void nsvg__parseCircle(NSVGparser* p, const char** attr) +{ + float cx = 0.0f; + float cy = 0.0f; + float r = 0.0f; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "r") == 0) r = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualLength(p))); + } + } + + if (r > 0.0f) { + nsvg__resetPath(p); + + nsvg__moveTo(p, cx+r, cy); + nsvg__cubicBezTo(p, cx+r, cy+r*NSVG_KAPPA90, cx+r*NSVG_KAPPA90, cy+r, cx, cy+r); + nsvg__cubicBezTo(p, cx-r*NSVG_KAPPA90, cy+r, cx-r, cy+r*NSVG_KAPPA90, cx-r, cy); + nsvg__cubicBezTo(p, cx-r, cy-r*NSVG_KAPPA90, cx-r*NSVG_KAPPA90, cy-r, cx, cy-r); + nsvg__cubicBezTo(p, cx+r*NSVG_KAPPA90, cy-r, cx+r, cy-r*NSVG_KAPPA90, cx+r, cy); + + nsvg__addPath(p, 1); + + nsvg__addShape(p); + } +} + +static void nsvg__parseEllipse(NSVGparser* p, const char** attr) +{ + float cx = 0.0f; + float cy = 0.0f; + float rx = 0.0f; + float ry = 0.0f; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "cx") == 0) cx = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "cy") == 0) cy = nsvg__parseCoordinate(p, attr[i+1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "rx") == 0) rx = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualWidth(p))); + if (strcmp(attr[i], "ry") == 0) ry = fabsf(nsvg__parseCoordinate(p, attr[i+1], 0.0f, nsvg__actualHeight(p))); + } + } + + if (rx > 0.0f && ry > 0.0f) { + + nsvg__resetPath(p); + + nsvg__moveTo(p, cx+rx, cy); + nsvg__cubicBezTo(p, cx+rx, cy+ry*NSVG_KAPPA90, cx+rx*NSVG_KAPPA90, cy+ry, cx, cy+ry); + nsvg__cubicBezTo(p, cx-rx*NSVG_KAPPA90, cy+ry, cx-rx, cy+ry*NSVG_KAPPA90, cx-rx, cy); + nsvg__cubicBezTo(p, cx-rx, cy-ry*NSVG_KAPPA90, cx-rx*NSVG_KAPPA90, cy-ry, cx, cy-ry); + nsvg__cubicBezTo(p, cx+rx*NSVG_KAPPA90, cy-ry, cx+rx, cy-ry*NSVG_KAPPA90, cx+rx, cy); + + nsvg__addPath(p, 1); + + nsvg__addShape(p); + } +} + +static void nsvg__parseLine(NSVGparser* p, const char** attr) +{ + float x1 = 0.0; + float y1 = 0.0; + float x2 = 0.0; + float y2 = 0.0; + int i; + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "x1") == 0) x1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "y1") == 0) y1 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + if (strcmp(attr[i], "x2") == 0) x2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigX(p), nsvg__actualWidth(p)); + if (strcmp(attr[i], "y2") == 0) y2 = nsvg__parseCoordinate(p, attr[i + 1], nsvg__actualOrigY(p), nsvg__actualHeight(p)); + } + } + + nsvg__resetPath(p); + + nsvg__moveTo(p, x1, y1); + nsvg__lineTo(p, x2, y2); + + nsvg__addPath(p, 0); + + nsvg__addShape(p); +} + +static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag) +{ + int i; + const char* s; + float args[2]; + int nargs, npts = 0; + char item[64]; + + nsvg__resetPath(p); + + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "points") == 0) { + s = attr[i + 1]; + nargs = 0; + while (*s) { + s = nsvg__getNextPathItem(s, item); + args[nargs++] = (float)nsvg__atof(item); + if (nargs >= 2) { + if (npts == 0) + nsvg__moveTo(p, args[0], args[1]); + else + nsvg__lineTo(p, args[0], args[1]); + nargs = 0; + npts++; + } + } + } + } + } + + nsvg__addPath(p, (char)closeFlag); + + nsvg__addShape(p); +} + +static void nsvg__parseSVG(NSVGparser* p, const char** attr) +{ + int i; + for (i = 0; attr[i]; i += 2) { + if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "width") == 0) { + p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); + } else if (strcmp(attr[i], "height") == 0) { + p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); + } else if (strcmp(attr[i], "viewBox") == 0) { + const char *s = attr[i + 1]; + char buf[64]; + s = nsvg__parseNumber(s, buf, 64); + p->viewMinx = nsvg__atof(buf); + while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; + if (!*s) return; + s = nsvg__parseNumber(s, buf, 64); + p->viewMiny = nsvg__atof(buf); + while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; + if (!*s) return; + s = nsvg__parseNumber(s, buf, 64); + p->viewWidth = nsvg__atof(buf); + while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; + if (!*s) return; + s = nsvg__parseNumber(s, buf, 64); + p->viewHeight = nsvg__atof(buf); + } else if (strcmp(attr[i], "preserveAspectRatio") == 0) { + if (strstr(attr[i + 1], "none") != 0) { + // No uniform scaling + p->alignType = NSVG_ALIGN_NONE; + } else { + // Parse X align + if (strstr(attr[i + 1], "xMin") != 0) + p->alignX = NSVG_ALIGN_MIN; + else if (strstr(attr[i + 1], "xMid") != 0) + p->alignX = NSVG_ALIGN_MID; + else if (strstr(attr[i + 1], "xMax") != 0) + p->alignX = NSVG_ALIGN_MAX; + // Parse X align + if (strstr(attr[i + 1], "yMin") != 0) + p->alignY = NSVG_ALIGN_MIN; + else if (strstr(attr[i + 1], "yMid") != 0) + p->alignY = NSVG_ALIGN_MID; + else if (strstr(attr[i + 1], "yMax") != 0) + p->alignY = NSVG_ALIGN_MAX; + // Parse meet/slice + p->alignType = NSVG_ALIGN_MEET; + if (strstr(attr[i + 1], "slice") != 0) + p->alignType = NSVG_ALIGN_SLICE; + } + } + } + } +} + +static void nsvg__parseGradient(NSVGparser* p, const char** attr, char type) +{ + int i; + NSVGgradientData* grad = (NSVGgradientData*)malloc(sizeof(NSVGgradientData)); + if (grad == NULL) return; + memset(grad, 0, sizeof(NSVGgradientData)); + grad->units = NSVG_OBJECT_SPACE; + grad->type = type; + if (grad->type == NSVG_PAINT_LINEAR_GRADIENT) { + grad->linear.x1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); + grad->linear.y1 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); + grad->linear.x2 = nsvg__coord(100.0f, NSVG_UNITS_PERCENT); + grad->linear.y2 = nsvg__coord(0.0f, NSVG_UNITS_PERCENT); + } else if (grad->type == NSVG_PAINT_RADIAL_GRADIENT) { + grad->radial.cx = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); + grad->radial.cy = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); + grad->radial.r = nsvg__coord(50.0f, NSVG_UNITS_PERCENT); + } + + nsvg__xformIdentity(grad->xform); + + for (i = 0; attr[i]; i += 2) { + if (strcmp(attr[i], "id") == 0) { + strncpy(grad->id, attr[i+1], 63); + grad->id[63] = '\0'; + } else if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { + if (strcmp(attr[i], "gradientUnits") == 0) { + if (strcmp(attr[i+1], "objectBoundingBox") == 0) + grad->units = NSVG_OBJECT_SPACE; + else + grad->units = NSVG_USER_SPACE; + } else if (strcmp(attr[i], "gradientTransform") == 0) { + nsvg__parseTransform(grad->xform, attr[i + 1]); + } else if (strcmp(attr[i], "cx") == 0) { + grad->radial.cx = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "cy") == 0) { + grad->radial.cy = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "r") == 0) { + grad->radial.r = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "fx") == 0) { + grad->radial.fx = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "fy") == 0) { + grad->radial.fy = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "x1") == 0) { + grad->linear.x1 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "y1") == 0) { + grad->linear.y1 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "x2") == 0) { + grad->linear.x2 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "y2") == 0) { + grad->linear.y2 = nsvg__parseCoordinateRaw(attr[i + 1]); + } else if (strcmp(attr[i], "spreadMethod") == 0) { + if (strcmp(attr[i+1], "pad") == 0) + grad->spread = NSVG_SPREAD_PAD; + else if (strcmp(attr[i+1], "reflect") == 0) + grad->spread = NSVG_SPREAD_REFLECT; + else if (strcmp(attr[i+1], "repeat") == 0) + grad->spread = NSVG_SPREAD_REPEAT; + } else if (strcmp(attr[i], "xlink:href") == 0) { + const char *href = attr[i+1]; + strncpy(grad->ref, href+1, 62); + grad->ref[62] = '\0'; + } + } + } + + grad->next = p->gradients; + p->gradients = grad; +} + +static void nsvg__parseGradientStop(NSVGparser* p, const char** attr) +{ + NSVGattrib* curAttr = nsvg__getAttr(p); + NSVGgradientData* grad; + NSVGgradientStop* stop; + int i, idx; + + curAttr->stopOffset = 0; + curAttr->stopColor = 0; + curAttr->stopOpacity = 1.0f; + + for (i = 0; attr[i]; i += 2) { + nsvg__parseAttr(p, attr[i], attr[i + 1]); + } + + // Add stop to the last gradient. + grad = p->gradients; + if (grad == NULL) return; + + grad->nstops++; + grad->stops = (NSVGgradientStop*)realloc(grad->stops, sizeof(NSVGgradientStop)*grad->nstops); + if (grad->stops == NULL) return; + + // Insert + idx = grad->nstops-1; + for (i = 0; i < grad->nstops-1; i++) { + if (curAttr->stopOffset < grad->stops[i].offset) { + idx = i; + break; + } + } + if (idx != grad->nstops-1) { + for (i = grad->nstops-1; i > idx; i--) + grad->stops[i] = grad->stops[i-1]; + } + + stop = &grad->stops[idx]; + stop->color = curAttr->stopColor; + stop->color |= (unsigned int)(curAttr->stopOpacity*255) << 24; + stop->offset = curAttr->stopOffset; +} + +static void nsvg__startElement(void* ud, const char* el, const char** attr) +{ + NSVGparser* p = (NSVGparser*)ud; + + if (p->defsFlag) { + // Skip everything but gradients in defs + if (strcmp(el, "linearGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); + } else if (strcmp(el, "radialGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); + } else if (strcmp(el, "stop") == 0) { + nsvg__parseGradientStop(p, attr); + } + return; + } + + if (strcmp(el, "g") == 0) { + nsvg__pushAttr(p); + nsvg__parseAttribs(p, attr); + } else if (strcmp(el, "path") == 0) { + if (p->pathFlag) // Do not allow nested paths. + return; + nsvg__pushAttr(p); + nsvg__parsePath(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "rect") == 0) { + nsvg__pushAttr(p); + nsvg__parseRect(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "circle") == 0) { + nsvg__pushAttr(p); + nsvg__parseCircle(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "ellipse") == 0) { + nsvg__pushAttr(p); + nsvg__parseEllipse(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "line") == 0) { + nsvg__pushAttr(p); + nsvg__parseLine(p, attr); + nsvg__popAttr(p); + } else if (strcmp(el, "polyline") == 0) { + nsvg__pushAttr(p); + nsvg__parsePoly(p, attr, 0); + nsvg__popAttr(p); + } else if (strcmp(el, "polygon") == 0) { + nsvg__pushAttr(p); + nsvg__parsePoly(p, attr, 1); + nsvg__popAttr(p); + } else if (strcmp(el, "linearGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_LINEAR_GRADIENT); + } else if (strcmp(el, "radialGradient") == 0) { + nsvg__parseGradient(p, attr, NSVG_PAINT_RADIAL_GRADIENT); + } else if (strcmp(el, "stop") == 0) { + nsvg__parseGradientStop(p, attr); + } else if (strcmp(el, "defs") == 0) { + p->defsFlag = 1; + } else if (strcmp(el, "svg") == 0) { + nsvg__parseSVG(p, attr); + } +} + +static void nsvg__endElement(void* ud, const char* el) +{ + NSVGparser* p = (NSVGparser*)ud; + + if (strcmp(el, "g") == 0) { + nsvg__popAttr(p); + } else if (strcmp(el, "path") == 0) { + p->pathFlag = 0; + } else if (strcmp(el, "defs") == 0) { + p->defsFlag = 0; + } +} + +static void nsvg__content(void* ud, const char* s) +{ + NSVG_NOTUSED(ud); + NSVG_NOTUSED(s); + // empty +} + +static void nsvg__imageBounds(NSVGparser* p, float* bounds) +{ + NSVGshape* shape; + shape = p->image->shapes; + if (shape == NULL) { + bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0; + return; + } + bounds[0] = shape->bounds[0]; + bounds[1] = shape->bounds[1]; + bounds[2] = shape->bounds[2]; + bounds[3] = shape->bounds[3]; + for (shape = shape->next; shape != NULL; shape = shape->next) { + bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]); + bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]); + bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]); + bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]); + } +} + +static float nsvg__viewAlign(float content, float container, int type) +{ + if (type == NSVG_ALIGN_MIN) + return 0; + else if (type == NSVG_ALIGN_MAX) + return container - content; + // mid + return (container - content) * 0.5f; +} + +static void nsvg__scaleGradient(NSVGgradient* grad, float tx, float ty, float sx, float sy) +{ + float t[6]; + nsvg__xformSetTranslation(t, tx, ty); + nsvg__xformMultiply (grad->xform, t); + + nsvg__xformSetScale(t, sx, sy); + nsvg__xformMultiply (grad->xform, t); +} + +static void nsvg__scaleToViewbox(NSVGparser* p, const char* units) +{ + NSVGshape* shape; + NSVGpath* path; + float tx, ty, sx, sy, us, bounds[4], t[6], avgs; + int i; + float* pt; + + // Guess image size if not set completely. + nsvg__imageBounds(p, bounds); + + if (p->viewWidth == 0) { + if (p->image->width > 0) { + p->viewWidth = p->image->width; + } else { + p->viewMinx = bounds[0]; + p->viewWidth = bounds[2] - bounds[0]; + } + } + if (p->viewHeight == 0) { + if (p->image->height > 0) { + p->viewHeight = p->image->height; + } else { + p->viewMiny = bounds[1]; + p->viewHeight = bounds[3] - bounds[1]; + } + } + if (p->image->width == 0) + p->image->width = p->viewWidth; + if (p->image->height == 0) + p->image->height = p->viewHeight; + + tx = -p->viewMinx; + ty = -p->viewMiny; + sx = p->viewWidth > 0 ? p->image->width / p->viewWidth : 0; + sy = p->viewHeight > 0 ? p->image->height / p->viewHeight : 0; + // Unit scaling + us = 1.0f / nsvg__convertToPixels(p, nsvg__coord(1.0f, nsvg__parseUnits(units)), 0.0f, 1.0f); + + // Fix aspect ratio + if (p->alignType == NSVG_ALIGN_MEET) { + // fit whole image into viewbox + sx = sy = nsvg__minf(sx, sy); + tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; + ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; + } else if (p->alignType == NSVG_ALIGN_SLICE) { + // fill whole viewbox with image + sx = sy = nsvg__maxf(sx, sy); + tx += nsvg__viewAlign(p->viewWidth*sx, p->image->width, p->alignX) / sx; + ty += nsvg__viewAlign(p->viewHeight*sy, p->image->height, p->alignY) / sy; + } + + // Transform + sx *= us; + sy *= us; + avgs = (sx+sy) / 2.0f; + for (shape = p->image->shapes; shape != NULL; shape = shape->next) { + shape->bounds[0] = (shape->bounds[0] + tx) * sx; + shape->bounds[1] = (shape->bounds[1] + ty) * sy; + shape->bounds[2] = (shape->bounds[2] + tx) * sx; + shape->bounds[3] = (shape->bounds[3] + ty) * sy; + for (path = shape->paths; path != NULL; path = path->next) { + path->bounds[0] = (path->bounds[0] + tx) * sx; + path->bounds[1] = (path->bounds[1] + ty) * sy; + path->bounds[2] = (path->bounds[2] + tx) * sx; + path->bounds[3] = (path->bounds[3] + ty) * sy; + for (i =0; i < path->npts; i++) { + pt = &path->pts[i*2]; + pt[0] = (pt[0] + tx) * sx; + pt[1] = (pt[1] + ty) * sy; + } + } + + if (shape->fill.type == NSVG_PAINT_LINEAR_GRADIENT || shape->fill.type == NSVG_PAINT_RADIAL_GRADIENT) { + nsvg__scaleGradient(shape->fill.gradient, tx,ty, sx,sy); + memcpy(t, shape->fill.gradient->xform, sizeof(float)*6); + nsvg__xformInverse(shape->fill.gradient->xform, t); + } + if (shape->stroke.type == NSVG_PAINT_LINEAR_GRADIENT || shape->stroke.type == NSVG_PAINT_RADIAL_GRADIENT) { + nsvg__scaleGradient(shape->stroke.gradient, tx,ty, sx,sy); + memcpy(t, shape->stroke.gradient->xform, sizeof(float)*6); + nsvg__xformInverse(shape->stroke.gradient->xform, t); + } + + shape->strokeWidth *= avgs; + shape->strokeDashOffset *= avgs; + for (i = 0; i < shape->strokeDashCount; i++) + shape->strokeDashArray[i] *= avgs; + } +} + +NSVGimage* nsvgParse(char* input, const char* units, float dpi) +{ + NSVGparser* p; + NSVGimage* ret = 0; + + p = nsvg__createParser(); + if (p == NULL) { + return NULL; + } + p->dpi = dpi; + + nsvg__parseXML(input, nsvg__startElement, nsvg__endElement, nsvg__content, p); + + // Scale to viewBox + nsvg__scaleToViewbox(p, units); + + ret = p->image; + p->image = NULL; + + nsvg__deleteParser(p); + + return ret; +} + +NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi) +{ + FILE* fp = NULL; + size_t size; + char* data = NULL; + NSVGimage* image = NULL; + + fp = fopen(filename, "rb"); + if (!fp) goto error; + fseek(fp, 0, SEEK_END); + size = ftell(fp); + fseek(fp, 0, SEEK_SET); + data = (char*)malloc(size+1); + if (data == NULL) goto error; + if (fread(data, 1, size, fp) != size) goto error; + data[size] = '\0'; // Must be null terminated. + fclose(fp); + image = nsvgParse(data, units, dpi); + free(data); + + return image; + +error: + if (fp) fclose(fp); + if (data) free(data); + if (image) nsvgDelete(image); + return NULL; +} + +NSVGpath* nsvgDuplicatePath(NSVGpath* p) +{ + NSVGpath* res = NULL; + + if (p == NULL) + return NULL; + + res = (NSVGpath*)malloc(sizeof(NSVGpath)); + if (res == NULL) goto error; + memset(res, 0, sizeof(NSVGpath)); + + res->pts = (float*)malloc(p->npts*2*sizeof(float)); + if (res->pts == NULL) goto error; + memcpy(res->pts, p->pts, p->npts * sizeof(float) * 2); + res->npts = p->npts; + + memcpy(res->bounds, p->bounds, sizeof(p->bounds)); + + res->closed = p->closed; + + return res; + +error: + if (res != NULL) { + free(res->pts); + free(res); + } + return NULL; +} + +void nsvgDelete(NSVGimage* image) +{ + NSVGshape *snext, *shape; + if (image == NULL) return; + shape = image->shapes; + while (shape != NULL) { + snext = shape->next; + nsvg__deletePaths(shape->paths); + nsvg__deletePaint(&shape->fill); + nsvg__deletePaint(&shape->stroke); + free(shape); + shape = snext; + } + free(image); +} + +#endif // NANOSVG_IMPLEMENTATION + +#endif // NANOSVG_H diff --git a/src/external/nanosvgrast.h b/src/external/nanosvgrast.h new file mode 100644 index 000000000..6e23acb7b --- /dev/null +++ b/src/external/nanosvgrast.h @@ -0,0 +1,1458 @@ +/* + * Copyright (c) 2013-14 Mikko Mononen memon@inside.org + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + * + * The polygon rasterization is heavily based on stb_truetype rasterizer + * by Sean Barrett - http://nothings.org/ + * + */ + +#ifndef NANOSVGRAST_H +#define NANOSVGRAST_H + +#include "nanosvg.h" + +#ifndef NANOSVGRAST_CPLUSPLUS +#ifdef __cplusplus +extern "C" { +#endif +#endif + +typedef struct NSVGrasterizer NSVGrasterizer; + +/* Example Usage: + // Load SVG + NSVGimage* image; + image = nsvgParseFromFile("test.svg", "px", 96); + + // Create rasterizer (can be used to render multiple images). + struct NSVGrasterizer* rast = nsvgCreateRasterizer(); + // Allocate memory for image + unsigned char* img = malloc(w*h*4); + // Rasterize + nsvgRasterize(rast, image, 0,0,1, img, w, h, w*4); +*/ + +// Allocated rasterizer context. +NSVGrasterizer* nsvgCreateRasterizer(void); + +// Rasterizes SVG image, returns RGBA image (non-premultiplied alpha) +// r - pointer to rasterizer context +// image - pointer to image to rasterize +// tx,ty - image offset (applied after scaling) +// scale - image scale +// dst - pointer to destination image data, 4 bytes per pixel (RGBA) +// w - width of the image to render +// h - height of the image to render +// stride - number of bytes per scaleline in the destination buffer +void nsvgRasterize(NSVGrasterizer* r, + NSVGimage* image, float tx, float ty, float scale, + unsigned char* dst, int w, int h, int stride); + +// Deletes rasterizer context. +void nsvgDeleteRasterizer(NSVGrasterizer*); + + +#ifndef NANOSVGRAST_CPLUSPLUS +#ifdef __cplusplus +} +#endif +#endif + +#ifdef NANOSVGRAST_IMPLEMENTATION + +#include +#include +#include + +#define NSVG__SUBSAMPLES 5 +#define NSVG__FIXSHIFT 10 +#define NSVG__FIX (1 << NSVG__FIXSHIFT) +#define NSVG__FIXMASK (NSVG__FIX-1) +#define NSVG__MEMPAGE_SIZE 1024 + +typedef struct NSVGedge { + float x0,y0, x1,y1; + int dir; + struct NSVGedge* next; +} NSVGedge; + +typedef struct NSVGpoint { + float x, y; + float dx, dy; + float len; + float dmx, dmy; + unsigned char flags; +} NSVGpoint; + +typedef struct NSVGactiveEdge { + int x,dx; + float ey; + int dir; + struct NSVGactiveEdge *next; +} NSVGactiveEdge; + +typedef struct NSVGmemPage { + unsigned char mem[NSVG__MEMPAGE_SIZE]; + int size; + struct NSVGmemPage* next; +} NSVGmemPage; + +typedef struct NSVGcachedPaint { + char type; + char spread; + float xform[6]; + unsigned int colors[256]; +} NSVGcachedPaint; + +struct NSVGrasterizer +{ + float px, py; + + float tessTol; + float distTol; + + NSVGedge* edges; + int nedges; + int cedges; + + NSVGpoint* points; + int npoints; + int cpoints; + + NSVGpoint* points2; + int npoints2; + int cpoints2; + + NSVGactiveEdge* freelist; + NSVGmemPage* pages; + NSVGmemPage* curpage; + + unsigned char* scanline; + int cscanline; + + unsigned char* bitmap; + int width, height, stride; +}; + +NSVGrasterizer* nsvgCreateRasterizer(void) +{ + NSVGrasterizer* r = (NSVGrasterizer*)malloc(sizeof(NSVGrasterizer)); + if (r == NULL) goto error; + memset(r, 0, sizeof(NSVGrasterizer)); + + r->tessTol = 0.25f; + r->distTol = 0.01f; + + return r; + +error: + nsvgDeleteRasterizer(r); + return NULL; +} + +void nsvgDeleteRasterizer(NSVGrasterizer* r) +{ + NSVGmemPage* p; + + if (r == NULL) return; + + p = r->pages; + while (p != NULL) { + NSVGmemPage* next = p->next; + free(p); + p = next; + } + + if (r->edges) free(r->edges); + if (r->points) free(r->points); + if (r->points2) free(r->points2); + if (r->scanline) free(r->scanline); + + free(r); +} + +static NSVGmemPage* nsvg__nextPage(NSVGrasterizer* r, NSVGmemPage* cur) +{ + NSVGmemPage *newp; + + // If using existing chain, return the next page in chain + if (cur != NULL && cur->next != NULL) { + return cur->next; + } + + // Alloc new page + newp = (NSVGmemPage*)malloc(sizeof(NSVGmemPage)); + if (newp == NULL) return NULL; + memset(newp, 0, sizeof(NSVGmemPage)); + + // Add to linked list + if (cur != NULL) + cur->next = newp; + else + r->pages = newp; + + return newp; +} + +static void nsvg__resetPool(NSVGrasterizer* r) +{ + NSVGmemPage* p = r->pages; + while (p != NULL) { + p->size = 0; + p = p->next; + } + r->curpage = r->pages; +} + +static unsigned char* nsvg__alloc(NSVGrasterizer* r, int size) +{ + unsigned char* buf; + if (size > NSVG__MEMPAGE_SIZE) return NULL; + if (r->curpage == NULL || r->curpage->size+size > NSVG__MEMPAGE_SIZE) { + r->curpage = nsvg__nextPage(r, r->curpage); + } + buf = &r->curpage->mem[r->curpage->size]; + r->curpage->size += size; + return buf; +} + +static int nsvg__ptEquals(float x1, float y1, float x2, float y2, float tol) +{ + float dx = x2 - x1; + float dy = y2 - y1; + return dx*dx + dy*dy < tol*tol; +} + +static void nsvg__addPathPoint(NSVGrasterizer* r, float x, float y, int flags) +{ + NSVGpoint* pt; + + if (r->npoints > 0) { + pt = &r->points[r->npoints-1]; + if (nsvg__ptEquals(pt->x,pt->y, x,y, r->distTol)) { + pt->flags = (unsigned char)(pt->flags | flags); + return; + } + } + + if (r->npoints+1 > r->cpoints) { + r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; + r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); + if (r->points == NULL) return; + } + + pt = &r->points[r->npoints]; + pt->x = x; + pt->y = y; + pt->flags = (unsigned char)flags; + r->npoints++; +} + +static void nsvg__appendPathPoint(NSVGrasterizer* r, NSVGpoint pt) +{ + if (r->npoints+1 > r->cpoints) { + r->cpoints = r->cpoints > 0 ? r->cpoints * 2 : 64; + r->points = (NSVGpoint*)realloc(r->points, sizeof(NSVGpoint) * r->cpoints); + if (r->points == NULL) return; + } + r->points[r->npoints] = pt; + r->npoints++; +} + +static void nsvg__duplicatePoints(NSVGrasterizer* r) +{ + if (r->npoints > r->cpoints2) { + r->cpoints2 = r->npoints; + r->points2 = (NSVGpoint*)realloc(r->points2, sizeof(NSVGpoint) * r->cpoints2); + if (r->points2 == NULL) return; + } + + memcpy(r->points2, r->points, sizeof(NSVGpoint) * r->npoints); + r->npoints2 = r->npoints; +} + +static void nsvg__addEdge(NSVGrasterizer* r, float x0, float y0, float x1, float y1) +{ + NSVGedge* e; + + // Skip horizontal edges + if (y0 == y1) + return; + + if (r->nedges+1 > r->cedges) { + r->cedges = r->cedges > 0 ? r->cedges * 2 : 64; + r->edges = (NSVGedge*)realloc(r->edges, sizeof(NSVGedge) * r->cedges); + if (r->edges == NULL) return; + } + + e = &r->edges[r->nedges]; + r->nedges++; + + if (y0 < y1) { + e->x0 = x0; + e->y0 = y0; + e->x1 = x1; + e->y1 = y1; + e->dir = 1; + } else { + e->x0 = x1; + e->y0 = y1; + e->x1 = x0; + e->y1 = y0; + e->dir = -1; + } +} + +static float nsvg__normalize(float *x, float* y) +{ + float d = sqrtf((*x)*(*x) + (*y)*(*y)); + if (d > 1e-6f) { + float id = 1.0f / d; + *x *= id; + *y *= id; + } + return d; +} + +static float nsvg__absf(float x) { return x < 0 ? -x : x; } + +static void nsvg__flattenCubicBez(NSVGrasterizer* r, + float x1, float y1, float x2, float y2, + float x3, float y3, float x4, float y4, + int level, int type) +{ + float x12,y12,x23,y23,x34,y34,x123,y123,x234,y234,x1234,y1234; + float dx,dy,d2,d3; + + if (level > 10) return; + + x12 = (x1+x2)*0.5f; + y12 = (y1+y2)*0.5f; + x23 = (x2+x3)*0.5f; + y23 = (y2+y3)*0.5f; + x34 = (x3+x4)*0.5f; + y34 = (y3+y4)*0.5f; + x123 = (x12+x23)*0.5f; + y123 = (y12+y23)*0.5f; + + dx = x4 - x1; + dy = y4 - y1; + d2 = nsvg__absf(((x2 - x4) * dy - (y2 - y4) * dx)); + d3 = nsvg__absf(((x3 - x4) * dy - (y3 - y4) * dx)); + + if ((d2 + d3)*(d2 + d3) < r->tessTol * (dx*dx + dy*dy)) { + nsvg__addPathPoint(r, x4, y4, type); + return; + } + + x234 = (x23+x34)*0.5f; + y234 = (y23+y34)*0.5f; + x1234 = (x123+x234)*0.5f; + y1234 = (y123+y234)*0.5f; + + nsvg__flattenCubicBez(r, x1,y1, x12,y12, x123,y123, x1234,y1234, level+1, 0); + nsvg__flattenCubicBez(r, x1234,y1234, x234,y234, x34,y34, x4,y4, level+1, type); +} + +static void nsvg__flattenShape(NSVGrasterizer* r, NSVGshape* shape, float scale) +{ + int i, j; + NSVGpath* path; + + for (path = shape->paths; path != NULL; path = path->next) { + r->npoints = 0; + // Flatten path + nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0); + for (i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, 0); + } + // Close path + nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, 0); + // Build edges + for (i = 0, j = r->npoints-1; i < r->npoints; j = i++) + nsvg__addEdge(r, r->points[j].x, r->points[j].y, r->points[i].x, r->points[i].y); + } +} + +enum NSVGpointFlags +{ + NSVG_PT_CORNER = 0x01, + NSVG_PT_BEVEL = 0x02, + NSVG_PT_LEFT = 0x04 +}; + +static void nsvg__initClosed(NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dx = p1->x - p0->x; + float dy = p1->y - p0->y; + float len = nsvg__normalize(&dx, &dy); + float px = p0->x + dx*len*0.5f, py = p0->y + dy*len*0.5f; + float dlx = dy, dly = -dx; + float lx = px - dlx*w, ly = py - dly*w; + float rx = px + dlx*w, ry = py + dly*w; + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__buttCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) +{ + float w = lineWidth * 0.5f; + float px = p->x, py = p->y; + float dlx = dy, dly = -dx; + float lx = px - dlx*w, ly = py - dly*w; + float rx = px + dlx*w, ry = py + dly*w; + + nsvg__addEdge(r, lx, ly, rx, ry); + + if (connect) { + nsvg__addEdge(r, left->x, left->y, lx, ly); + nsvg__addEdge(r, rx, ry, right->x, right->y); + } + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__squareCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int connect) +{ + float w = lineWidth * 0.5f; + float px = p->x - dx*w, py = p->y - dy*w; + float dlx = dy, dly = -dx; + float lx = px - dlx*w, ly = py - dly*w; + float rx = px + dlx*w, ry = py + dly*w; + + nsvg__addEdge(r, lx, ly, rx, ry); + + if (connect) { + nsvg__addEdge(r, left->x, left->y, lx, ly); + nsvg__addEdge(r, rx, ry, right->x, right->y); + } + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +#ifndef NSVG_PI +#define NSVG_PI (3.14159265358979323846264338327f) +#endif + +static void nsvg__roundCap(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p, float dx, float dy, float lineWidth, int ncap, int connect) +{ + int i; + float w = lineWidth * 0.5f; + float px = p->x, py = p->y; + float dlx = dy, dly = -dx; + float lx = 0, ly = 0, rx = 0, ry = 0, prevx = 0, prevy = 0; + + for (i = 0; i < ncap; i++) { + float a = (float)i/(float)(ncap-1)*NSVG_PI; + float ax = cosf(a) * w, ay = sinf(a) * w; + float x = px - dlx*ax - dx*ay; + float y = py - dly*ax - dy*ay; + + if (i > 0) + nsvg__addEdge(r, prevx, prevy, x, y); + + prevx = x; + prevy = y; + + if (i == 0) { + lx = x; ly = y; + } else if (i == ncap-1) { + rx = x; ry = y; + } + } + + if (connect) { + nsvg__addEdge(r, left->x, left->y, lx, ly); + nsvg__addEdge(r, rx, ry, right->x, right->y); + } + + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__bevelJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float lx0 = p1->x - (dlx0 * w), ly0 = p1->y - (dly0 * w); + float rx0 = p1->x + (dlx0 * w), ry0 = p1->y + (dly0 * w); + float lx1 = p1->x - (dlx1 * w), ly1 = p1->y - (dly1 * w); + float rx1 = p1->x + (dlx1 * w), ry1 = p1->y + (dly1 * w); + + nsvg__addEdge(r, lx0, ly0, left->x, left->y); + nsvg__addEdge(r, lx1, ly1, lx0, ly0); + + nsvg__addEdge(r, right->x, right->y, rx0, ry0); + nsvg__addEdge(r, rx0, ry0, rx1, ry1); + + left->x = lx1; left->y = ly1; + right->x = rx1; right->y = ry1; +} + +static void nsvg__miterJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float lx0, rx0, lx1, rx1; + float ly0, ry0, ly1, ry1; + + if (p1->flags & NSVG_PT_LEFT) { + lx0 = lx1 = p1->x - p1->dmx * w; + ly0 = ly1 = p1->y - p1->dmy * w; + nsvg__addEdge(r, lx1, ly1, left->x, left->y); + + rx0 = p1->x + (dlx0 * w); + ry0 = p1->y + (dly0 * w); + rx1 = p1->x + (dlx1 * w); + ry1 = p1->y + (dly1 * w); + nsvg__addEdge(r, right->x, right->y, rx0, ry0); + nsvg__addEdge(r, rx0, ry0, rx1, ry1); + } else { + lx0 = p1->x - (dlx0 * w); + ly0 = p1->y - (dly0 * w); + lx1 = p1->x - (dlx1 * w); + ly1 = p1->y - (dly1 * w); + nsvg__addEdge(r, lx0, ly0, left->x, left->y); + nsvg__addEdge(r, lx1, ly1, lx0, ly0); + + rx0 = rx1 = p1->x + p1->dmx * w; + ry0 = ry1 = p1->y + p1->dmy * w; + nsvg__addEdge(r, right->x, right->y, rx1, ry1); + } + + left->x = lx1; left->y = ly1; + right->x = rx1; right->y = ry1; +} + +static void nsvg__roundJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p0, NSVGpoint* p1, float lineWidth, int ncap) +{ + int i, n; + float w = lineWidth * 0.5f; + float dlx0 = p0->dy, dly0 = -p0->dx; + float dlx1 = p1->dy, dly1 = -p1->dx; + float a0 = atan2f(dly0, dlx0); + float a1 = atan2f(dly1, dlx1); + float da = a1 - a0; + float lx, ly, rx, ry; + + if (da < NSVG_PI) da += NSVG_PI*2; + if (da > NSVG_PI) da -= NSVG_PI*2; + + n = (int)ceilf((nsvg__absf(da) / NSVG_PI) * (float)ncap); + if (n < 2) n = 2; + if (n > ncap) n = ncap; + + lx = left->x; + ly = left->y; + rx = right->x; + ry = right->y; + + for (i = 0; i < n; i++) { + float u = (float)i/(float)(n-1); + float a = a0 + u*da; + float ax = cosf(a) * w, ay = sinf(a) * w; + float lx1 = p1->x - ax, ly1 = p1->y - ay; + float rx1 = p1->x + ax, ry1 = p1->y + ay; + + nsvg__addEdge(r, lx1, ly1, lx, ly); + nsvg__addEdge(r, rx, ry, rx1, ry1); + + lx = lx1; ly = ly1; + rx = rx1; ry = ry1; + } + + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static void nsvg__straightJoin(NSVGrasterizer* r, NSVGpoint* left, NSVGpoint* right, NSVGpoint* p1, float lineWidth) +{ + float w = lineWidth * 0.5f; + float lx = p1->x - (p1->dmx * w), ly = p1->y - (p1->dmy * w); + float rx = p1->x + (p1->dmx * w), ry = p1->y + (p1->dmy * w); + + nsvg__addEdge(r, lx, ly, left->x, left->y); + nsvg__addEdge(r, right->x, right->y, rx, ry); + + left->x = lx; left->y = ly; + right->x = rx; right->y = ry; +} + +static int nsvg__curveDivs(float r, float arc, float tol) +{ + float da = acosf(r / (r + tol)) * 2.0f; + int divs = (int)ceilf(arc / da); + if (divs < 2) divs = 2; + return divs; +} + +static void nsvg__expandStroke(NSVGrasterizer* r, NSVGpoint* points, int npoints, int closed, int lineJoin, int lineCap, float lineWidth) +{ + int ncap = nsvg__curveDivs(lineWidth*0.5f, NSVG_PI, r->tessTol); // Calculate divisions per half circle. + NSVGpoint left = {0,0,0,0,0,0,0,0}, right = {0,0,0,0,0,0,0,0}, firstLeft = {0,0,0,0,0,0,0,0}, firstRight = {0,0,0,0,0,0,0,0}; + NSVGpoint* p0, *p1; + int j, s, e; + + // Build stroke edges + if (closed) { + // Looping + p0 = &points[npoints-1]; + p1 = &points[0]; + s = 0; + e = npoints; + } else { + // Add cap + p0 = &points[0]; + p1 = &points[1]; + s = 1; + e = npoints-1; + } + + if (closed) { + nsvg__initClosed(&left, &right, p0, p1, lineWidth); + firstLeft = left; + firstRight = right; + } else { + // Add cap + float dx = p1->x - p0->x; + float dy = p1->y - p0->y; + nsvg__normalize(&dx, &dy); + if (lineCap == NSVG_CAP_BUTT) + nsvg__buttCap(r, &left, &right, p0, dx, dy, lineWidth, 0); + else if (lineCap == NSVG_CAP_SQUARE) + nsvg__squareCap(r, &left, &right, p0, dx, dy, lineWidth, 0); + else if (lineCap == NSVG_CAP_ROUND) + nsvg__roundCap(r, &left, &right, p0, dx, dy, lineWidth, ncap, 0); + } + + for (j = s; j < e; ++j) { + if (p1->flags & NSVG_PT_CORNER) { + if (lineJoin == NSVG_JOIN_ROUND) + nsvg__roundJoin(r, &left, &right, p0, p1, lineWidth, ncap); + else if (lineJoin == NSVG_JOIN_BEVEL || (p1->flags & NSVG_PT_BEVEL)) + nsvg__bevelJoin(r, &left, &right, p0, p1, lineWidth); + else + nsvg__miterJoin(r, &left, &right, p0, p1, lineWidth); + } else { + nsvg__straightJoin(r, &left, &right, p1, lineWidth); + } + p0 = p1++; + } + + if (closed) { + // Loop it + nsvg__addEdge(r, firstLeft.x, firstLeft.y, left.x, left.y); + nsvg__addEdge(r, right.x, right.y, firstRight.x, firstRight.y); + } else { + // Add cap + float dx = p1->x - p0->x; + float dy = p1->y - p0->y; + nsvg__normalize(&dx, &dy); + if (lineCap == NSVG_CAP_BUTT) + nsvg__buttCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); + else if (lineCap == NSVG_CAP_SQUARE) + nsvg__squareCap(r, &right, &left, p1, -dx, -dy, lineWidth, 1); + else if (lineCap == NSVG_CAP_ROUND) + nsvg__roundCap(r, &right, &left, p1, -dx, -dy, lineWidth, ncap, 1); + } +} + +static void nsvg__prepareStroke(NSVGrasterizer* r, float miterLimit, int lineJoin) +{ + int i, j; + NSVGpoint* p0, *p1; + + p0 = &r->points[r->npoints-1]; + p1 = &r->points[0]; + for (i = 0; i < r->npoints; i++) { + // Calculate segment direction and length + p0->dx = p1->x - p0->x; + p0->dy = p1->y - p0->y; + p0->len = nsvg__normalize(&p0->dx, &p0->dy); + // Advance + p0 = p1++; + } + + // calculate joins + p0 = &r->points[r->npoints-1]; + p1 = &r->points[0]; + for (j = 0; j < r->npoints; j++) { + float dlx0, dly0, dlx1, dly1, dmr2, cross; + dlx0 = p0->dy; + dly0 = -p0->dx; + dlx1 = p1->dy; + dly1 = -p1->dx; + // Calculate extrusions + p1->dmx = (dlx0 + dlx1) * 0.5f; + p1->dmy = (dly0 + dly1) * 0.5f; + dmr2 = p1->dmx*p1->dmx + p1->dmy*p1->dmy; + if (dmr2 > 0.000001f) { + float s2 = 1.0f / dmr2; + if (s2 > 600.0f) { + s2 = 600.0f; + } + p1->dmx *= s2; + p1->dmy *= s2; + } + + // Clear flags, but keep the corner. + p1->flags = (p1->flags & NSVG_PT_CORNER) ? NSVG_PT_CORNER : 0; + + // Keep track of left turns. + cross = p1->dx * p0->dy - p0->dx * p1->dy; + if (cross > 0.0f) + p1->flags |= NSVG_PT_LEFT; + + // Check to see if the corner needs to be beveled. + if (p1->flags & NSVG_PT_CORNER) { + if ((dmr2 * miterLimit*miterLimit) < 1.0f || lineJoin == NSVG_JOIN_BEVEL || lineJoin == NSVG_JOIN_ROUND) { + p1->flags |= NSVG_PT_BEVEL; + } + } + + p0 = p1++; + } +} + +static void nsvg__flattenShapeStroke(NSVGrasterizer* r, NSVGshape* shape, float scale) +{ + int i, j, closed; + NSVGpath* path; + NSVGpoint* p0, *p1; + float miterLimit = shape->miterLimit; + int lineJoin = shape->strokeLineJoin; + int lineCap = shape->strokeLineCap; + float lineWidth = shape->strokeWidth * scale; + + for (path = shape->paths; path != NULL; path = path->next) { + // Flatten path + r->npoints = 0; + nsvg__addPathPoint(r, path->pts[0]*scale, path->pts[1]*scale, NSVG_PT_CORNER); + for (i = 0; i < path->npts-1; i += 3) { + float* p = &path->pts[i*2]; + nsvg__flattenCubicBez(r, p[0]*scale,p[1]*scale, p[2]*scale,p[3]*scale, p[4]*scale,p[5]*scale, p[6]*scale,p[7]*scale, 0, NSVG_PT_CORNER); + } + if (r->npoints < 2) + continue; + + closed = path->closed; + + // If the first and last points are the same, remove the last, mark as closed path. + p0 = &r->points[r->npoints-1]; + p1 = &r->points[0]; + if (nsvg__ptEquals(p0->x,p0->y, p1->x,p1->y, r->distTol)) { + r->npoints--; + p0 = &r->points[r->npoints-1]; + closed = 1; + } + + if (shape->strokeDashCount > 0) { + int idash = 0, dashState = 1; + float totalDist = 0, dashLen, allDashLen, dashOffset; + NSVGpoint cur; + + if (closed) + nsvg__appendPathPoint(r, r->points[0]); + + // Duplicate points -> points2. + nsvg__duplicatePoints(r); + + r->npoints = 0; + cur = r->points2[0]; + nsvg__appendPathPoint(r, cur); + + // Figure out dash offset. + allDashLen = 0; + for (j = 0; j < shape->strokeDashCount; j++) + allDashLen += shape->strokeDashArray[j]; + if (shape->strokeDashCount & 1) + allDashLen *= 2.0f; + // Find location inside pattern + dashOffset = fmodf(shape->strokeDashOffset, allDashLen); + if (dashOffset < 0.0f) + dashOffset += allDashLen; + + while (dashOffset > shape->strokeDashArray[idash]) { + dashOffset -= shape->strokeDashArray[idash]; + idash = (idash + 1) % shape->strokeDashCount; + } + dashLen = (shape->strokeDashArray[idash] - dashOffset) * scale; + + for (j = 1; j < r->npoints2; ) { + float dx = r->points2[j].x - cur.x; + float dy = r->points2[j].y - cur.y; + float dist = sqrtf(dx*dx + dy*dy); + + if ((totalDist + dist) > dashLen) { + // Calculate intermediate point + float d = (dashLen - totalDist) / dist; + float x = cur.x + dx * d; + float y = cur.y + dy * d; + nsvg__addPathPoint(r, x, y, NSVG_PT_CORNER); + + // Stroke + if (r->npoints > 1 && dashState) { + nsvg__prepareStroke(r, miterLimit, lineJoin); + nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); + } + // Advance dash pattern + dashState = !dashState; + idash = (idash+1) % shape->strokeDashCount; + dashLen = shape->strokeDashArray[idash] * scale; + // Restart + cur.x = x; + cur.y = y; + cur.flags = NSVG_PT_CORNER; + totalDist = 0.0f; + r->npoints = 0; + nsvg__appendPathPoint(r, cur); + } else { + totalDist += dist; + cur = r->points2[j]; + nsvg__appendPathPoint(r, cur); + j++; + } + } + // Stroke any leftover path + if (r->npoints > 1 && dashState) + nsvg__expandStroke(r, r->points, r->npoints, 0, lineJoin, lineCap, lineWidth); + } else { + nsvg__prepareStroke(r, miterLimit, lineJoin); + nsvg__expandStroke(r, r->points, r->npoints, closed, lineJoin, lineCap, lineWidth); + } + } +} + +static int nsvg__cmpEdge(const void *p, const void *q) +{ + const NSVGedge* a = (const NSVGedge*)p; + const NSVGedge* b = (const NSVGedge*)q; + + if (a->y0 < b->y0) return -1; + if (a->y0 > b->y0) return 1; + return 0; +} + + +static NSVGactiveEdge* nsvg__addActive(NSVGrasterizer* r, NSVGedge* e, float startPoint) +{ + NSVGactiveEdge* z; + + if (r->freelist != NULL) { + // Restore from freelist. + z = r->freelist; + r->freelist = z->next; + } else { + // Alloc new edge. + z = (NSVGactiveEdge*)nsvg__alloc(r, sizeof(NSVGactiveEdge)); + if (z == NULL) return NULL; + } + + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); +// STBTT_assert(e->y0 <= start_point); + // round dx down to avoid going too far + if (dxdy < 0) + z->dx = (int)(-floorf(NSVG__FIX * -dxdy)); + else + z->dx = (int)floorf(NSVG__FIX * dxdy); + z->x = (int)floorf(NSVG__FIX * (e->x0 + dxdy * (startPoint - e->y0))); +// z->x -= off_x * FIX; + z->ey = e->y1; + z->next = 0; + z->dir = e->dir; + + return z; +} + +static void nsvg__freeActive(NSVGrasterizer* r, NSVGactiveEdge* z) +{ + z->next = r->freelist; + r->freelist = z; +} + +static void nsvg__fillScanline(unsigned char* scanline, int len, int x0, int x1, int maxWeight, int* xmin, int* xmax) +{ + int i = x0 >> NSVG__FIXSHIFT; + int j = x1 >> NSVG__FIXSHIFT; + if (i < *xmin) *xmin = i; + if (j > *xmax) *xmax = j; + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = (unsigned char)(scanline[i] + ((x1 - x0) * maxWeight >> NSVG__FIXSHIFT)); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = (unsigned char)(scanline[i] + (((NSVG__FIX - (x0 & NSVG__FIXMASK)) * maxWeight) >> NSVG__FIXSHIFT)); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = (unsigned char)(scanline[j] + (((x1 & NSVG__FIXMASK) * maxWeight) >> NSVG__FIXSHIFT)); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = (unsigned char)(scanline[i] + maxWeight); + } + } +} + +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void nsvg__fillActiveEdges(unsigned char* scanline, int len, NSVGactiveEdge* e, int maxWeight, int* xmin, int* xmax, char fillRule) +{ + // non-zero winding fill + int x0 = 0, w = 0; + + if (fillRule == NSVG_FILLRULE_NONZERO) { + // Non-zero + while (e != NULL) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->dir; + } else { + int x1 = e->x; w += e->dir; + // if we went to zero, we need to draw + if (w == 0) + nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); + } + e = e->next; + } + } else if (fillRule == NSVG_FILLRULE_EVENODD) { + // Even-odd + while (e != NULL) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w = 1; + } else { + int x1 = e->x; w = 0; + nsvg__fillScanline(scanline, len, x0, x1, maxWeight, xmin, xmax); + } + e = e->next; + } + } +} + +static float nsvg__clampf(float a, float mn, float mx) { return a < mn ? mn : (a > mx ? mx : a); } + +static unsigned int nsvg__RGBA(unsigned char r, unsigned char g, unsigned char b, unsigned char a) +{ + return ((unsigned int)r) | ((unsigned int)g << 8) | ((unsigned int)b << 16) | ((unsigned int)a << 24); +} + +static unsigned int nsvg__lerpRGBA(unsigned int c0, unsigned int c1, float u) +{ + int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); + int r = (((c0) & 0xff)*(256-iu) + (((c1) & 0xff)*iu)) >> 8; + int g = (((c0>>8) & 0xff)*(256-iu) + (((c1>>8) & 0xff)*iu)) >> 8; + int b = (((c0>>16) & 0xff)*(256-iu) + (((c1>>16) & 0xff)*iu)) >> 8; + int a = (((c0>>24) & 0xff)*(256-iu) + (((c1>>24) & 0xff)*iu)) >> 8; + return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); +} + +static unsigned int nsvg__applyOpacity(unsigned int c, float u) +{ + int iu = (int)(nsvg__clampf(u, 0.0f, 1.0f) * 256.0f); + int r = (c) & 0xff; + int g = (c>>8) & 0xff; + int b = (c>>16) & 0xff; + int a = (((c>>24) & 0xff)*iu) >> 8; + return nsvg__RGBA((unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a); +} + +static inline int nsvg__div255(int x) +{ + return ((x+1) * 257) >> 16; +} + +static void nsvg__scanlineSolid(unsigned char* dst, int count, unsigned char* cover, int x, int y, + float tx, float ty, float scale, NSVGcachedPaint* cache) +{ + + if (cache->type == NSVG_PAINT_COLOR) { + int i, cr, cg, cb, ca; + cr = cache->colors[0] & 0xff; + cg = (cache->colors[0] >> 8) & 0xff; + cb = (cache->colors[0] >> 16) & 0xff; + ca = (cache->colors[0] >> 24) & 0xff; + + for (i = 0; i < count; i++) { + int r,g,b; + int a = nsvg__div255((int)cover[0] * ca); + int ia = 255 - a; + // Premultiply + r = nsvg__div255(cr * a); + g = nsvg__div255(cg * a); + b = nsvg__div255(cb * a); + + // Blend over + r += nsvg__div255(ia * (int)dst[0]); + g += nsvg__div255(ia * (int)dst[1]); + b += nsvg__div255(ia * (int)dst[2]); + a += nsvg__div255(ia * (int)dst[3]); + + dst[0] = (unsigned char)r; + dst[1] = (unsigned char)g; + dst[2] = (unsigned char)b; + dst[3] = (unsigned char)a; + + cover++; + dst += 4; + } + } else if (cache->type == NSVG_PAINT_LINEAR_GRADIENT) { + // TODO: spread modes. + // TODO: plenty of opportunities to optimize. + float fx, fy, dx, gy; + float* t = cache->xform; + int i, cr, cg, cb, ca; + unsigned int c; + + fx = ((float)x - tx) / scale; + fy = ((float)y - ty) / scale; + dx = 1.0f / scale; + + for (i = 0; i < count; i++) { + int r,g,b,a,ia; + gy = fx*t[1] + fy*t[3] + t[5]; + c = cache->colors[(int)nsvg__clampf(gy*255.0f, 0, 255.0f)]; + cr = (c) & 0xff; + cg = (c >> 8) & 0xff; + cb = (c >> 16) & 0xff; + ca = (c >> 24) & 0xff; + + a = nsvg__div255((int)cover[0] * ca); + ia = 255 - a; + + // Premultiply + r = nsvg__div255(cr * a); + g = nsvg__div255(cg * a); + b = nsvg__div255(cb * a); + + // Blend over + r += nsvg__div255(ia * (int)dst[0]); + g += nsvg__div255(ia * (int)dst[1]); + b += nsvg__div255(ia * (int)dst[2]); + a += nsvg__div255(ia * (int)dst[3]); + + dst[0] = (unsigned char)r; + dst[1] = (unsigned char)g; + dst[2] = (unsigned char)b; + dst[3] = (unsigned char)a; + + cover++; + dst += 4; + fx += dx; + } + } else if (cache->type == NSVG_PAINT_RADIAL_GRADIENT) { + // TODO: spread modes. + // TODO: plenty of opportunities to optimize. + // TODO: focus (fx,fy) + float fx, fy, dx, gx, gy, gd; + float* t = cache->xform; + int i, cr, cg, cb, ca; + unsigned int c; + + fx = ((float)x - tx) / scale; + fy = ((float)y - ty) / scale; + dx = 1.0f / scale; + + for (i = 0; i < count; i++) { + int r,g,b,a,ia; + gx = fx*t[0] + fy*t[2] + t[4]; + gy = fx*t[1] + fy*t[3] + t[5]; + gd = sqrtf(gx*gx + gy*gy); + c = cache->colors[(int)nsvg__clampf(gd*255.0f, 0, 255.0f)]; + cr = (c) & 0xff; + cg = (c >> 8) & 0xff; + cb = (c >> 16) & 0xff; + ca = (c >> 24) & 0xff; + + a = nsvg__div255((int)cover[0] * ca); + ia = 255 - a; + + // Premultiply + r = nsvg__div255(cr * a); + g = nsvg__div255(cg * a); + b = nsvg__div255(cb * a); + + // Blend over + r += nsvg__div255(ia * (int)dst[0]); + g += nsvg__div255(ia * (int)dst[1]); + b += nsvg__div255(ia * (int)dst[2]); + a += nsvg__div255(ia * (int)dst[3]); + + dst[0] = (unsigned char)r; + dst[1] = (unsigned char)g; + dst[2] = (unsigned char)b; + dst[3] = (unsigned char)a; + + cover++; + dst += 4; + fx += dx; + } + } +} + +static void nsvg__rasterizeSortedEdges(NSVGrasterizer *r, float tx, float ty, float scale, NSVGcachedPaint* cache, char fillRule) +{ + NSVGactiveEdge *active = NULL; + int y, s; + int e = 0; + int maxWeight = (255 / NSVG__SUBSAMPLES); // weight per vertical scanline + int xmin, xmax; + + for (y = 0; y < r->height; y++) { + memset(r->scanline, 0, r->width); + xmin = r->width; + xmax = 0; + for (s = 0; s < NSVG__SUBSAMPLES; ++s) { + // find center of pixel for this scanline + float scany = (float)(y*NSVG__SUBSAMPLES + s) + 0.5f; + NSVGactiveEdge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + NSVGactiveEdge *z = *step; + if (z->ey <= scany) { + *step = z->next; // delete from list +// NSVG__assert(z->valid); + nsvg__freeActive(r, z); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for (;;) { + int changed = 0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + NSVGactiveEdge* t = *step; + NSVGactiveEdge* q = t->next; + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e < r->nedges && r->edges[e].y0 <= scany) { + if (r->edges[e].y1 > scany) { + NSVGactiveEdge* z = nsvg__addActive(r, &r->edges[e], scany); + if (z == NULL) break; + // find insertion point + if (active == NULL) { + active = z; + } else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + NSVGactiveEdge* p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + e++; + } + + // now process all active edges in non-zero fashion + if (active != NULL) + nsvg__fillActiveEdges(r->scanline, r->width, active, maxWeight, &xmin, &xmax, fillRule); + } + // Blit + if (xmin < 0) xmin = 0; + if (xmax > r->width-1) xmax = r->width-1; + if (xmin <= xmax) { + nsvg__scanlineSolid(&r->bitmap[y * r->stride] + xmin*4, xmax-xmin+1, &r->scanline[xmin], xmin, y, tx,ty, scale, cache); + } + } + +} + +static void nsvg__unpremultiplyAlpha(unsigned char* image, int w, int h, int stride) +{ + int x,y; + + // Unpremultiply + for (y = 0; y < h; y++) { + unsigned char *row = &image[y*stride]; + for (x = 0; x < w; x++) { + int r = row[0], g = row[1], b = row[2], a = row[3]; + if (a != 0) { + row[0] = (unsigned char)(r*255/a); + row[1] = (unsigned char)(g*255/a); + row[2] = (unsigned char)(b*255/a); + } + row += 4; + } + } + + // Defringe + for (y = 0; y < h; y++) { + unsigned char *row = &image[y*stride]; + for (x = 0; x < w; x++) { + int r = 0, g = 0, b = 0, a = row[3], n = 0; + if (a == 0) { + if (x-1 > 0 && row[-1] != 0) { + r += row[-4]; + g += row[-3]; + b += row[-2]; + n++; + } + if (x+1 < w && row[7] != 0) { + r += row[4]; + g += row[5]; + b += row[6]; + n++; + } + if (y-1 > 0 && row[-stride+3] != 0) { + r += row[-stride]; + g += row[-stride+1]; + b += row[-stride+2]; + n++; + } + if (y+1 < h && row[stride+3] != 0) { + r += row[stride]; + g += row[stride+1]; + b += row[stride+2]; + n++; + } + if (n > 0) { + row[0] = (unsigned char)(r/n); + row[1] = (unsigned char)(g/n); + row[2] = (unsigned char)(b/n); + } + } + row += 4; + } + } +} + + +static void nsvg__initPaint(NSVGcachedPaint* cache, NSVGpaint* paint, float opacity) +{ + int i, j; + NSVGgradient* grad; + + cache->type = paint->type; + + if (paint->type == NSVG_PAINT_COLOR) { + cache->colors[0] = nsvg__applyOpacity(paint->color, opacity); + return; + } + + grad = paint->gradient; + + cache->spread = grad->spread; + memcpy(cache->xform, grad->xform, sizeof(float)*6); + + if (grad->nstops == 0) { + for (i = 0; i < 256; i++) + cache->colors[i] = 0; + } if (grad->nstops == 1) { + for (i = 0; i < 256; i++) + cache->colors[i] = nsvg__applyOpacity(grad->stops[i].color, opacity); + } else { + unsigned int ca, cb = 0; + float ua, ub, du, u; + int ia, ib, count; + + ca = nsvg__applyOpacity(grad->stops[0].color, opacity); + ua = nsvg__clampf(grad->stops[0].offset, 0, 1); + ub = nsvg__clampf(grad->stops[grad->nstops-1].offset, ua, 1); + ia = (int)(ua * 255.0f); + ib = (int)(ub * 255.0f); + for (i = 0; i < ia; i++) { + cache->colors[i] = ca; + } + + for (i = 0; i < grad->nstops-1; i++) { + ca = nsvg__applyOpacity(grad->stops[i].color, opacity); + cb = nsvg__applyOpacity(grad->stops[i+1].color, opacity); + ua = nsvg__clampf(grad->stops[i].offset, 0, 1); + ub = nsvg__clampf(grad->stops[i+1].offset, 0, 1); + ia = (int)(ua * 255.0f); + ib = (int)(ub * 255.0f); + count = ib - ia; + if (count <= 0) continue; + u = 0; + du = 1.0f / (float)count; + for (j = 0; j < count; j++) { + cache->colors[ia+j] = nsvg__lerpRGBA(ca,cb,u); + u += du; + } + } + + for (i = ib; i < 256; i++) + cache->colors[i] = cb; + } + +} + +/* +static void dumpEdges(NSVGrasterizer* r, const char* name) +{ + float xmin = 0, xmax = 0, ymin = 0, ymax = 0; + NSVGedge *e = NULL; + int i; + if (r->nedges == 0) return; + FILE* fp = fopen(name, "w"); + if (fp == NULL) return; + + xmin = xmax = r->edges[0].x0; + ymin = ymax = r->edges[0].y0; + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + xmin = nsvg__minf(xmin, e->x0); + xmin = nsvg__minf(xmin, e->x1); + xmax = nsvg__maxf(xmax, e->x0); + xmax = nsvg__maxf(xmax, e->x1); + ymin = nsvg__minf(ymin, e->y0); + ymin = nsvg__minf(ymin, e->y1); + ymax = nsvg__maxf(ymax, e->y0); + ymax = nsvg__maxf(ymax, e->y1); + } + + fprintf(fp, "", xmin, ymin, (xmax - xmin), (ymax - ymin)); + + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + fprintf(fp ,"", e->x0,e->y0, e->x1,e->y1); + } + + for (i = 0; i < r->npoints; i++) { + if (i+1 < r->npoints) + fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i+1].x, r->points[i+1].y); + fprintf(fp ,"", r->points[i].x, r->points[i].y, r->points[i].flags == 0 ? "#f00" : "#0f0"); + } + + fprintf(fp, ""); + fclose(fp); +} +*/ + +void nsvgRasterize(NSVGrasterizer* r, + NSVGimage* image, float tx, float ty, float scale, + unsigned char* dst, int w, int h, int stride) +{ + NSVGshape *shape = NULL; + NSVGedge *e = NULL; + NSVGcachedPaint cache; + int i; + + r->bitmap = dst; + r->width = w; + r->height = h; + r->stride = stride; + + if (w > r->cscanline) { + r->cscanline = w; + r->scanline = (unsigned char*)realloc(r->scanline, w); + if (r->scanline == NULL) return; + } + + for (i = 0; i < h; i++) + memset(&dst[i*stride], 0, w*4); + + for (shape = image->shapes; shape != NULL; shape = shape->next) { + if (!(shape->flags & NSVG_FLAGS_VISIBLE)) + continue; + + if (shape->fill.type != NSVG_PAINT_NONE) { + nsvg__resetPool(r); + r->freelist = NULL; + r->nedges = 0; + + nsvg__flattenShape(r, shape, scale); + + // Scale and translate edges + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + e->x0 = tx + e->x0; + e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; + e->x1 = tx + e->x1; + e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; + } + + // Rasterize edges + if (r->nedges != 0) + qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); + + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + nsvg__initPaint(&cache, &shape->fill, shape->opacity); + + nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, shape->fillRule); + } + if (shape->stroke.type != NSVG_PAINT_NONE && (shape->strokeWidth * scale) > 0.01f) { + nsvg__resetPool(r); + r->freelist = NULL; + r->nedges = 0; + + nsvg__flattenShapeStroke(r, shape, scale); + +// dumpEdges(r, "edge.svg"); + + // Scale and translate edges + for (i = 0; i < r->nedges; i++) { + e = &r->edges[i]; + e->x0 = tx + e->x0; + e->y0 = (ty + e->y0) * NSVG__SUBSAMPLES; + e->x1 = tx + e->x1; + e->y1 = (ty + e->y1) * NSVG__SUBSAMPLES; + } + + // Rasterize edges + if (r->nedges != 0) + qsort(r->edges, r->nedges, sizeof(NSVGedge), nsvg__cmpEdge); + + // now, traverse the scanlines and find the intersections on each scanline, use non-zero rule + nsvg__initPaint(&cache, &shape->stroke, shape->opacity); + + nsvg__rasterizeSortedEdges(r, tx,ty,scale, &cache, NSVG_FILLRULE_NONZERO); + } + } + + nsvg__unpremultiplyAlpha(dst, w, h, stride); + + r->bitmap = NULL; + r->width = 0; + r->height = 0; + r->stride = 0; +} + +#endif // NANOSVGRAST_IMPLEMENTATION + +#endif // NANOSVGRAST_H diff --git a/src/raylib.h b/src/raylib.h index 6f67d839e..198c6c497 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1245,6 +1245,8 @@ RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2); // NOTE: These functions do not require GPU access RLAPI Image LoadImage(const char *fileName); // Load image from file into CPU memory (RAM) RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data +RLAPI Image LoadImageSvg(const char *fileName, int width, int height); // Load image from SVG file data with specified size +RLAPI Image LoadImageSvgFromString(const char *string, int width, int height); // Load an image from a SVG string with custom size RLAPI Image LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data) RLAPI Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load image from memory buffer, fileType refers to extension: i.e. '.png' RLAPI Image LoadImageFromTexture(Texture2D texture); // Load image from GPU texture data diff --git a/src/rtextures.c b/src/rtextures.c index 566371112..09202ef77 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -215,6 +215,14 @@ #define STB_IMAGE_RESIZE_IMPLEMENTATION #include "external/stb_image_resize.h" // Required for: stbir_resize_uint8() [ImageResize()] +#if defined(SUPPORT_FILEFORMAT_SVG) + #define NANOSVG_IMPLEMENTATION // Expands implementation + #include "external/nanosvg.h" + + #define NANOSVGRAST_IMPLEMENTATION + #include "external/nanosvgrast.h" +#endif + //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- @@ -310,6 +318,72 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int return image; } +// Load an image from SVG file data with a custom size +Image LoadImageSvg(const char *fileName, int width, int height) +{ + Image image = { 0 }; + + unsigned int dataSize = 0; + unsigned char *string = LoadFileData(fileName, &dataSize); + + if (string != NULL) + { + image = LoadImageSvgFromString(string, width, height); + RL_FREE(string); + } + + return image; +} + +// Load an image from a SVG string with custom size +Image LoadImageSvgFromString(const char *string, int width, int height) +{ + Image image = { 0 }; + + if (string != NULL) + { + struct NSVGimage *svgImage = nsvgParse(string, "px", 96.0f); + + // Allocate memory for image + unsigned char *img = malloc(width*height*4); + + // Calculate scales for both the width and the height + const float scaleWidth = width/svgImage->width; + const float scaleHeight = height/svgImage->height; + + // Set the largest of the 2 scales to be the scale to use + const float scale = (scaleHeight > scaleWidth) ? scaleWidth : scaleHeight; + + int offsetX = 0; + int offsetY = 0; + + if (scaleHeight > scaleWidth) + { + offsetY = (height - svgImage->height*scale) / 2; + } + else + { + offsetX = (width - svgImage->width*scale) / 2; + } + + // Rasterize + struct NSVGrasterizer* rast = nsvgCreateRasterizer(); + nsvgRasterize(rast, svgImage, (int)offsetX, (int)offsetY, scale, img, width, height, width*4); + + // Populate image struct with all data + image.data = img; + image.width = width; + image.height = height; + image.mipmaps = 1; + image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + + // Delete + nsvgDelete(svgImage); + } + + return image; +} + // Load animated image data // - Image.data buffer includes all frames: [image#0][image#1][image#2][...] // - Number of frames is returned through 'frames' parameter @@ -470,6 +544,27 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i { image.data = rl_load_astc_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); } +#endif +#if defined(SUPPORT_FILEFORMAT_SVG) + else if (strcmp(fileType, ".svg") == 0) + { + if (fileData != NULL) + { + // Creating a duplicate svg to read sizes from due to nsvgParse modifiying the string buffer. + unsigned char *duplicate = (unsigned char*)RL_MALLOC(dataSize); + memcpy(duplicate, fileData, dataSize); + struct NSVGimage *svgImage = nsvgParse(duplicate, "px", 96.0f); + RL_FREE(duplicate); + + const int width = (int)svgImage->width; + const int height = (int)svgImage->height; + // Delete + nsvgDelete(svgImage); + + + image = LoadImageSvgFromString(fileData, width, height); + } + } #endif else TRACELOG(LOG_WARNING, "IMAGE: Data format not supported"); From d6f3891009bbd582ceb4728682ffb6e1a89bd721 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Sep 2023 13:16:44 +0200 Subject: [PATCH 0242/1350] REVIEWED: `LoadImageSvg()` --- src/raylib.h | 3 +- src/rtextures.c | 136 +++++++++++++++++++++++------------------------- 2 files changed, 66 insertions(+), 73 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 198c6c497..4f3e35380 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1245,8 +1245,7 @@ RLAPI Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2); // NOTE: These functions do not require GPU access RLAPI Image LoadImage(const char *fileName); // Load image from file into CPU memory (RAM) RLAPI Image LoadImageRaw(const char *fileName, int width, int height, int format, int headerSize); // Load image from RAW file data -RLAPI Image LoadImageSvg(const char *fileName, int width, int height); // Load image from SVG file data with specified size -RLAPI Image LoadImageSvgFromString(const char *string, int width, int height); // Load an image from a SVG string with custom size +RLAPI Image LoadImageSvg(const char *fileNameOrString, int width, int height); // Load image from SVG file data or string with specified size RLAPI Image LoadImageAnim(const char *fileName, int *frames); // Load image sequence from file (frames appended to image.data) RLAPI Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, int dataSize); // Load image from memory buffer, fileType refers to extension: i.e. '.png' RLAPI Image LoadImageFromTexture(Texture2D texture); // Load image from GPU texture data diff --git a/src/rtextures.c b/src/rtextures.c index 09202ef77..6b7875b08 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -318,67 +318,60 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int return image; } -// Load an image from SVG file data with a custom size -Image LoadImageSvg(const char *fileName, int width, int height) +// Load an image from a SVG file or string with custom size +Image LoadImageSvg(const char *fileNameOrString, int width, int height) { Image image = { 0 }; - - unsigned int dataSize = 0; - unsigned char *string = LoadFileData(fileName, &dataSize); - - if (string != NULL) + bool isSvgStringValid = false; + + // TODO: Validate fileName or string + if (fileNameOrString != NULL) { - image = LoadImageSvgFromString(string, width, height); - RL_FREE(string); - } - - return image; -} - -// Load an image from a SVG string with custom size -Image LoadImageSvgFromString(const char *string, int width, int height) -{ - Image image = { 0 }; - - if (string != NULL) - { - struct NSVGimage *svgImage = nsvgParse(string, "px", 96.0f); - - // Allocate memory for image - unsigned char *img = malloc(width*height*4); - - // Calculate scales for both the width and the height - const float scaleWidth = width/svgImage->width; - const float scaleHeight = height/svgImage->height; - - // Set the largest of the 2 scales to be the scale to use - const float scale = (scaleHeight > scaleWidth) ? scaleWidth : scaleHeight; - - int offsetX = 0; - int offsetY = 0; - - if (scaleHeight > scaleWidth) + if (FileExists(fileNameOrString)) { - offsetY = (height - svgImage->height*scale) / 2; + int dataSize = 0; + unsigned char *fileData = LoadFileData(fileNameOrString, &dataSize); + isSvgStringValid = true; } else { - offsetX = (width - svgImage->width*scale) / 2; + // TODO: Validate it's a valid SVG string + isSvgStringValid = true; } - // Rasterize - struct NSVGrasterizer* rast = nsvgCreateRasterizer(); - nsvgRasterize(rast, svgImage, (int)offsetX, (int)offsetY, scale, img, width, height, width*4); + if (isSvgStringValid != NULL) + { + struct NSVGimage *svgImage = nsvgParse(fileNameOrString, "px", 96.0f); + + unsigned char *img = RL_MALLOC(width*height*4); - // Populate image struct with all data - image.data = img; - image.width = width; - image.height = height; - image.mipmaps = 1; - image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + // Calculate scales for both the width and the height + const float scaleWidth = width/svgImage->width; + const float scaleHeight = height/svgImage->height; - // Delete - nsvgDelete(svgImage); + // Set the largest of the 2 scales to be the scale to use + const float scale = (scaleHeight > scaleWidth)? scaleWidth : scaleHeight; + + int offsetX = 0; + int offsetY = 0; + + if (scaleHeight > scaleWidth) offsetY = (height - svgImage->height*scale) / 2; + else offsetX = (width - svgImage->width*scale) / 2; + + // Rasterize + struct NSVGrasterizer *rast = nsvgCreateRasterizer(); + nsvgRasterize(rast, svgImage, (int)offsetX, (int)offsetY, scale, img, width, height, width*4); + + // Populate image struct with all data + image.data = img; + image.width = width; + image.height = height; + image.mipmaps = 1; + image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + + // Free used memory + nsvgDelete(svgImage); + } } return image; @@ -515,6 +508,28 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i image.mipmaps = 1; } #endif +#if defined(SUPPORT_FILEFORMAT_SVG) + else if ((strcmp(fileType, ".svg") == 0) || (strcmp(fileType, ".SVG") == 0)) + { + // TODO: Validate fileData as valid SVG string data + + struct NSVGimage *svgImage = nsvgParse(fileData, "px", 96.0f); + unsigned char *img = RL_MALLOC(svgImage->width*svgImage->height*4); + + // Rasterize + struct NSVGrasterizer *rast = nsvgCreateRasterizer(); + nsvgRasterize(rast, svgImage, 0, 0, 1.0f, img, svgImage->width, svgImage->height, svgImage->width*4); + + // Populate image struct with all data + image.data = img; + image.width = svgImage->width; + image.height = svgImage->height; + image.mipmaps = 1; + image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + + nsvgDelete(svgImage); + } +#endif #if defined(SUPPORT_FILEFORMAT_DDS) else if ((strcmp(fileType, ".dds") == 0) || (strcmp(fileType, ".DDS") == 0)) { @@ -544,27 +559,6 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i { image.data = rl_load_astc_from_memory(fileData, dataSize, &image.width, &image.height, &image.format, &image.mipmaps); } -#endif -#if defined(SUPPORT_FILEFORMAT_SVG) - else if (strcmp(fileType, ".svg") == 0) - { - if (fileData != NULL) - { - // Creating a duplicate svg to read sizes from due to nsvgParse modifiying the string buffer. - unsigned char *duplicate = (unsigned char*)RL_MALLOC(dataSize); - memcpy(duplicate, fileData, dataSize); - struct NSVGimage *svgImage = nsvgParse(duplicate, "px", 96.0f); - RL_FREE(duplicate); - - const int width = (int)svgImage->width; - const int height = (int)svgImage->height; - // Delete - nsvgDelete(svgImage); - - - image = LoadImageSvgFromString(fileData, width, height); - } - } #endif else TRACELOG(LOG_WARNING, "IMAGE: Data format not supported"); From 67a693fc5be8cc8bdde3cf2e83dfffd031e84667 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 2 Sep 2023 19:20:56 +0200 Subject: [PATCH 0243/1350] REVIEWED: `LoadImageSvg()` --- src/rtextures.c | 79 +++++++++++++++++++++++++++++++------------------ 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index 6b7875b08..3092071fd 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -324,24 +324,35 @@ Image LoadImageSvg(const char *fileNameOrString, int width, int height) Image image = { 0 }; bool isSvgStringValid = false; - // TODO: Validate fileName or string + // Validate fileName or string if (fileNameOrString != NULL) { + int dataSize = 0; + unsigned char *fileData = NULL; + if (FileExists(fileNameOrString)) { - int dataSize = 0; - unsigned char *fileData = LoadFileData(fileNameOrString, &dataSize); + fileData = LoadFileData(fileNameOrString, &dataSize); isSvgStringValid = true; } else { - // TODO: Validate it's a valid SVG string - isSvgStringValid = true; + // Validate fileData as valid SVG string data + // + if ((fileNameOrString != NULL) && + (fileNameOrString[0] == '<') && + (fileNameOrString[1] == 's') && + (fileNameOrString[2] == 'v') && + (fileNameOrString[3] == 'g')) + { + fileData = fileNameOrString; + isSvgStringValid = true; + } } - if (isSvgStringValid != NULL) + if (isSvgStringValid) { - struct NSVGimage *svgImage = nsvgParse(fileNameOrString, "px", 96.0f); + struct NSVGimage *svgImage = nsvgParse(fileData, "px", 96.0f); unsigned char *img = RL_MALLOC(width*height*4); @@ -372,6 +383,8 @@ Image LoadImageSvg(const char *fileNameOrString, int width, int height) // Free used memory nsvgDelete(svgImage); } + + if (isSvgStringValid && (fileData != fileNameOrString)) UnloadFileData(fileData); } return image; @@ -500,34 +513,44 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i #if defined(SUPPORT_FILEFORMAT_QOI) else if ((strcmp(fileType, ".qoi") == 0) || (strcmp(fileType, ".QOI") == 0)) { - qoi_desc desc = { 0 }; - image.data = qoi_decode(fileData, dataSize, &desc, 4); - image.width = desc.width; - image.height = desc.height; - image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; - image.mipmaps = 1; + if (fileData != NULL) + { + qoi_desc desc = { 0 }; + image.data = qoi_decode(fileData, dataSize, &desc, 4); + image.width = desc.width; + image.height = desc.height; + image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + image.mipmaps = 1; + } } #endif #if defined(SUPPORT_FILEFORMAT_SVG) else if ((strcmp(fileType, ".svg") == 0) || (strcmp(fileType, ".SVG") == 0)) { - // TODO: Validate fileData as valid SVG string data - - struct NSVGimage *svgImage = nsvgParse(fileData, "px", 96.0f); - unsigned char *img = RL_MALLOC(svgImage->width*svgImage->height*4); - - // Rasterize - struct NSVGrasterizer *rast = nsvgCreateRasterizer(); - nsvgRasterize(rast, svgImage, 0, 0, 1.0f, img, svgImage->width, svgImage->height, svgImage->width*4); + // Validate fileData as valid SVG string data + // + if ((fileData != NULL) && + (fileData[0] == '<') && + (fileData[1] == 's') && + (fileData[2] == 'v') && + (fileData[3] == 'g')) + { + struct NSVGimage *svgImage = nsvgParse(fileData, "px", 96.0f); + unsigned char *img = RL_MALLOC(svgImage->width*svgImage->height*4); - // Populate image struct with all data - image.data = img; - image.width = svgImage->width; - image.height = svgImage->height; - image.mipmaps = 1; - image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + // Rasterize + struct NSVGrasterizer *rast = nsvgCreateRasterizer(); + nsvgRasterize(rast, svgImage, 0, 0, 1.0f, img, svgImage->width, svgImage->height, svgImage->width*4); - nsvgDelete(svgImage); + // Populate image struct with all data + image.data = img; + image.width = svgImage->width; + image.height = svgImage->height; + image.mipmaps = 1; + image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; + + nsvgDelete(svgImage); + } } #endif #if defined(SUPPORT_FILEFORMAT_DDS) From 8bd6bb622c53add567564fe567dcc1a23c7f7a3c Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Sat, 2 Sep 2023 17:38:10 -0300 Subject: [PATCH 0244/1350] Add SUPPORT_FILEFORMAT_SVG to cmake (#3284) --- CMakeOptions.txt | 1 + cmake/CompileDefinitions.cmake | 1 + 2 files changed, 2 insertions(+) diff --git a/CMakeOptions.txt b/CMakeOptions.txt index 83faa5637..260522340 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -71,6 +71,7 @@ cmake_dependent_option(SUPPORT_FILEFORMAT_QOI "Support loading QOI as textures" cmake_dependent_option(SUPPORT_FILEFORMAT_PSD "Support loading PSD as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_PKM "Support loading PKM as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_PVR "Support loading PVR as textures" ${OFF} CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_FILEFORMAT_SVG "Support loading SVG as textures" ON CUSTOMIZE_BUILD ON) # rtext.c cmake_dependent_option(SUPPORT_FILEFORMAT_FNT "Support loading fonts in FNT format" ON CUSTOMIZE_BUILD ON) diff --git a/cmake/CompileDefinitions.cmake b/cmake/CompileDefinitions.cmake index 1458e2f3c..68c53d7a1 100644 --- a/cmake/CompileDefinitions.cmake +++ b/cmake/CompileDefinitions.cmake @@ -49,6 +49,7 @@ if (${CUSTOMIZE_BUILD}) define_if("raylib" SUPPORT_FILEFORMAT_PSD) define_if("raylib" SUPPORT_FILEFORMAT_PKM) define_if("raylib" SUPPORT_FILEFORMAT_PVR) + define_if("raylib" SUPPORT_FILEFORMAT_SVG) define_if("raylib" SUPPORT_FILEFORMAT_FNT) define_if("raylib" SUPPORT_FILEFORMAT_TTF) define_if("raylib" SUPPORT_TEXT_MANIPULATION) From a69d4014339ed77ce531858503cf6b96c4cf708b Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Sat, 2 Sep 2023 17:50:59 -0300 Subject: [PATCH 0245/1350] Fix examples/textures/textures_fog_of_war.c help instructions (#3285) --- examples/textures/textures_fog_of_war.c | 3 ++- examples/textures/textures_fog_of_war.png | Bin 38477 -> 37736 bytes 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/textures/textures_fog_of_war.c b/examples/textures/textures_fog_of_war.c index 433f364a6..516055aeb 100644 --- a/examples/textures/textures_fog_of_war.c +++ b/examples/textures/textures_fog_of_war.c @@ -134,7 +134,8 @@ int main(void) (Vector2){ 0, 0 }, 0.0f, WHITE); // Draw player current tile - DrawText(TextFormat("Current tile: [%i,%i]", playerTileX, playerTileY), 10, 10, 20, LIME); + DrawText(TextFormat("Current tile: [%i,%i]", playerTileX, playerTileY), 10, 10, 20, RAYWHITE); + DrawText("ARROW KEYS to move", 10, screenHeight-25, 20, RAYWHITE); EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/examples/textures/textures_fog_of_war.png b/examples/textures/textures_fog_of_war.png index 6a91316ee53a91cc63262e31e3b1bf615c6220de..01563c3906f256433bc11af25d2a2a95517bd22b 100644 GIT binary patch literal 37736 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 O&$r*q2{_}&PyRpTk3qHo literal 38477 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 Date: Sun, 3 Sep 2023 06:03:00 -0300 Subject: [PATCH 0246/1350] Fix examples/textures/textures_image_rotate.c help instructions (#3286) --- examples/textures/textures_image_rotate.c | 4 +++- examples/textures/textures_image_rotate.png | Bin 24060 -> 35476 bytes 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/textures/textures_image_rotate.c b/examples/textures/textures_image_rotate.c index a590e1e0a..e5c735732 100644 --- a/examples/textures/textures_image_rotate.c +++ b/examples/textures/textures_image_rotate.c @@ -64,6 +64,8 @@ int main(void) DrawTexture(textures[currentTexture], screenWidth/2 - textures[currentTexture].width/2, screenHeight/2 - textures[currentTexture].height/2, WHITE); + DrawText("Press LEFT MOUSE BUTTON to rotate the image clockwise", 250, 420, 10, DARKGRAY); + EndDrawing(); //---------------------------------------------------------------------------------- } @@ -76,4 +78,4 @@ int main(void) //-------------------------------------------------------------------------------------- return 0; -} \ No newline at end of file +} diff --git a/examples/textures/textures_image_rotate.png b/examples/textures/textures_image_rotate.png index 64bbc54e496f4587e2609a58cea3ceee637123d1..862612e5ababd8a7c0034d173983f0f5c2931311 100644 GIT binary patch literal 35476 zcmeFZdpy(q|3AK&!{#`LoDZAH;R=m83v+0Q5M8BG%^{~G

    -PYomGl#oMQSGlHC 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 leEhsT8j7@l^pb#uA|Qewy(v{dKsuo+Eh0^N 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 From 8a82e17a794ac5ac8c073f9a96cf5e4ae0c1ebf2 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Sep 2023 18:43:03 +0200 Subject: [PATCH 0247/1350] Update rtextures.c --- src/rtextures.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rtextures.c b/src/rtextures.c index 3092071fd..4e2fdbd91 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -324,6 +324,7 @@ Image LoadImageSvg(const char *fileNameOrString, int width, int height) Image image = { 0 }; bool isSvgStringValid = false; +#if defined(SUPPORT_FILEFORMAT_SVG) // Validate fileName or string if (fileNameOrString != NULL) { @@ -386,6 +387,9 @@ Image LoadImageSvg(const char *fileNameOrString, int width, int height) if (isSvgStringValid && (fileData != fileNameOrString)) UnloadFileData(fileData); } +#else + TRACELOG(LOG_WARNING, "SVG image support not enabled, image can not be loaded"); +#endif return image; } From bdda1efd44d7a1dce1ce5ad03f7de14370642807 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Sep 2023 18:43:25 +0200 Subject: [PATCH 0248/1350] Fix #3247 --- src/rcore.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 912f85c0d..67907c367 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -887,9 +887,19 @@ void InitWindow(int width, int height, const char *title) // WARNING: External function: Module required: rtext LoadFontDefault(); #if defined(SUPPORT_MODULE_RSHAPES) + // Set font white rectangle for shapes drawing, so shapes and text can be batched together + // WARNING: rshapes module is required, if not available, default internal white rectangle is used Rectangle rec = GetFontDefault().recs[95]; - // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); // WARNING: Module required: rshapes + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); + } + else + { + // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); + } #endif #else #if defined(SUPPORT_MODULE_RSHAPES) From c104a975904f23d011cb6072f98945b8d38eff17 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 4 Sep 2023 18:51:44 +0200 Subject: [PATCH 0249/1350] Update config.h --- src/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config.h b/src/config.h index 2c7d5e2d0..370837610 100644 --- a/src/config.h +++ b/src/config.h @@ -156,7 +156,7 @@ //#define SUPPORT_FILEFORMAT_ASTC 1 //#define SUPPORT_FILEFORMAT_PKM 1 //#define SUPPORT_FILEFORMAT_PVR 1 -#define SUPPORT_FILEFORMAT_SVG 1 +//#define SUPPORT_FILEFORMAT_SVG 1 // Support image export functionality (.png, .bmp, .tga, .jpg, .qoi) #define SUPPORT_IMAGE_EXPORT 1 From a316f9e7fc7f8e06852a40544e57f89018672ee4 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Tue, 5 Sep 2023 05:02:25 -0400 Subject: [PATCH 0250/1350] Disable UBSAN in zig builds. (#3292) Zig debug builds automatically enable ubsan. As the fix for #1891 had to be reverted, debug builds using zig will crash like so: ``` Illegal instruction at address 0x3237d2 raylib/src/rlgl.h:3690:91: 0x3237d2 in rlDrawVertexArrayElements (/home/rcorre/src/raylib-zig-template/raylib/src/rcore.c) glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)buffer + offset); ``` This disables UBSAN when using zig to build raylib. --- src/build.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/build.zig b/src/build.zig index 27250f5ff..09d954bf5 100644 --- a/src/build.zig +++ b/src/build.zig @@ -6,6 +6,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built "-std=gnu99", "-D_GNU_SOURCE", "-DGL_SILENCE_DEPRECATION=199309L", + "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 }; const raylib = b.addStaticLibrary(.{ From 97a8fe1e15b0904231373dbe6a2918ecd0c5068d Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Tue, 5 Sep 2023 18:03:32 +0900 Subject: [PATCH 0251/1350] Update README.md (#3290) specially -> especially --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 90de36ca5..bff6980b0 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ **raylib is a simple and easy-to-use library to enjoy videogames programming.** -raylib is highly inspired by Borland BGI graphics lib and by XNA framework and it's specially well suited for prototyping, tooling, graphical applications, embedded systems and education. +raylib is highly inspired by Borland BGI graphics lib and by XNA framework and it's especially well suited for prototyping, tooling, graphical applications, embedded systems and education. *NOTE for ADVENTURERS: raylib is a programming library to enjoy videogames programming; no fancy interface, no visual helpers, no debug button... just coding in the most pure spartan-programmers way.* From 76fe16259d6cf8711d771ffb66b21e1673efa785 Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Tue, 5 Sep 2023 06:03:59 -0300 Subject: [PATCH 0252/1350] Update cmake SUPPORT_FILEFORMAT_SVG default value (#3291) --- CMakeOptions.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeOptions.txt b/CMakeOptions.txt index 260522340..799530951 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -71,7 +71,7 @@ cmake_dependent_option(SUPPORT_FILEFORMAT_QOI "Support loading QOI as textures" cmake_dependent_option(SUPPORT_FILEFORMAT_PSD "Support loading PSD as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_PKM "Support loading PKM as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_PVR "Support loading PVR as textures" ${OFF} CUSTOMIZE_BUILD OFF) -cmake_dependent_option(SUPPORT_FILEFORMAT_SVG "Support loading SVG as textures" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_SVG "Support loading SVG as textures" ${OFF} CUSTOMIZE_BUILD OFF) # rtext.c cmake_dependent_option(SUPPORT_FILEFORMAT_FNT "Support loading fonts in FNT format" ON CUSTOMIZE_BUILD ON) From 9cce5a93fffe6e297c004480b9b941d70bf4cd9e Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 5 Sep 2023 20:04:21 +0200 Subject: [PATCH 0253/1350] Fix #3293 --- src/rtext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtext.c b/src/rtext.c index 5ca97d06d..04d7a2a14 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1075,7 +1075,7 @@ void DrawFPS(int posX, int posY) if ((fps < 30) && (fps >= 15)) color = ORANGE; // Warning FPS else if (fps < 15) color = RED; // Low FPS - DrawText(TextFormat("%2i FPS", GetFPS()), posX, posY, 20, color); + DrawText(TextFormat("%2i FPS", fps), posX, posY, 20, color); } // Draw text (using default font) From d7debba2224b638a3429fcceffeade28dfa7a4cd Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 6 Sep 2023 23:16:31 +0200 Subject: [PATCH 0254/1350] Mouse offset and scaling must be considered also on web! --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 67907c367..138d3e4ad 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -4051,7 +4051,7 @@ Vector2 GetMousePosition(void) { Vector2 position = { 0 }; -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) +#if defined(PLATFORM_ANDROID) //|| defined(PLATFORM_WEB) position = GetTouchPosition(0); #else position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; From b0c6972e5cadb7c9e6680b8674fe378952efe668 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 6 Sep 2023 23:33:29 +0200 Subject: [PATCH 0255/1350] Update rcore.c --- src/rcore.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 138d3e4ad..c93038239 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -49,9 +49,6 @@ * #define SUPPORT_MOUSE_GESTURES * Mouse gestures are directly mapped like touches and processed by gestures system. * -* #define SUPPORT_TOUCH_AS_MOUSE -* Touch input and mouse input are shared. Mouse functions also return touch information. -* * #define SUPPORT_SSH_KEYBOARD_RPI (Raspberry Pi only) * Reconfigure standard input to receive key inputs, works with SSH connection. * WARNING: Reconfiguring standard input could lead to undesired effects, like breaking other From 8cf76ec113a049d60aa5d221543d35b8ddf25dcc Mon Sep 17 00:00:00 2001 From: SuperUserNameMan Date: Thu, 7 Sep 2023 17:35:02 +0200 Subject: [PATCH 0256/1350] Update Makefile : clean raygui.c & physac.c (#3296) --- src/Makefile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Makefile b/src/Makefile index e9b68cd93..ed7594938 100644 --- a/src/Makefile +++ b/src/Makefile @@ -827,7 +827,7 @@ clean: clean_shell_$(PLATFORM_SHELL) @echo "removed all generated files!" clean_shell_sh: - rm -fv *.o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).bc $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so* + rm -fv *.o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).bc $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so* raygui.c physac.c ifeq ($(PLATFORM),PLATFORM_ANDROID) rm -fv $(NATIVE_APP_GLUE)/android_native_app_glue.o endif @@ -839,4 +839,6 @@ clean_shell_cmd: cd $(RAYLIB_RELEASE_PATH) & \ del lib$(RAYLIB_LIB_NAME).a /s & \ del lib$(RAYLIB_LIB_NAME)dll.a /s & \ - del $(RAYLIB_LIB_NAME).dll /s + del $(RAYLIB_LIB_NAME).dll /s & \ + del raygui.c /s & \ + del physac.c /s From 18e9784c6d2f05ea82d5be0b86913b870e4fcf88 Mon Sep 17 00:00:00 2001 From: MichaelFiber <42419558+michaelfiber@users.noreply.github.com> Date: Thu, 7 Sep 2023 11:42:28 -0400 Subject: [PATCH 0257/1350] Remove PLATFORM_RPI (#3232) * Remove PLATFORM_RPI * remove build artifacts --------- Co-authored-by: MichaelFiber Co-authored-by: Ray --- cmake/LibraryConfigurations.cmake | 13 -- examples/Makefile | 50 +------ examples/Makefile.Web | 50 +------ examples/core/core_basic_window_web.c | 2 +- examples/core/core_input_gamepad.c | 9 +- examples/core/core_vr_simulator.c | 2 +- examples/models/models_skybox.c | 2 +- examples/others/raylib_opengl_interop.c | 2 +- examples/shaders/shaders_basic_lighting.c | 2 +- examples/shaders/shaders_custom_uniform.c | 2 +- examples/shaders/shaders_eratosthenes.c | 2 +- examples/shaders/shaders_fog.c | 2 +- examples/shaders/shaders_hot_reloading.c | 2 +- examples/shaders/shaders_hybrid_render.c | 2 +- examples/shaders/shaders_julia_set.c | 2 +- examples/shaders/shaders_lightmap.c | 2 +- examples/shaders/shaders_mesh_instancing.c | 2 +- examples/shaders/shaders_model_shader.c | 2 +- examples/shaders/shaders_multi_sample2d.c | 2 +- examples/shaders/shaders_palette_switch.c | 2 +- examples/shaders/shaders_postprocessing.c | 2 +- examples/shaders/shaders_raymarching.c | 2 +- examples/shaders/shaders_shapes_textures.c | 2 +- examples/shaders/shaders_simple_mask.c | 2 +- examples/shaders/shaders_spotlight.c | 2 +- examples/shaders/shaders_texture_drawing.c | 2 +- examples/shaders/shaders_texture_outline.c | 2 +- examples/shaders/shaders_texture_waves.c | 2 +- examples/shaders/shaders_write_depth.c | 2 +- examples/text/text_font_sdf.c | 2 +- projects/CMake/core_basic_window.c | 2 +- projects/VSCode/Makefile | 2 +- src/Makefile | 55 +------- src/rcore.c | 150 +++++---------------- src/rlgl.h | 2 +- 35 files changed, 70 insertions(+), 315 deletions(-) diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index d12df3a00..1debfeb42 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -73,19 +73,6 @@ elseif (${PLATFORM} MATCHES "Android") find_library(OPENGL_LIBRARY OpenGL) set(LIBS_PRIVATE m log android EGL GLESv2 OpenSLES atomic c) -elseif (${PLATFORM} MATCHES "Raspberry Pi") - set(PLATFORM_CPP "PLATFORM_RPI") - set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") - - add_definitions(-D_DEFAULT_SOURCE) - - find_library(GLESV2 brcmGLESv2 HINTS /opt/vc/lib) - find_library(EGL brcmEGL HINTS /opt/vc/lib) - find_library(BCMHOST bcm_host HINTS /opt/vc/lib) - include_directories(/opt/vc/include /opt/vc/include/interface/vmcs_host/linux /opt/vc/include/interface/vcos/pthreads) - link_directories(/opt/vc/lib) - set(LIBS_PRIVATE ${GLESV2} ${EGL} ${BCMHOST} pthread rt m dl) - elseif ("${PLATFORM}" MATCHES "DRM") set(PLATFORM_CPP "PLATFORM_DRM") set(GRAPHICS "GRAPHICS_API_OPENGL_ES2") diff --git a/examples/Makefile b/examples/Makefile index 4ba4f1720..338fe168c 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -25,7 +25,7 @@ # Define required environment variables #------------------------------------------------------------------------------------------------ -# Define target platform: PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB +# Define target platform: PLATFORM_DESKTOP, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB PLATFORM ?= PLATFORM_DESKTOP # Define required raylib variables @@ -58,15 +58,6 @@ BUILD_WEB_HEAP_SIZE ?= 134217728 BUILD_WEB_RESOURCES ?= TRUE BUILD_WEB_RESOURCES_PATH ?= $(dir $<)resources@resources -# Use cross-compiler for PLATFORM_RPI -ifeq ($(PLATFORM),PLATFORM_RPI) - USE_RPI_CROSS_COMPILER ?= FALSE - ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) - RPI_TOOLCHAIN ?= C:/SysGCC/Raspberry - RPI_TOOLCHAIN_SYSROOT ?= $(RPI_TOOLCHAIN)/arm-linux-gnueabihf/sysroot - endif -endif - # Determine PLATFORM_OS in case PLATFORM_DESKTOP or PLATFORM_WEB selected ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_WEB)) # No uname.exe on MinGW!, but OS=Windows_NT on Windows! @@ -95,12 +86,6 @@ ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_WEB)) endif endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - UNAMEOS = $(shell uname) - ifeq ($(UNAMEOS),Linux) - PLATFORM_OS = LINUX - endif -endif ifeq ($(PLATFORM),PLATFORM_DRM) UNAMEOS = $(shell uname) ifeq ($(UNAMEOS),Linux) @@ -118,9 +103,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) endif # Default path for raylib on Raspberry Pi -ifeq ($(PLATFORM),PLATFORM_RPI) - RAYLIB_PATH ?= /home/pi/raylib -endif ifeq ($(PLATFORM),PLATFORM_DRM) RAYLIB_PATH ?= /home/pi/raylib endif @@ -154,13 +136,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) CC = clang endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) - # Define RPI cross-compiler - #CC = armv6j-hardfloat-linux-gnueabi-gcc - CC = $(RPI_TOOLCHAIN)/bin/arm-linux-gnueabihf-gcc - endif -endif ifeq ($(PLATFORM),PLATFORM_WEB) # HTML5 emscripten compiler # WARNING: To compile to HTML5, code must be redesigned @@ -231,9 +206,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) endif endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - CFLAGS += -std=gnu99 -endif ifeq ($(PLATFORM),PLATFORM_DRM) CFLAGS += -std=gnu99 -DEGL_NO_X11 endif @@ -252,11 +224,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) INCLUDE_PATHS += -I$(RAYLIB_INCLUDE_PATH) endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include - INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include/interface/vmcs_host/linux - INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include/interface/vcos/pthreads -endif ifeq ($(PLATFORM),PLATFORM_DRM) INCLUDE_PATHS += -I/usr/include/libdrm endif @@ -328,9 +295,6 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # logic to a self contained function: UpdateDrawFrame(), check core_basic_window_web.c for reference. endif -ifeq ($(PLATFORM),PLATFORM_RPI) - LDFLAGS += -L$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/lib -endif # Define libraries required on linking: LDLIBS # NOTE: To link libraries (lib.so or lib.a), use -l @@ -381,14 +345,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDLIBS += -lglfw endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - # Libraries for Raspberry Pi compiling - # NOTE: Required packages: libasound2-dev (ALSA) - LDLIBS = -lraylib -lbrcmGLESv2 -lbrcmEGL -lpthread -lrt -lm -lbcm_host -ldl -latomic - ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) - LDLIBS += -lvchiq_arm -lvcos - endif -endif ifeq ($(PLATFORM),PLATFORM_DRM) # Libraries for DRM compiling # NOTE: Required packages: libasound2-dev (ALSA) @@ -593,10 +549,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) rm -f *.o endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - find . -type f -executable -delete - rm -fv *.o -endif ifeq ($(PLATFORM),PLATFORM_DRM) find . -type f -executable -delete rm -fv *.o diff --git a/examples/Makefile.Web b/examples/Makefile.Web index ccb4550f9..518fab918 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -25,7 +25,7 @@ # Define required environment variables #------------------------------------------------------------------------------------------------ -# Define target platform: PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB +# Define target platform: PLATFORM_DESKTOP, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB PLATFORM ?= PLATFORM_WEB # Define required raylib variables @@ -51,15 +51,6 @@ USE_EXTERNAL_GLFW ?= FALSE # NOTE: This variable is only used for PLATFORM_OS: LINUX USE_WAYLAND_DISPLAY ?= FALSE -# Use cross-compiler for PLATFORM_RPI -ifeq ($(PLATFORM),PLATFORM_RPI) - USE_RPI_CROSS_COMPILER ?= FALSE - ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) - RPI_TOOLCHAIN ?= C:/SysGCC/Raspberry - RPI_TOOLCHAIN_SYSROOT ?= $(RPI_TOOLCHAIN)/arm-linux-gnueabihf/sysroot - endif -endif - # Determine PLATFORM_OS in case PLATFORM_DESKTOP or PLATFORM_WEB selected ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_WEB)) # No uname.exe on MinGW!, but OS=Windows_NT on Windows! @@ -88,12 +79,6 @@ ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_WEB)) endif endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - UNAMEOS = $(shell uname) - ifeq ($(UNAMEOS),Linux) - PLATFORM_OS = LINUX - endif -endif ifeq ($(PLATFORM),PLATFORM_DRM) UNAMEOS = $(shell uname) ifeq ($(UNAMEOS),Linux) @@ -111,9 +96,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) endif # Default path for raylib on Raspberry Pi -ifeq ($(PLATFORM),PLATFORM_RPI) - RAYLIB_PATH ?= /home/pi/raylib -endif ifeq ($(PLATFORM),PLATFORM_DRM) RAYLIB_PATH ?= /home/pi/raylib endif @@ -147,13 +129,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) CC = clang endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) - # Define RPI cross-compiler - #CC = armv6j-hardfloat-linux-gnueabi-gcc - CC = $(RPI_TOOLCHAIN)/bin/arm-linux-gnueabihf-gcc - endif -endif ifeq ($(PLATFORM),PLATFORM_WEB) # HTML5 emscripten compiler # WARNING: To compile to HTML5, code must be redesigned @@ -224,9 +199,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) endif endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - CFLAGS += -std=gnu99 -endif ifeq ($(PLATFORM),PLATFORM_DRM) CFLAGS += -std=gnu99 -DEGL_NO_X11 endif @@ -245,11 +217,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) INCLUDE_PATHS += -I$(RAYLIB_INCLUDE_PATH) endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include - INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include/interface/vmcs_host/linux - INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include/interface/vcos/pthreads -endif ifeq ($(PLATFORM),PLATFORM_DRM) INCLUDE_PATHS += -I/usr/include/libdrm endif @@ -304,9 +271,6 @@ ifeq ($(PLATFORM),PLATFORM_WEB) LDFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html EXT = .html endif -ifeq ($(PLATFORM),PLATFORM_RPI) - LDFLAGS += -L$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/lib -endif # Define libraries required on linking: LDLIBS # NOTE: To link libraries (lib.so or lib.a), use -l @@ -357,14 +321,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDLIBS += -lglfw endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - # Libraries for Raspberry Pi compiling - # NOTE: Required packages: libasound2-dev (ALSA) - LDLIBS = -lraylib -lbrcmGLESv2 -lbrcmEGL -lpthread -lrt -lm -lbcm_host -ldl -latomic - ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) - LDLIBS += -lvchiq_arm -lvcos - endif -endif ifeq ($(PLATFORM),PLATFORM_DRM) # Libraries for DRM compiling # NOTE: Required packages: libasound2-dev (ALSA) @@ -1067,10 +1023,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) rm -f *.o endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - find . -type f -executable -delete - rm -fv *.o -endif ifeq ($(PLATFORM),PLATFORM_DRM) find . -type f -executable -delete rm -fv *.o diff --git a/examples/core/core_basic_window_web.c b/examples/core/core_basic_window_web.c index 7d83254a1..e0e1f9fb1 100644 --- a/examples/core/core_basic_window_web.c +++ b/examples/core/core_basic_window_web.c @@ -2,7 +2,7 @@ * * raylib [core] example - Basic window (adapted for HTML5 platform) * -* NOTE: This example is prepared to compile for PLATFORM_WEB, PLATFORM_DESKTOP and PLATFORM_RPI +* NOTE: This example is prepared to compile for PLATFORM_WEB, and PLATFORM_DESKTOP * As you will notice, code structure is slightly diferent to the other examples... * To compile it for PLATFORM_WEB just uncomment #define PLATFORM_WEB at beginning * diff --git a/examples/core/core_input_gamepad.c b/examples/core/core_input_gamepad.c index 1eb516e85..c28ff1f9e 100644 --- a/examples/core/core_input_gamepad.c +++ b/examples/core/core_input_gamepad.c @@ -21,13 +21,8 @@ // NOTE: Gamepad name ID depends on drivers and OS #define XBOX360_LEGACY_NAME_ID "Xbox Controller" -#if defined(PLATFORM_RPI) - #define XBOX360_NAME_ID "Microsoft X-Box 360 pad" - #define PS3_NAME_ID "PLAYSTATION(R)3 Controller" -#else - #define XBOX360_NAME_ID "Xbox 360 Controller" - #define PS3_NAME_ID "PLAYSTATION(R)3 Controller" -#endif +#define XBOX360_NAME_ID "Xbox 360 Controller" +#define PS3_NAME_ID "PLAYSTATION(R)3 Controller" //------------------------------------------------------------------------------------ // Program main entry point diff --git a/examples/core/core_vr_simulator.c b/examples/core/core_vr_simulator.c index bc69cc698..fc2dee6b9 100644 --- a/examples/core/core_vr_simulator.c +++ b/examples/core/core_vr_simulator.c @@ -15,7 +15,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/models/models_skybox.c b/examples/models/models_skybox.c index 34616de59..7a500e04d 100644 --- a/examples/models/models_skybox.c +++ b/examples/models/models_skybox.c @@ -18,7 +18,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/others/raylib_opengl_interop.c b/examples/others/raylib_opengl_interop.c index bcdbbbdd5..09b548c60 100644 --- a/examples/others/raylib_opengl_interop.c +++ b/examples/others/raylib_opengl_interop.c @@ -42,7 +42,7 @@ #endif #define GLSL_VERSION 330 #endif -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_basic_lighting.c b/examples/shaders/shaders_basic_lighting.c index 61cec2e80..3fbe60b83 100644 --- a/examples/shaders/shaders_basic_lighting.c +++ b/examples/shaders/shaders_basic_lighting.c @@ -27,7 +27,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_custom_uniform.c b/examples/shaders/shaders_custom_uniform.c index eaeca5e51..0a1a76428 100644 --- a/examples/shaders/shaders_custom_uniform.c +++ b/examples/shaders/shaders_custom_uniform.c @@ -22,7 +22,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_eratosthenes.c b/examples/shaders/shaders_eratosthenes.c index a481f3008..005a97aea 100644 --- a/examples/shaders/shaders_eratosthenes.c +++ b/examples/shaders/shaders_eratosthenes.c @@ -29,7 +29,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_fog.c b/examples/shaders/shaders_fog.c index 24a1c1068..cd36936d6 100644 --- a/examples/shaders/shaders_fog.c +++ b/examples/shaders/shaders_fog.c @@ -27,7 +27,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_hot_reloading.c b/examples/shaders/shaders_hot_reloading.c index 395d44cfe..2b1175e4b 100644 --- a/examples/shaders/shaders_hot_reloading.c +++ b/examples/shaders/shaders_hot_reloading.c @@ -21,7 +21,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_hybrid_render.c b/examples/shaders/shaders_hybrid_render.c index f07917fb0..53e14b884 100644 --- a/examples/shaders/shaders_hybrid_render.c +++ b/examples/shaders/shaders_hybrid_render.c @@ -20,7 +20,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_julia_set.c b/examples/shaders/shaders_julia_set.c index ee1988058..aebb287a1 100644 --- a/examples/shaders/shaders_julia_set.c +++ b/examples/shaders/shaders_julia_set.c @@ -22,7 +22,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_lightmap.c b/examples/shaders/shaders_lightmap.c index b636c8b28..c5ed6094c 100644 --- a/examples/shaders/shaders_lightmap.c +++ b/examples/shaders/shaders_lightmap.c @@ -25,7 +25,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_mesh_instancing.c b/examples/shaders/shaders_mesh_instancing.c index 7789f2cb0..7d603a6bf 100644 --- a/examples/shaders/shaders_mesh_instancing.c +++ b/examples/shaders/shaders_mesh_instancing.c @@ -24,7 +24,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_model_shader.c b/examples/shaders/shaders_model_shader.c index e84ecbfbe..739a33b7e 100644 --- a/examples/shaders/shaders_model_shader.c +++ b/examples/shaders/shaders_model_shader.c @@ -22,7 +22,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_multi_sample2d.c b/examples/shaders/shaders_multi_sample2d.c index d6f8803b7..f7c369bc4 100644 --- a/examples/shaders/shaders_multi_sample2d.c +++ b/examples/shaders/shaders_multi_sample2d.c @@ -22,7 +22,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_palette_switch.c b/examples/shaders/shaders_palette_switch.c index bb1eda4e0..6b108b092 100644 --- a/examples/shaders/shaders_palette_switch.c +++ b/examples/shaders/shaders_palette_switch.c @@ -24,7 +24,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_postprocessing.c b/examples/shaders/shaders_postprocessing.c index 7f6bd0092..1a7c621e5 100644 --- a/examples/shaders/shaders_postprocessing.c +++ b/examples/shaders/shaders_postprocessing.c @@ -22,7 +22,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_raymarching.c b/examples/shaders/shaders_raymarching.c index 7b34a5233..e9b7755ad 100644 --- a/examples/shaders/shaders_raymarching.c +++ b/examples/shaders/shaders_raymarching.c @@ -18,7 +18,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB -> Not supported at this moment +#else // PLATFORM_ANDROID, PLATFORM_WEB -> Not supported at this moment #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_shapes_textures.c b/examples/shaders/shaders_shapes_textures.c index d3ec8daf0..cca4959ed 100644 --- a/examples/shaders/shaders_shapes_textures.c +++ b/examples/shaders/shaders_shapes_textures.c @@ -22,7 +22,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_simple_mask.c b/examples/shaders/shaders_simple_mask.c index 6283ccbcc..7ed315b0a 100644 --- a/examples/shaders/shaders_simple_mask.c +++ b/examples/shaders/shaders_simple_mask.c @@ -25,7 +25,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_spotlight.c b/examples/shaders/shaders_spotlight.c index c96c983d0..067f9622d 100644 --- a/examples/shaders/shaders_spotlight.c +++ b/examples/shaders/shaders_spotlight.c @@ -34,7 +34,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_texture_drawing.c b/examples/shaders/shaders_texture_drawing.c index 006168d57..43ffa21ef 100644 --- a/examples/shaders/shaders_texture_drawing.c +++ b/examples/shaders/shaders_texture_drawing.c @@ -19,7 +19,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_texture_outline.c b/examples/shaders/shaders_texture_outline.c index 09f625bb5..83f2820e7 100644 --- a/examples/shaders/shaders_texture_outline.c +++ b/examples/shaders/shaders_texture_outline.c @@ -20,7 +20,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_texture_waves.c b/examples/shaders/shaders_texture_waves.c index 27ad1f6e4..5bdaada44 100644 --- a/examples/shaders/shaders_texture_waves.c +++ b/examples/shaders/shaders_texture_waves.c @@ -24,7 +24,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/shaders/shaders_write_depth.c b/examples/shaders/shaders_write_depth.c index 048e297ad..d9e40d0dd 100644 --- a/examples/shaders/shaders_write_depth.c +++ b/examples/shaders/shaders_write_depth.c @@ -19,7 +19,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/examples/text/text_font_sdf.c b/examples/text/text_font_sdf.c index 6a223ae5c..cba47b438 100644 --- a/examples/text/text_font_sdf.c +++ b/examples/text/text_font_sdf.c @@ -15,7 +15,7 @@ #if defined(PLATFORM_DESKTOP) #define GLSL_VERSION 330 -#else // PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +#else // PLATFORM_ANDROID, PLATFORM_WEB #define GLSL_VERSION 100 #endif diff --git a/projects/CMake/core_basic_window.c b/projects/CMake/core_basic_window.c index 0e5542967..86709f7a9 100644 --- a/projects/CMake/core_basic_window.c +++ b/projects/CMake/core_basic_window.c @@ -2,7 +2,7 @@ * * raylib [core] example - Basic window (adapted for HTML5 platform) * -* This example is prepared to compile for PLATFORM_WEB, PLATFORM_DESKTOP and PLATFORM_RPI +* This example is prepared to compile for PLATFORM_WEB and PLATFORM_DESKTOP * As you will notice, code structure is slightly different to the other examples... * To compile it for PLATFORM_WEB just uncomment #define PLATFORM_WEB at beginning * diff --git a/projects/VSCode/Makefile b/projects/VSCode/Makefile index 9ab042e8f..a2a26f092 100644 --- a/projects/VSCode/Makefile +++ b/projects/VSCode/Makefile @@ -32,7 +32,7 @@ RAYLIB_PATH ?= ..\.. COMPILER_PATH ?= C:/raylib/w64devkit/bin # Define default options -# One of PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_ANDROID, PLATFORM_WEB +# One of PLATFORM_DESKTOP, PLATFORM_ANDROID, PLATFORM_WEB PLATFORM ?= PLATFORM_DESKTOP # Locations of your newly installed library and associated headers. See ../src/Makefile diff --git a/src/Makefile b/src/Makefile index ed7594938..63cbe2a41 100644 --- a/src/Makefile +++ b/src/Makefile @@ -8,7 +8,6 @@ # PLATFORM_DESKTOP: OSX/macOS (arm64, x86_64) # PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly # PLATFORM_ANDROID: Android (arm, i686, arm64, x86_64) -# PLATFORM_RPI: Raspberry Pi (deprecated - RPI OS Buster only) # PLATFORM_DRM: Linux native mode, including Raspberry Pi (RPI OS Bullseye) # PLATFORM_WEB: HTML5 (Chrome, Firefox) # @@ -41,7 +40,7 @@ # Define required environment variables #------------------------------------------------------------------------------------------------ -# Define target platform: PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB +# Define target platform: PLATFORM_DESKTOP, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB PLATFORM ?= PLATFORM_DESKTOP # Define required raylib variables @@ -94,14 +93,6 @@ USE_EXTERNAL_GLFW ?= FALSE # NOTE: This variable is only used for PLATFORM_OS: LINUX USE_WAYLAND_DISPLAY ?= FALSE -# Use cross-compiler for PLATFORM_RPI -USE_RPI_CROSS_COMPILER ?= FALSE -ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) - RPI_TOOLCHAIN ?= C:/SysGCC/Raspberry - RPI_TOOLCHAIN_NAME ?= arm-linux-gnueabihf - RPI_TOOLCHAIN_SYSROOT ?= $(RPI_TOOLCHAIN)/$(RPI_TOOLCHAIN_NAME)/sysroot -endif - # Determine if the file has root access (only required to install raylib) # "whoami" prints the name of the user that calls him (so, if it is the root user, "whoami" prints "root") ROOT = $(shell whoami) @@ -144,15 +135,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) endif endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - UNAMEOS = $(shell uname) - ifeq ($(UNAMEOS),Linux) - PLATFORM_OS = LINUX - endif - ifndef PLATFORM_SHELL - PLATFORM_SHELL = sh - endif -endif ifeq ($(PLATFORM),PLATFORM_DRM) UNAMEOS = $(shell uname) ifeq ($(UNAMEOS),Linux) @@ -235,10 +217,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 #GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE) endif -ifeq ($(PLATFORM),PLATFORM_RPI) - # On RPI OpenGL ES 2.0 must be used - GRAPHICS = GRAPHICS_API_OPENGL_ES2 -endif + ifeq ($(PLATFORM),PLATFORM_DRM) # On DRM OpenGL ES 2.0 must be used GRAPHICS = GRAPHICS_API_OPENGL_ES2 @@ -269,14 +248,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) CC = clang endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) - # Define RPI cross-compiler - #CC = armv6j-hardfloat-linux-gnueabi-gcc - CC = $(RPI_TOOLCHAIN)/bin/$(RPI_TOOLCHAIN_NAME)-gcc - AR = $(RPI_TOOLCHAIN)/bin/$(RPI_TOOLCHAIN_NAME)-ar - endif -endif ifeq ($(PLATFORM),PLATFORM_DRM) ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) # Define RPI cross-compiler @@ -452,11 +423,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) INCLUDE_PATHS += -I/usr/local/include endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include - INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include/interface/vmcs_host/linux - INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include/interface/vcos/pthreads -endif ifeq ($(PLATFORM),PLATFORM_DRM) INCLUDE_PATHS += -I/usr/include/libdrm ifeq ($(USE_RPI_CROSSCOMPILER), TRUE) @@ -506,9 +472,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).$(RAYLIB_API_VERSION).so -Lsrc -L/usr/local/lib endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) -L$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/lib -endif ifeq ($(PLATFORM),PLATFORM_DRM) LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) ifeq ($(USE_RPI_CROSSCOMPILER), TRUE) @@ -557,12 +520,6 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDLIBS = -lglfw endif endif -ifeq ($(PLATFORM),PLATFORM_RPI) - LDLIBS = -lbrcmGLESv2 -lbrcmEGL -lpthread -lrt -lm -lbcm_host -ldl - ifeq ($(RAYLIB_MODULE_AUDIO),TRUE) - LDLIBS += -latomic - endif -endif ifeq ($(PLATFORM),PLATFORM_DRM) LDLIBS = -lGLESv2 -lEGL -ldrm -lgbm -lpthread -lrt -lm -ldl ifeq ($(RAYLIB_MODULE_AUDIO),TRUE) @@ -647,14 +604,6 @@ else cd $(RAYLIB_RELEASE_PATH) && ln -fs lib$(RAYLIB_LIB_NAME).$(RAYLIB_VERSION).so lib$(RAYLIB_LIB_NAME).so endif endif - ifeq ($(PLATFORM),PLATFORM_RPI) - # Compile raylib shared library version $(RAYLIB_VERSION). - # WARNING: you should type "make clean" before doing this target - $(CC) -shared -o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_VERSION) $(OBJS) $(LDFLAGS) $(LDLIBS) - @echo "raylib shared library generated (lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_VERSION)) in $(RAYLIB_RELEASE_PATH)!" - cd $(RAYLIB_RELEASE_PATH) && ln -fsv lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_VERSION) lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) - cd $(RAYLIB_RELEASE_PATH) && ln -fsv lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) lib$(RAYLIB_LIB_NAME).so - endif ifeq ($(PLATFORM),PLATFORM_DRM) # Compile raylib shared library version $(RAYLIB_VERSION). # WARNING: you should type "make clean" before doing this target diff --git a/src/rcore.c b/src/rcore.c index c93038239..fcb222bb3 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -8,7 +8,6 @@ * - PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) * - PLATFORM_DESKTOP: OSX/macOS * - PLATFORM_ANDROID: Android (ARM, ARM64) -* - PLATFORM_RPI: Raspberry Pi 0,1,2,3 (Raspbian, native mode) * - PLATFORM_DRM: Linux native mode, including Raspberry Pi 4 with V3D fkms driver * - PLATFORM_WEB: HTML5 with WebAssembly * @@ -21,12 +20,6 @@ * Windowing and input system configured for Android device, app activity managed internally in this module. * NOTE: OpenGL ES 2.0 is required and graphic device is managed by EGL * -* #define PLATFORM_RPI (deprecated - RPI OS Buster only) -* Windowing and input system configured for Raspberry Pi in native mode (no XWindow required), -* graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/ -* WARNING: This platform is deprecated, since RPI OS Bullseye, the old Dispmanx libraries are not -* supported and you must be using PLATFORM_DRM -* * #define PLATFORM_DRM * Windowing and input system configured for DRM native mode (RPI4 and other devices) * graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/ @@ -260,7 +253,7 @@ //#include // OpenGL ES 2.0 library (not required in this module, only in rlgl) #endif -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) #include // POSIX file control definitions - open(), creat(), fcntl() #include // POSIX standard function definitions - read(), close(), STDIN_FILENO #include // POSIX terminal control definitions - tcgetattr(), tcsetattr() @@ -272,9 +265,6 @@ #include // Linux: Keycodes constants definition (KEY_A, ...) #include // Linux: Joystick support library -#if defined(PLATFORM_RPI) - #include "bcm_host.h" // Raspberry Pi VideoCore IV access functions -#endif #if defined(PLATFORM_DRM) #include // Generic Buffer Management (native platform for EGL on DRM) #include // Direct Rendering Manager user-level library interface @@ -299,7 +289,7 @@ //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) #define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event number #define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...) @@ -351,7 +341,7 @@ //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) typedef struct { pthread_t threadId; // Event reading thread id int fd; // File descriptor to the device it is assigned to @@ -375,10 +365,7 @@ typedef struct CoreData { #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) GLFWwindow *handle; // GLFW window handle (graphic device) #endif -#if defined(PLATFORM_RPI) - EGL_DISPMANX_WINDOW_T handle; // Native window handle (graphic device) -#endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) #if defined(PLATFORM_DRM) int fd; // File descriptor for /dev/dri/... drmModeConnector *connector; // Direct Rendering Manager (DRM) mode connector @@ -428,7 +415,7 @@ typedef struct CoreData { const char *basePath; // Base path for data storage } Storage; struct { -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) InputEventWorker eventWorker[10]; // List of worker threads for every monitored "/dev/input/event" #endif struct { @@ -444,7 +431,7 @@ typedef struct CoreData { int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode) int charPressedQueueCount; // Input characters queue count -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) int defaultMode; // Default keyboard mode #if defined(SUPPORT_SSH_KEYBOARD_RPI) bool evtMode; // Keyboard in event mode @@ -468,7 +455,7 @@ typedef struct CoreData { char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state Vector2 currentWheelMove; // Registers current mouse wheel variation Vector2 previousWheelMove; // Registers previous mouse wheel variation -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) Vector2 eventWheelMove; // Registers the event mouse wheel variation // NOTE: currentButtonState[] can't be written directly due to multithreading, app could miss the update char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab @@ -489,7 +476,7 @@ typedef struct CoreData { char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) pthread_t threadId; // Gamepad reading thread id int streamId[MAX_GAMEPADS]; // Gamepad device file descriptor #endif @@ -502,7 +489,7 @@ typedef struct CoreData { double draw; // Time measure for frame draw double frame; // Time measure for one frame double target; // Desired time for one frame, if 0 not applied -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) unsigned long long int base; // Base time measure for hi-res timer #endif unsigned int frameCounter; // Frame counter @@ -667,7 +654,7 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData); #endif -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) static void InitKeyboard(void); // Initialize raw keyboard system static void RestoreKeyboard(void); // Restore keyboard system #if defined(SUPPORT_SSH_KEYBOARD_RPI) @@ -682,13 +669,11 @@ static void *EventThread(void *arg); // Input device events r static void InitGamepad(void); // Initialize raw gamepad input static void *GamepadThread(void *arg); // Mouse reading thread -#if defined(PLATFORM_DRM) static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode); // Search matching DRM mode in connector's mode list static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced); // Search exactly matching DRM connector mode in connector's list static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced); // Search the nearest matching DRM connector mode in connector's list -#endif -#endif // PLATFORM_RPI || PLATFORM_DRM +#endif // PLATFORM_DRM #if defined(SUPPORT_EVENTS_AUTOMATION) static void LoadAutomationEvents(const char *fileName); // Load automation events from file @@ -857,7 +842,7 @@ void InitWindow(int width, int height, const char *title) } } #endif -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_DRM) // Initialize graphics device (display device and OpenGL context) // NOTE: returns true if window and graphic device has been initialized successfully CORE.Window.ready = InitGraphicsDevice(width, height); @@ -916,7 +901,7 @@ void InitWindow(int width, int height, const char *title) } #endif -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) // Initialize raw input system InitEvdevInput(); // Evdev inputs initialization InitGamepad(); // Gamepad init @@ -958,7 +943,7 @@ void InitWindow(int width, int height, const char *title) CORE.Time.frameCounter = 0; #endif -#endif // PLATFORM_DESKTOP || PLATFORM_WEB || PLATFORM_RPI || PLATFORM_DRM +#endif // PLATFORM_DESKTOP || PLATFORM_WEB || PLATFORM_DRM } // Close window and unload OpenGL context @@ -988,7 +973,7 @@ void CloseWindow(void) timeEndPeriod(1); // Restore time period #endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) +#if defined(PLATFORM_ANDROID) // Close surface, context and display if (CORE.Window.device != EGL_NO_DISPLAY) { @@ -1076,7 +1061,7 @@ void CloseWindow(void) } #endif -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) // Wait for mouse and gamepad threads to finish before closing // NOTE: Those threads should already have finished at this point // because they are controlled by CORE.Window.shouldClose variable @@ -1138,7 +1123,7 @@ bool WindowShouldClose(void) else return true; #endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) if (CORE.Window.ready) return CORE.Window.shouldClose; else return true; #endif @@ -1324,7 +1309,7 @@ void ToggleFullscreen(void) CORE.Window.fullscreen = !CORE.Window.fullscreen; // Toggle fullscreen flag #endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) TRACELOG(LOG_WARNING, "SYSTEM: Failed to toggle to windowed mode"); #endif } @@ -3047,7 +3032,7 @@ double GetTime(void) time = glfwGetTime(); // Elapsed time since glfwInit() #endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) struct timespec ts = { 0 }; clock_gettime(CLOCK_MONOTONIC, &ts); unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; @@ -3883,7 +3868,7 @@ const char *GetGamepadName(int gamepad) if (CORE.Input.Gamepad.ready[gamepad]) return glfwGetJoystickName(gamepad); else return NULL; #endif -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); return CORE.Input.Gamepad.name[gamepad]; #endif @@ -3896,7 +3881,7 @@ const char *GetGamepadName(int gamepad) // Get gamepad axis count int GetGamepadAxisCount(int gamepad) { -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) int axisCount = 0; if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGAXES, &axisCount); CORE.Input.Gamepad.axisCount = axisCount; @@ -4138,7 +4123,7 @@ int GetTouchX(void) { #if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) return (int)CORE.Input.Touch.position[0].x; -#else // PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_DRM +#else // PLATFORM_DESKTOP, PLATFORM_DRM return GetMouseX(); #endif } @@ -4148,7 +4133,7 @@ int GetTouchY(void) { #if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) return (int)CORE.Input.Touch.position[0].y; -#else // PLATFORM_DESKTOP, PLATFORM_RPI, PLATFORM_DRM +#else // PLATFORM_DESKTOP, PLATFORM_DRM return GetMouseY(); #endif } @@ -4165,7 +4150,7 @@ Vector2 GetTouchPosition(int index) // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages if (index == 0) position = GetMousePosition(); #endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) || defined(PLATFORM_DRM) if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index]; else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS); #endif @@ -4537,21 +4522,10 @@ static bool InitGraphicsDevice(int width, int height) #endif // PLATFORM_DESKTOP || PLATFORM_WEB -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; -#if defined(PLATFORM_RPI) - bcm_host_init(); - - DISPMANX_ELEMENT_HANDLE_T dispmanElement = { 0 }; - DISPMANX_DISPLAY_HANDLE_T dispmanDisplay = { 0 }; - DISPMANX_UPDATE_HANDLE_T dispmanUpdate = { 0 }; - - VC_RECT_T dstRect = { 0 }; - VC_RECT_T srcRect = { 0 }; -#endif - #if defined(PLATFORM_DRM) CORE.Window.fd = -1; CORE.Window.connector = NULL; @@ -4748,7 +4722,7 @@ static bool InitGraphicsDevice(int width, int height) EGL_NONE }; -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) EGLint numConfigs = 0; // Get an EGL device connection @@ -4863,58 +4837,6 @@ static bool InitGraphicsDevice(int width, int height) CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, CORE.Android.app->window, NULL); #endif // PLATFORM_ANDROID -#if defined(PLATFORM_RPI) - graphics_get_display_size(0, &CORE.Window.display.width, &CORE.Window.display.height); - - // Screen size security check - if (CORE.Window.screen.width <= 0) CORE.Window.screen.width = CORE.Window.display.width; - if (CORE.Window.screen.height <= 0) CORE.Window.screen.height = CORE.Window.display.height; - - // At this point we need to manage render size vs screen size - // NOTE: This function use and modify global module variables: - // -> CORE.Window.screen.width/CORE.Window.screen.height - // -> CORE.Window.render.width/CORE.Window.render.height - // -> CORE.Window.screenScale - SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); - - dstRect.x = 0; - dstRect.y = 0; - dstRect.width = CORE.Window.display.width; - dstRect.height = CORE.Window.display.height; - - srcRect.x = 0; - srcRect.y = 0; - srcRect.width = CORE.Window.render.width << 16; - srcRect.height = CORE.Window.render.height << 16; - - // NOTE: RPI dispmanx windowing system takes care of source rectangle scaling to destination rectangle by hardware (no cost) - // Take care that renderWidth/renderHeight fit on displayWidth/displayHeight aspect ratio - - VC_DISPMANX_ALPHA_T alpha = { 0 }; - alpha.flags = DISPMANX_FLAGS_ALPHA_FIXED_ALL_PIXELS; - //alpha.flags = DISPMANX_FLAGS_ALPHA_FROM_SOURCE; // TODO: Allow transparent framebuffer! -> FLAG_WINDOW_TRANSPARENT - alpha.opacity = 255; // Set transparency level for framebuffer, requires EGLAttrib: EGL_TRANSPARENT_TYPE - alpha.mask = 0; - - dispmanDisplay = vc_dispmanx_display_open(0); // LCD - dispmanUpdate = vc_dispmanx_update_start(0); - - dispmanElement = vc_dispmanx_element_add(dispmanUpdate, dispmanDisplay, 0/*layer*/, &dstRect, 0/*src*/, - &srcRect, DISPMANX_PROTECTION_NONE, &alpha, 0/*clamp*/, DISPMANX_NO_ROTATE); - - CORE.Window.handle.element = dispmanElement; - CORE.Window.handle.width = CORE.Window.render.width; - CORE.Window.handle.height = CORE.Window.render.height; - vc_dispmanx_update_submit_sync(dispmanUpdate); - - CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, &CORE.Window.handle, NULL); - - const unsigned char *const renderer = glGetString(GL_RENDERER); - if (renderer) TRACELOG(LOG_INFO, "DISPLAY: Renderer name is: %s", renderer); - else TRACELOG(LOG_WARNING, "DISPLAY: Failed to get renderer name"); - //--------------------------------------------------------------------------------- -#endif // PLATFORM_RPI - #if defined(PLATFORM_DRM) CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, (EGLNativeWindowType)CORE.Window.gbmSurface, NULL); if (EGL_NO_SURFACE == CORE.Window.surface) @@ -4952,7 +4874,7 @@ static bool InitGraphicsDevice(int width, int height) TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); } -#endif // PLATFORM_ANDROID || PLATFORM_RPI || PLATFORM_DRM +#endif // PLATFORM_ANDROID || PLATFORM_DRM // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions @@ -5096,7 +5018,7 @@ static void InitTimer(void) timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms) #endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) struct timespec now = { 0 }; if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success @@ -5160,7 +5082,7 @@ void SwapScreenBuffer(void) glfwSwapBuffers(CORE.Window.handle); #endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) eglSwapBuffers(CORE.Window.device, CORE.Window.surface); #if defined(PLATFORM_DRM) @@ -5190,7 +5112,7 @@ void SwapScreenBuffer(void) CORE.Window.prevBO = bo; #endif // PLATFORM_DRM -#endif // PLATFORM_ANDROID || PLATFORM_RPI || PLATFORM_DRM +#endif // PLATFORM_ANDROID || PLATFORM_DRM } // Register all input events @@ -5208,13 +5130,11 @@ void PollInputEvents(void) // Reset key repeats for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; -#if !(defined(PLATFORM_RPI) || defined(PLATFORM_DRM)) // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN CORE.Input.Gamepad.axisCount = 0; -#endif -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) // Register previous keys states for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) { @@ -5461,7 +5381,7 @@ void PollInputEvents(void) } #endif -#if (defined(PLATFORM_RPI) || defined(PLATFORM_DRM)) && defined(SUPPORT_SSH_KEYBOARD_RPI) +#if defined(PLATFORM_DRM) && defined(SUPPORT_SSH_KEYBOARD_RPI) // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here. // stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console @@ -6336,7 +6256,7 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent } #endif -#if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) // Initialize Keyboard system (using standard input) static void InitKeyboard(void) { @@ -6977,7 +6897,7 @@ static void *EventThread(void *arg) if (CORE.Input.Touch.position[2].x >= 0) CORE.Input.Touch.pointCount++; if (CORE.Input.Touch.position[3].x >= 0) CORE.Input.Touch.pointCount++; -#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_RPI, PLATFORM_DRM +#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_DRM if (gestureUpdate) { GestureEvent gestureEvent = { 0 }; @@ -7094,7 +7014,7 @@ static void *GamepadThread(void *arg) return NULL; } -#endif // PLATFORM_RPI || PLATFORM_DRM +#endif // PLATFORM_DRM #if defined(PLATFORM_DRM) // Search matching DRM mode in connector's mode list diff --git a/src/rlgl.h b/src/rlgl.h index f274931e2..dee4d501f 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -815,7 +815,7 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad // It seems OpenGL ES 2.0 instancing entry points are not defined on Raspberry Pi // provided headers (despite being defined in official Khronos GLES2 headers) - #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) + #if defined(PLATFORM_DRM) typedef void (GL_APIENTRYP PFNGLDRAWARRAYSINSTANCEDEXTPROC) (GLenum mode, GLint start, GLsizei count, GLsizei primcount); typedef void (GL_APIENTRYP PFNGLDRAWELEMENTSINSTANCEDEXTPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei primcount); typedef void (GL_APIENTRYP PFNGLVERTEXATTRIBDIVISOREXTPROC) (GLuint index, GLuint divisor); From 30a9a24db9d2561335a2a42d423cf9cfeab6e769 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 7 Sep 2023 18:00:10 +0200 Subject: [PATCH 0258/1350] Review to avoid UBSAN complaining #1891 --- src/rlgl.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index dee4d501f..6dee60f95 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3687,7 +3687,11 @@ void rlDrawVertexArray(int offset, int count) // Draw vertex array elements void rlDrawVertexArrayElements(int offset, int count, const void *buffer) { - glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)buffer + offset); + // NOTE: Added pointer math separately from function to avoid UBSAN complaining + unsigned short *bufferPtr = (unsigned short *)buffer; + if (offset > 0) bufferPtr += offset; + + glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)bufferPtr); } // Draw vertex array instanced @@ -3702,7 +3706,11 @@ void rlDrawVertexArrayInstanced(int offset, int count, int instances) void rlDrawVertexArrayElementsInstanced(int offset, int count, const void *buffer, int instances) { #if defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2) - glDrawElementsInstanced(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)buffer + offset, instances); + // NOTE: Added pointer math separately from function to avoid UBSAN complaining + unsigned short *bufferPtr = (unsigned short *)buffer; + if (offset > 0) bufferPtr += offset; + + glDrawElementsInstanced(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)bufferPtr, instances); #endif } From ecc80bbceaaa90e24fff2ce956ce0730991bd552 Mon Sep 17 00:00:00 2001 From: Dan Vu Date: Fri, 8 Sep 2023 11:13:00 +0200 Subject: [PATCH 0259/1350] added raylib-raku to bindings (#3299) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index d4bd8fc21..bdd5e2558 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -77,6 +77,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-sunder | **auto** | [Sunder](https://github.com/ashn-dot-dev/sunder) | 0BSD | https://github.com/ashn-dot-dev/raylib-sunder | | rayed-bqn | **auto** | [BQN](https://mlochbaum.github.io/BQN/) | MIT | https://github.com/Brian-ED/rayed-bqn | | rayjs | 4.6-dev | [QuickJS](https://bellard.org/quickjs/) | MIT | https://github.com/mode777/rayjs | +| raylib-raku | **auto** | [Raku](https://www.raku.org/) | Artistic License 2.0 | https://github.com/vushu/raylib-raku | ### Utility Wrapers These are utility wrappers for specific languages, they are not required to use raylib in the language but may adapt the raylib API to be more inline with the language's pardigm. From 2d5d0c2999717ea28924d6cdd47bab014478cc32 Mon Sep 17 00:00:00 2001 From: Gabriel dos Santos Sanches Date: Fri, 8 Sep 2023 12:22:12 +0200 Subject: [PATCH 0260/1350] examples: core: adds 2D camera two player split screen (#3298) --- examples/Makefile | 1 + examples/Makefile.Web | 1 + examples/README.md | 1 + examples/core/core_camera_2d_split_screen.c | 137 +++++++ examples/core/core_camera_2d_split_screen.png | Bin 0 -> 20010 bytes .../core_camera_2d_split_screen.vcxproj | 387 ++++++++++++++++++ 6 files changed, 527 insertions(+) create mode 100644 examples/core/core_camera_2d_split_screen.c create mode 100644 examples/core/core_camera_2d_split_screen.png create mode 100644 projects/VS2022/examples/core_camera_2d_split_screen.vcxproj diff --git a/examples/Makefile b/examples/Makefile index 338fe168c..911743938 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -386,6 +386,7 @@ CORE = \ core/core_window_letterbox \ core/core_window_should_close \ core/core_split_screen \ + core/core_camera_2d_split_screen \ core/core_smooth_pixelperfect \ core/core_custom_frame_control diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 518fab918..44ace4ee4 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -361,6 +361,7 @@ CORE = \ core/core_window_letterbox \ core/core_window_should_close \ core/core_split_screen \ + core/core_camera_2d_split_screen \ core/core_smooth_pixelperfect \ core/core_custom_frame_control \ core/core_loading_thread diff --git a/examples/README.md b/examples/README.md index 8399d677d..35acaa25a 100644 --- a/examples/README.md +++ b/examples/README.md @@ -54,6 +54,7 @@ Examples using raylib core platform functionality like window creation, inputs, | 28 | [core_smooth_pixelperfect](core/core_smooth_pixelperfect.c) | core_smooth_pixelperfect | ⭐️⭐️⭐️☆ | 3.7 | **4.0** | [Giancamillo Alessandroni](https://github.com/NotManyIdeasDev) | | 29 | [core_split_screen](core/core_split_screen.c) | core_split_screen | ⭐️⭐️⭐️⭐️ | 3.7 | **4.0** | [Jeffery Myers](https://github.com/JeffM2501) | | 30 | [core_window_should_close](core/core_window_should_close.c) | core_window_should_close | ⭐️⭐️☆☆ | **4.2** | **4.2** | [Ray](https://github.com/raysan5) | +| 31 | [core_camera_2d_split_screen](core/core_camera_2d_split_screen.c) | core_camera_2d_split_screen | ⭐️⭐️⭐️⭐️ | **4.5** | **4.5** | [Jeffery Myers](https://github.com/JeffM2501) | ### category: shapes diff --git a/examples/core/core_camera_2d_split_screen.c b/examples/core/core_camera_2d_split_screen.c new file mode 100644 index 000000000..5f5059569 --- /dev/null +++ b/examples/core/core_camera_2d_split_screen.c @@ -0,0 +1,137 @@ +/******************************************************************************************* +* +* raylib [core] example - split screen +* +* Addapted from the Split Screen example (https://github.com/raysan5/raylib/blob/master/examples/core/core_split_screen.c) +* +* Example originally created with raylib 4.5, last time updated with raylib 4.5 +* +* Example contributed by Jeffery Myers (@JeffM2501) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2021-2023 Jeffery Myers (@JeffM2501) +* +********************************************************************************************/ + +#include "raylib.h" + +#include + +#define PLAYER_SIZE 40 + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 440; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - camera 2D split screen"); + + Rectangle player1 = { 200, 200, PLAYER_SIZE, PLAYER_SIZE }; + Rectangle player2 = { 250, 200, PLAYER_SIZE, PLAYER_SIZE }; + + Camera2D camera1 = { 0 }; + camera1.target = (Vector2){ player1.x, player1.y }; + camera1.offset = (Vector2){ 200.0f, 200.0f }; + camera1.rotation = 0.0f; + camera1.zoom = 1.0f; + + Camera2D camera2 = { 0 }; + camera2.target = (Vector2){ player2.x, player2.y }; + camera2.offset = (Vector2){ 200.0f, 200.0f }; + camera2.rotation = 0.0f; + camera2.zoom = 1.0f; + + RenderTexture screenCamera1 = LoadRenderTexture(screenWidth / 2, screenHeight); + RenderTexture screenCamera2 = LoadRenderTexture(screenWidth / 2, screenHeight); + + // Build a flipped rectangle the size of the split view to use for drawing later + Rectangle splitScreenRect = { 0.0f, 0.0f, (float)screenCamera1.texture.width, (float)-screenCamera1.texture.height }; + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + void DrawScene(void) { + for (int i = 0; i < screenWidth/PLAYER_SIZE + 1; i++) + { + DrawLineV((Vector2){PLAYER_SIZE*i, 0}, (Vector2){PLAYER_SIZE*i, screenHeight}, LIGHTGRAY); + } + + for (int i = 0; i < screenHeight/PLAYER_SIZE + 1; i++) + { + DrawLineV((Vector2){0, PLAYER_SIZE*i}, (Vector2){screenWidth, PLAYER_SIZE*i}, LIGHTGRAY); + } + + for (int i = 0; i < screenWidth/PLAYER_SIZE; i++) + { + for (int j = 0; j < screenHeight/PLAYER_SIZE; j++) + { + char coordinate_str[8]; + snprintf(coordinate_str, sizeof(coordinate_str), "%d,%d", i, j); + DrawText(coordinate_str, 10 + PLAYER_SIZE*i, 10 + PLAYER_SIZE*j, 10, LIGHTGRAY); + } + } + + DrawRectangleRec(player1, RED); + DrawRectangleRec(player2, BLUE); + } + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyDown(KEY_S)) player1.y += 3; + else if (IsKeyDown(KEY_W)) player1.y -= 3; + if (IsKeyDown(KEY_D)) player1.x += 3; + else if (IsKeyDown(KEY_A)) player1.x -= 3; + + if (IsKeyDown(KEY_UP)) player2.y += 3; + else if (IsKeyDown(KEY_DOWN)) player2.y -= 3; + if (IsKeyDown(KEY_RIGHT)) player2.x += 3; + else if (IsKeyDown(KEY_LEFT)) player2.x -= 3; + + camera1.target = (Vector2){ player1.x, player1.y }; + camera2.target = (Vector2){ player2.x, player2.y }; + + // Draw + //---------------------------------------------------------------------------------- + BeginTextureMode(screenCamera1); + ClearBackground(RAYWHITE); + BeginMode2D(camera1); + DrawScene(); + EndMode2D(); + DrawText("PLAYER1 W/S/A/D to move", 10, 10, 15, RED); + EndTextureMode(); + + BeginTextureMode(screenCamera2); + ClearBackground(RAYWHITE); + BeginMode2D(camera2); + DrawScene(); + EndMode2D(); + DrawText("PLAYER2 UP/DOWN/LEFT/RIGHT to move", 10, 10, 15, BLUE); + EndTextureMode(); + + // Draw both views render textures to the screen side by side + BeginDrawing(); + ClearBackground(BLACK); + DrawTextureRec(screenCamera1.texture, splitScreenRect, (Vector2){ 0, 0 }, WHITE); + DrawTextureRec(screenCamera2.texture, splitScreenRect, (Vector2){ screenWidth/2.0f, 0 }, WHITE); + EndDrawing(); + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadRenderTexture(screenCamera1); // Unload render texture + UnloadRenderTexture(screenCamera2); // Unload render texture + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/core/core_camera_2d_split_screen.png b/examples/core/core_camera_2d_split_screen.png new file mode 100644 index 0000000000000000000000000000000000000000..ed5aaa58bfc2bb8124098d2b5c583961414bc218 GIT binary patch literal 20010 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 literal 0 HcmV?d00001 diff --git a/projects/VS2022/examples/core_camera_2d_split_screen.vcxproj b/projects/VS2022/examples/core_camera_2d_split_screen.vcxproj new file mode 100644 index 000000000..1efc58a65 --- /dev/null +++ b/projects/VS2022/examples/core_camera_2d_split_screen.vcxproj @@ -0,0 +1,387 @@ + + + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + Win32 + + + Release + x64 + + + + {946A1700-C7AA-46F0-AEF2-67C98B5722AC} + Win32Proj + core_camera_2d_split_screen + 10.0 + core_camera_2d_split_screen + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + From 1896268775e59d00b2b4f3fea0bc244eee1a1cb4 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 8 Sep 2023 13:27:13 +0200 Subject: [PATCH 0261/1350] Reviewed examples for consistency --- examples/Makefile | 4 +- examples/Makefile.Web | 13 +- examples/README.md | 42 ++--- examples/core/core_2d_camera_split_screen.c | 167 ++++++++++++++++++ examples/core/core_2d_camera_split_screen.png | Bin 0 -> 21543 bytes examples/core/core_3d_camera_free.c | 2 +- ...screen.c => core_3d_camera_split_screen.c} | 87 +++++---- examples/core/core_3d_camera_split_screen.png | Bin 0 -> 16165 bytes examples/core/core_camera_2d_split_screen.c | 137 -------------- examples/core/core_camera_2d_split_screen.png | Bin 20010 -> 0 bytes examples/core/core_split_screen.png | Bin 21609 -> 0 bytes examples/shapes/shapes_bouncing_ball.c | 6 +- examples/shapes/shapes_lines_bezier.c | 24 ++- examples/shapes/shapes_lines_splines.c | 155 ++++++++++++++++ ...oj => core_2d_camera_split_screen.vcxproj} | 8 +- ...oj => core_3d_camera_split_screen.vcxproj} | 8 +- projects/VS2022/raylib.sln | 57 ++++-- 17 files changed, 481 insertions(+), 229 deletions(-) create mode 100644 examples/core/core_2d_camera_split_screen.c create mode 100644 examples/core/core_2d_camera_split_screen.png rename examples/core/{core_split_screen.c => core_3d_camera_split_screen.c} (68%) create mode 100644 examples/core/core_3d_camera_split_screen.png delete mode 100644 examples/core/core_camera_2d_split_screen.c delete mode 100644 examples/core/core_camera_2d_split_screen.png delete mode 100644 examples/core/core_split_screen.png create mode 100644 examples/shapes/shapes_lines_splines.c rename projects/VS2022/examples/{core_split_screen.vcxproj => core_2d_camera_split_screen.vcxproj} (98%) rename projects/VS2022/examples/{core_camera_2d_split_screen.vcxproj => core_3d_camera_split_screen.vcxproj} (99%) diff --git a/examples/Makefile b/examples/Makefile index 911743938..cd079c312 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -370,9 +370,11 @@ CORE = \ core/core_2d_camera \ core/core_2d_camera_platformer \ core/core_2d_camera_mouse_zoom \ + core/core_2d_camera_split_screen \ core/core_3d_camera_mode \ core/core_3d_camera_free \ core/core_3d_camera_first_person \ + core/core_3d_camera_split_screen \ core/core_3d_picking \ core/core_world_screen \ core/core_custom_logging \ @@ -385,8 +387,6 @@ CORE = \ core/core_window_flags \ core/core_window_letterbox \ core/core_window_should_close \ - core/core_split_screen \ - core/core_camera_2d_split_screen \ core/core_smooth_pixelperfect \ core/core_custom_frame_control diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 44ace4ee4..267e02396 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -346,9 +346,11 @@ CORE = \ core/core_2d_camera \ core/core_2d_camera_platformer \ core/core_2d_camera_mouse_zoom \ + core/core_2d_camera_split_screen \ core/core_3d_camera_mode \ core/core_3d_camera_free \ core/core_3d_camera_first_person \ + core/core_3d_camera_split_screen \ core/core_3d_picking \ core/core_world_screen \ core/core_custom_logging \ @@ -360,8 +362,6 @@ CORE = \ core/core_window_flags \ core/core_window_letterbox \ core/core_window_should_close \ - core/core_split_screen \ - core/core_camera_2d_split_screen \ core/core_smooth_pixelperfect \ core/core_custom_frame_control \ core/core_loading_thread @@ -529,6 +529,9 @@ core/core_2d_camera_platformer: core/core_2d_camera_platformer.c core/core_2d_camera_mouse_zoom: core/core_2d_camera_mouse_zoom.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + +core/core_2d_camera_split_screen: core/core_2d_camera_split_screen.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) core/core_3d_camera_mode: core/core_3d_camera_mode.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -538,6 +541,9 @@ core/core_3d_camera_free: core/core_3d_camera_free.c core/core_3d_camera_first_person: core/core_3d_camera_first_person.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + +core/core_3d_camera_split_screen: core/core_3d_camera_split_screen.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) core/core_3d_picking: core/core_3d_picking.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -570,9 +576,6 @@ core/core_vr_simulator: core/core_vr_simulator.c core/core_window_flags: core/core_window_flags.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -core/core_split_screen: core/core_split_screen.c - $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - core/core_smooth_pixelperfect: core/core_smooth_pixelperfect.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) diff --git a/examples/README.md b/examples/README.md index 35acaa25a..82e4b7825 100644 --- a/examples/README.md +++ b/examples/README.md @@ -34,27 +34,27 @@ Examples using raylib core platform functionality like window creation, inputs, | 08 | [core_2d_camera](core/core_2d_camera.c) | core_2d_camera | ⭐️⭐️☆☆ | 1.5 | 3.0 | [Ray](https://github.com/raysan5) | | 09 | [core_2d_camera_mouse_zoom](core/core_2d_camera_mouse_zoom.c) | core_2d_camera_mouse_zoom | ⭐️⭐️☆☆ | **4.2** | **4.2** | [Jeffery Myers](https://github.com/JeffM2501) | | 10 | [core_2d_camera_platformer](core/core_2d_camera_platformer.c) | core_2d_camera_platformer | ⭐️⭐️⭐️☆ | 2.5 | 3.0 | [avyy](https://github.com/avyy) | -| 11 | [core_3d_camera_mode](core/core_3d_camera_mode.c) | core_3d_camera_mode | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | -| 12 | [core_3d_camera_free](core/core_3d_camera_free.c) | core_3d_camera_free | ⭐️☆☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | -| 13 | [core_3d_camera_first_person](core/core_3d_camera_first_person.c) | core_3d_camera_first_person | ⭐️⭐️☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | -| 14 | [core_3d_picking](core/core_3d_picking.c) | core_3d_picking | ⭐️⭐️☆☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) | -| 15 | [core_world_screen](core/core_world_screen.c) | core_world_screen | ⭐️⭐️☆☆ | 1.3 | 1.4 | [Ray](https://github.com/raysan5) | -| 16 | [core_custom_logging](core/core_custom_logging.c) | core_custom_logging | ⭐️⭐️⭐️☆ | 2.5 | 2.5 | [Pablo Marcos Oltra](https://github.com/pamarcos) | -| 17 | [core_window_flags](core/core_window_flags.c) | core_window_flags | ⭐️⭐️⭐️☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 18 | [core_window_letterbox](core/core_window_letterbox.c) | core_window_letterbox | ⭐️⭐️☆☆ | 2.5 | **4.0** | [Anata](https://github.com/anatagawa) | -| 19 | [core_window_should_close](core/core_window_should_close.c) | core_window_should_close | ⭐️☆☆☆ | **4.2** | **4.2** | [Ray](https://github.com/raysan5) | -| 20 | [core_drop_files](core/core_drop_files.c) | core_drop_files | ⭐️⭐️☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | -| 21 | [core_random_values](core/core_random_values.c) | core_random_values | ⭐️☆☆☆ | 1.1 | 1.1 | [Ray](https://github.com/raysan5) | -| 22 | [core_storage_values](core/core_storage_values.c) | core_storage_values | ⭐️⭐️☆☆ | 1.4 | **4.2** | [Ray](https://github.com/raysan5) | -| 23 | [core_vr_simulator](core/core_vr_simulator.c) | core_vr_simulator | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) | -| 24 | [core_loading_thread](core/core_loading_thread.c) | core_loading_thread | ⭐️⭐️⭐️☆ | 2.5 | 3.0 | [Ray](https://github.com/raysan5) | -| 25 | [core_scissor_test](core/core_scissor_test.c) | core_scissor_test | ⭐️☆☆☆ | 2.5 | 3.0 | [Chris Dill](https://github.com/MysteriousSpace) | -| 26 | [core_basic_screen_manager](core/core_basic_screen_manager.c) | core_basic_screen_manager | ⭐️☆☆☆ | **4.0** | **4.0** | [Ray](https://github.com/raysan5) | -| 27 | [core_custom_frame_control](core/core_custom_frame_control.c) | core_custom_frame_control | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Ray](https://github.com/raysan5) | -| 28 | [core_smooth_pixelperfect](core/core_smooth_pixelperfect.c) | core_smooth_pixelperfect | ⭐️⭐️⭐️☆ | 3.7 | **4.0** | [Giancamillo Alessandroni](https://github.com/NotManyIdeasDev) | -| 29 | [core_split_screen](core/core_split_screen.c) | core_split_screen | ⭐️⭐️⭐️⭐️ | 3.7 | **4.0** | [Jeffery Myers](https://github.com/JeffM2501) | -| 30 | [core_window_should_close](core/core_window_should_close.c) | core_window_should_close | ⭐️⭐️☆☆ | **4.2** | **4.2** | [Ray](https://github.com/raysan5) | -| 31 | [core_camera_2d_split_screen](core/core_camera_2d_split_screen.c) | core_camera_2d_split_screen | ⭐️⭐️⭐️⭐️ | **4.5** | **4.5** | [Jeffery Myers](https://github.com/JeffM2501) | +| 11 | [core_2d_camera_split_screen](core/core_2d_camera_split_screen.c) | core_2d_camera_split_screen | ⭐️⭐️⭐️⭐️ | **4.5** | **4.5** | [Gabriel dos Santos Sanches](https://github.com/gabrielssanches) | +| 12 | [core_3d_camera_mode](core/core_3d_camera_mode.c) | core_3d_camera_mode | ⭐️☆☆☆ | 1.0 | 1.0 | [Ray](https://github.com/raysan5) | +| 13 | [core_3d_camera_free](core/core_3d_camera_free.c) | core_3d_camera_free | ⭐️☆☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | +| 14 | [core_3d_camera_first_person](core/core_3d_camera_first_person.c) | core_3d_camera_first_person | ⭐️⭐️☆☆ | 1.3 | 1.3 | [Ray](https://github.com/raysan5) | +| 15 | [core_3d_camera:split_screen](core/core_3d_camera_split_screen.c) | core_3d_camera_split_screen | ⭐️⭐️⭐️⭐️ | 3.7 | **4.0** | [Jeffery Myers](https://github.com/JeffM2501) | +| 16 | [core_3d_picking](core/core_3d_picking.c) | core_3d_picking | ⭐️⭐️☆☆ | 1.3 | **4.0** | [Ray](https://github.com/raysan5) | +| 17 | [core_world_screen](core/core_world_screen.c) | core_world_screen | ⭐️⭐️☆☆ | 1.3 | 1.4 | [Ray](https://github.com/raysan5) | +| 18 | [core_custom_logging](core/core_custom_logging.c) | core_custom_logging | ⭐️⭐️⭐️☆ | 2.5 | 2.5 | [Pablo Marcos Oltra](https://github.com/pamarcos) | +| 19 | [core_window_flags](core/core_window_flags.c) | core_window_flags | ⭐️⭐️⭐️☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 20 | [core_window_letterbox](core/core_window_letterbox.c) | core_window_letterbox | ⭐️⭐️☆☆ | 2.5 | **4.0** | [Anata](https://github.com/anatagawa) | +| 21 | [core_window_should_close](core/core_window_should_close.c) | core_window_should_close | ⭐️☆☆☆ | **4.2** | **4.2** | [Ray](https://github.com/raysan5) | +| 22 | [core_drop_files](core/core_drop_files.c) | core_drop_files | ⭐️⭐️☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | +| 23 | [core_random_values](core/core_random_values.c) | core_random_values | ⭐️☆☆☆ | 1.1 | 1.1 | [Ray](https://github.com/raysan5) | +| 24 | [core_storage_values](core/core_storage_values.c) | core_storage_values | ⭐️⭐️☆☆ | 1.4 | **4.2** | [Ray](https://github.com/raysan5) | +| 25 | [core_vr_simulator](core/core_vr_simulator.c) | core_vr_simulator | ⭐️⭐️⭐️☆ | 2.5 | **4.0** | [Ray](https://github.com/raysan5) | +| 26 | [core_loading_thread](core/core_loading_thread.c) | core_loading_thread | ⭐️⭐️⭐️☆ | 2.5 | 3.0 | [Ray](https://github.com/raysan5) | +| 27 | [core_scissor_test](core/core_scissor_test.c) | core_scissor_test | ⭐️☆☆☆ | 2.5 | 3.0 | [Chris Dill](https://github.com/MysteriousSpace) | +| 28 | [core_basic_screen_manager](core/core_basic_screen_manager.c) | core_basic_screen_manager | ⭐️☆☆☆ | **4.0** | **4.0** | [Ray](https://github.com/raysan5) | +| 29 | [core_custom_frame_control](core/core_custom_frame_control.c) | core_custom_frame_control | ⭐️⭐️⭐️⭐️ | **4.0** | **4.0** | [Ray](https://github.com/raysan5) | +| 30 | [core_smooth_pixelperfect](core/core_smooth_pixelperfect.c) | core_smooth_pixelperfect | ⭐️⭐️⭐️☆ | 3.7 | **4.0** | [Giancamillo Alessandroni](https://github.com/NotManyIdeasDev) | +| 31 | [core_window_should_close](core/core_window_should_close.c) | core_window_should_close | ⭐️⭐️☆☆ | **4.2** | **4.2** | [Ray](https://github.com/raysan5) | ### category: shapes diff --git a/examples/core/core_2d_camera_split_screen.c b/examples/core/core_2d_camera_split_screen.c new file mode 100644 index 000000000..57a0dfd3a --- /dev/null +++ b/examples/core/core_2d_camera_split_screen.c @@ -0,0 +1,167 @@ +/******************************************************************************************* +* +* raylib [core] example - 2d camera split screen +* +* Addapted from the core_3d_camera_split_screen example: +* https://github.com/raysan5/raylib/blob/master/examples/core/core_3d_camera_split_screen.c +* +* Example originally created with raylib 4.5, last time updated with raylib 4.5 +* +* Example contributed by Gabriel dos Santos Sanches (@gabrielssanches) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2023 Gabriel dos Santos Sanches (@gabrielssanches) +* +********************************************************************************************/ + +#include "raylib.h" + +#define PLAYER_SIZE 40 + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 440; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - 2d camera split screen"); + + Rectangle player1 = { 200, 200, PLAYER_SIZE, PLAYER_SIZE }; + Rectangle player2 = { 250, 200, PLAYER_SIZE, PLAYER_SIZE }; + + Camera2D camera1 = { 0 }; + camera1.target = (Vector2){ player1.x, player1.y }; + camera1.offset = (Vector2){ 200.0f, 200.0f }; + camera1.rotation = 0.0f; + camera1.zoom = 1.0f; + + Camera2D camera2 = { 0 }; + camera2.target = (Vector2){ player2.x, player2.y }; + camera2.offset = (Vector2){ 200.0f, 200.0f }; + camera2.rotation = 0.0f; + camera2.zoom = 1.0f; + + RenderTexture screenCamera1 = LoadRenderTexture(screenWidth/2, screenHeight); + RenderTexture screenCamera2 = LoadRenderTexture(screenWidth/2, screenHeight); + + // Build a flipped rectangle the size of the split view to use for drawing later + Rectangle splitScreenRect = { 0.0f, 0.0f, (float)screenCamera1.texture.width, (float)-screenCamera1.texture.height }; + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyDown(KEY_S)) player1.y += 3.0f; + else if (IsKeyDown(KEY_W)) player1.y -= 3.0f; + if (IsKeyDown(KEY_D)) player1.x += 3.0f; + else if (IsKeyDown(KEY_A)) player1.x -= 3.0f; + + if (IsKeyDown(KEY_UP)) player2.y -= 3.0f; + else if (IsKeyDown(KEY_DOWN)) player2.y += 3.0f; + if (IsKeyDown(KEY_RIGHT)) player2.x += 3.0f; + else if (IsKeyDown(KEY_LEFT)) player2.x -= 3.0f; + + camera1.target = (Vector2){ player1.x, player1.y }; + camera2.target = (Vector2){ player2.x, player2.y }; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginTextureMode(screenCamera1); + ClearBackground(RAYWHITE); + + BeginMode2D(camera1); + + // Draw full scene with first camera + for (int i = 0; i < screenWidth/PLAYER_SIZE + 1; i++) + { + DrawLineV((Vector2){PLAYER_SIZE*i, 0}, (Vector2){PLAYER_SIZE*i, screenHeight}, LIGHTGRAY); + } + + for (int i = 0; i < screenHeight/PLAYER_SIZE + 1; i++) + { + DrawLineV((Vector2){0, PLAYER_SIZE*i}, (Vector2){screenWidth, PLAYER_SIZE*i}, LIGHTGRAY); + } + + for (int i = 0; i < screenWidth/PLAYER_SIZE; i++) + { + for (int j = 0; j < screenHeight/PLAYER_SIZE; j++) + { + DrawText(TextFormat("[%i,%i]", i, j), 10 + PLAYER_SIZE*i, 15 + PLAYER_SIZE*j, 10, LIGHTGRAY); + } + } + + DrawRectangleRec(player1, RED); + DrawRectangleRec(player2, BLUE); + EndMode2D(); + + DrawRectangle(0, 0, GetScreenWidth()/2, 30, Fade(RAYWHITE, 0.6f)); + DrawText("PLAYER1: W/S/A/D to move", 10, 10, 10, MAROON); + + EndTextureMode(); + + BeginTextureMode(screenCamera2); + ClearBackground(RAYWHITE); + + BeginMode2D(camera2); + + // Draw full scene with second camera + for (int i = 0; i < screenWidth/PLAYER_SIZE + 1; i++) + { + DrawLineV((Vector2){PLAYER_SIZE*i, 0}, (Vector2){PLAYER_SIZE*i, screenHeight}, LIGHTGRAY); + } + + for (int i = 0; i < screenHeight/PLAYER_SIZE + 1; i++) + { + DrawLineV((Vector2){0, PLAYER_SIZE*i}, (Vector2){screenWidth, PLAYER_SIZE*i}, LIGHTGRAY); + } + + for (int i = 0; i < screenWidth/PLAYER_SIZE; i++) + { + for (int j = 0; j < screenHeight/PLAYER_SIZE; j++) + { + DrawText(TextFormat("[%i,%i]", i, j), 10 + PLAYER_SIZE*i, 15 + PLAYER_SIZE*j, 10, LIGHTGRAY); + } + } + + DrawRectangleRec(player1, RED); + DrawRectangleRec(player2, BLUE); + + EndMode2D(); + + DrawRectangle(0, 0, GetScreenWidth()/2, 30, Fade(RAYWHITE, 0.6f)); + DrawText("PLAYER2: UP/DOWN/LEFT/RIGHT to move", 10, 10, 10, DARKBLUE); + + EndTextureMode(); + + // Draw both views render textures to the screen side by side + BeginDrawing(); + ClearBackground(BLACK); + + DrawTextureRec(screenCamera1.texture, splitScreenRect, (Vector2){ 0, 0 }, WHITE); + DrawTextureRec(screenCamera2.texture, splitScreenRect, (Vector2){ screenWidth/2.0f, 0 }, WHITE); + + DrawRectangle(GetScreenWidth()/2 - 2, 0, 4, GetScreenHeight(), LIGHTGRAY); + EndDrawing(); + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadRenderTexture(screenCamera1); // Unload render texture + UnloadRenderTexture(screenCamera2); // Unload render texture + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/core/core_2d_camera_split_screen.png b/examples/core/core_2d_camera_split_screen.png new file mode 100644 index 0000000000000000000000000000000000000000..a441e39298f2b7962e187f6b5918915a8babff20 GIT binary patch literal 21543 zcmeHvc|4T+`#wfvFk?oWF_s}MS}+(1VM3@>+9hR(nj~AK$U3%aFqIIeqA4jYlcTa^ z86y-rMyjbu)<#K4SsMDiA1X7QQ=RksZ0GZReg5$pPv+_UyqEjBuj{_<=aB6xE0h#Y zN<>5iwanULt%wLrMMOk27Xb%X3ZGIvMMTzhFSA&(e*d=Z4L5c_4vAVrz=<(bzx=R> z62ar~hwrUEW5UtvG6@;2E~%g>8~K#x5h=4P!O~~ z{2@|kM$Uohq&%`TClkg0=m*x)NQH6eJO2p2z>O^7AuB0ol~G|F4E7Ixq-{aa_K_^7 zJp0}lN+=R8MARRT4-6bxjKLa5`4cz9e|-Y_R54yfk4ez5FHzh|VBn!V3|StPB{+_> znc}>lITbvEk%aS)IXw!f;;;q1mJgaJ!!R&#WjbeH#rMV~(l|ah+&`L@v1`2PVisB~ z=VjOKyvj7yO_eA4sNlAWUJM|2VCtf4R0;l}ZOpt$84_o<9O6X(wkOv)78`PuA(s^B zJ>*SvjQ#vPq=?XGwAFT38c(n)M2^8YJgJ^e$!)`DoHoHP$s`*@bd`2sl8tKHnR(I~ z@YDh)XO#hM0(tl(_8l`1pAp~SRW|K@ZmDu(d|R2UL9a-5QUf8pbLmaFO}A%cExi?Q zYbe&aRiP&4C=C%gi>*GkL?lVkV;@4(^!Iz~l^@-sVuiLgExIqFse9B&wX96{I?B)2 zZ?GZCLrb-Au#}E1JeTWAeDh`cNs%Hqqm)%|(HRmhvx_VjXzJd*A)SP`)dI!3 z<<;`)g=sTIUvo7j=|cPR zZH;I>ektm`^DpZnbn#nWVp1D(>zk)kJX&J50ev!zal+AR#T5=FjVNyYDiovJ8rH31 zrpC`*9C{U{CH^t|rChwTO`-&KHz%sun`KG;* zdIqsh4&ADAjKlbd2hrD#lRU1nJ4~JHlq-w2T35*)9kIL|oSeGkmD2^AqY9Rc_HGKI zR!v;pK71(+)~k_Z_BcN4`jM+LJ7&+)MMgBlRk^#P0{hGLO#GXr(8+<6&N%@)FLG8@ zP*fK}!9he?)CYHl7eGeMvxVVCc#aOS3;VGokNsy;SJ!Zy&314K-i0ZmF$LNWW*c~v zzFfVWJ>^Vb&mFZQ2Z`Oh*5yg&g`7CAPZv0)JBi|OKLoOK9h0o~o@snD=G zBioBe1DVFa+icxP7V$wY9A%?5ZZ%-k{YTr>X($vj<2aUU9_sXbf; z9DQ=F)dRe1`(w)B8hyUkiQTXb)6Z6DWq*WcA5;cIylFJ?l$n#oa_8x~u)R0j?~e&* zb(=Vsl2zE~Hj{pWh5x(Ek6slmw$>p$r57y4;#~M*o_1!KMk43IC|P=i%RuwJ3;or_ zj6f3B^2-kU9W+3w24quY)BPWrp^I4^MXYW0 zw2pYry)mGJPYg540fGDg$EUl>!sK7@ob@lf=UfVzz~GqZ(5qK9SK&kN9dPes_TkTLbB}7# zK_}VM2Qxon(sgmcbS8FX2ohNq!J>@1mTb8xJI|4>w@z(A^omiXsY;l&^YHQ{K&DS;QGY+ zelwa{jj=Ht`tBIMUF>g%;5asfjQ{!}M6H)->uaw*Xmy%ghVnr4BqLG~e-Hm`-PwTg{H z-ej8lH<7)jWE(}>4?6ArNIvA)mOJVE1y=GXeD7fT%QKuFH3uB}1K-I0y6_WMQ8BCPT%y3 zY^DsePvyhc4OwN0DjhtqTcvB}`KwqzysO@8D5J%*MZ!Tw`>|jDoRJEWArMDP%tMp! zYw9T-+EeaLm_=PPu;%&xbpMnYckVEHjc~=V&CiiZ{rXQu<k(G5s~hH80792} zx*Zl(%vl~ZvHLTlN_g708N2)^E(w1M^-v|8+ez|@HB`G6udqPv0rla^%DF4$IwQ2V zF1`@UQS)m#a*KsctaQ@BHt0_+^;BFA5?;g#z!zZJ@;QQb!l3odsSlZP=${DTcNWtOI4&`rtB8jlo7F|4sN7Z6b@<9 z&~EiSw83p=W3?$RIC-ugab(TLV6fl)*yv507uve22JmMTc&b#rM_+a&*l=^J*zxvu>@fGViKV zSGQUM$MOMwS7*E}AKh^_D6KW+m%gJZ%?4l>?AbrDcD5Sg2n~+tW(yk5eyJpcznvz| z3h!Xkf&u>WyrZ+i^y!`P$-c%Zrp)L%kSd%@+(OmXsH_pqFqrFJ5C*v%m90~v~AxuYe_ZV0Tf(c6{KyE_=UI7iGxeyw%9(! z(KLVj)ZSnAfNcq>1pjRoTP_Fi=^M5c|$4YuK=;1l?r@IWSnv_HIJo@J#r|W9xX4N z`I9BXKTot?n2UiVMTXgWx7Dth8F8YeCZ ze}eK$`e{+t4RI&J)p&)=9%id{PwVsJBQIw#)U^eWuAurcCFFOkdkR4>!gK6NW>c?hF- z#kTJ9hk4iq2M#ckLKquf3El`ZMa=vX%&azA?-A&)PA>ztW+HYs5+WeSa zHmUq==Es;S19QDkapi9*u7-jsT>Ur<&vp9>3<9YUbnWK{0E+VfTLdVK0HGYCHGtq{ zd`I2`f7&c3#?Vz^Tog(V{#qvRmxqupI}XDVD!Q3YGWkLEIv~+aIDco1=lYnD&$T(W zG1GT!ldoH4%DK1TI#<2(lD2u^wub8o-eux`W1k?Q7ov3& zVuk3XNRg|v3{|{qE1#0LZUZT8wAN%xc6{#4!}Eh;A>cJV=vqOhHKiv^mzcrF76*?E zhp4=Mo}RPOGbdK(>#T$ti}z?0hHvnQz;RU$I463^Zdb0>v{TZ3Wn#etK)z%7%-N?@ zF3feVn`=D+XoT`3wYhw{c__}c+3RjG^(@TH-Apm&@R@`dFkh~d#uRe=jRz%^(en!| zyd&|$C9;K8J)HgLSKih^PA1@r0db%&^CpgP=8VL4i#lN^2`-9I!rCu2c6RJ1=>Ipf zLLL)v!i58nz*%w{KRk#2ormX}!TcxA@4sXSst-l;4i4$S5bJP;^*R!bN(sp;<^$;T zUKEYj;4iBgRPw{a*py+$!1I}|0>=>Y27 z9xE4cVeVQ7AUZ)iy1mA0SqM8Hrh^S+QtzxR%iW&VxO^;&*AnBfcc$%hsoiP>ctmEn zJP?)yDK=5OQYy^J6nxWa4g5%IjCTbo|3vNNDYl34>I1URlJOu6`RsxM$N5f3H#;Cm zR73m63pfua7{E85fSME4k3?LZg{geqiqLqMw#gd3QD60=;}|bi1iW~6nOsv}B1DXv z7yVgcjB;u#$D8Qo91d{*fX+!}>YauM4x%ClXVo82sO;Cr6}Po}wcR!Zb2F_?5RhwU zi-UYFBAMgwc2RXs?pWGX`Y5LKB_QTNQ-O_XDrJ!!wl&bK+K&h^JU?@?FS?R)8242U z{3ag!v>XO0iv2T6zfpyKkzw((SHW=4eMrwtTf9X-oeW}hI(^1`3W$^dfF<4kPB}p6 zSxkxlJCo~hYIRza0q?__DhK;KPv)FLmU1wodwbfVw{abgeN_7Nb}_vg#p>_OBAx*g;hH=8^ru`HITvQeVsdM12xut~vrD1hd`Ib{JdT4a-9r zRKruZ9Bqv5eiWu2Xt|U0*D3P8GG7&|jfbuW65{wWi8BvF_^b$D$Cm;4JSCnn1oKx% z-QpWzt8m|eW-;B`DqDoc5iDf@)p5?+hD$5ga@Agh3(f^;B+QL8kG1^w%N_{0dX%Gn1^f2XV>`hbXlR2<8j0tM2P?`9` zc4Y_;X4S9Bjt(eNS`I{_fY@yK=$Hx!P}^(QE!zJ;`M)Nw|Gx{$x+Vj6QGJK~2@!8> zYrx-7a3gFB5_Qyp5xPBRSA2xW9;LzgF z^(Vo%Ywc5y)kK}!0wPI$Q0|er{Y=)zG?|PZ1@~^X3@K1#{05}0Wx|}AL{)QaoK)(n z$=*}6jrn1|^T)IM&1YfDw=lZdr`UirPu`*lmBj3YbZTpK7zc#Sbv1%uD2UV$GFkr? z`oexxWD8zDInfaXK>7l4>$xfAq%KzeZ5Kk+ET2U(@PXzd1+2-z=5s&E+#H;gw=)wI zo<>Z*X7eC_HopoJ+r7Bi-bDPk#8Qnv!00=V4=C_{j?wkT(z)R@?A}UA4~LGmRgDR= z!vIs~;=%_@!=FQ?T3%5ut=8YWaU15omVLLwWo!N>~Ew@}p$Sujoo>%U7y{*@eBKS1gc zMH(0bKF<~a_t34!Vy?i#g2QHL>Z$nU@IpfuFHC5NR@}y2l?6np>T@7V#R6ysW&-ok zk`0Nd^_yc(o6;Ng>Fw{pqYAQKNLJ3!fo+bs8n=yQ3NldlMacDovI_oSzbYRe18CXD zp(W%)O{@-+wkYF(ZSqKurEm8mb5boqiGJbYAFd(Dj0Eb0kZj&Hd7iIdzv7x%<+0F- zP>hjGWlas{);%KS$0ySkri{j{4&kns`7pS2-#fDJ{K5v}A1K&G9G`0e%)N`il`Xbwc1&W$O{%h~&*f2355dP^v90CC@ z;}^jh6*@y-lx#jwvb7W^l{D)4du}U*c@TNjC9b0P*e%HPKy4*tG3Sb-LHjr!O`)1EE&x=rkAT-*+@%Vk| zFZf`=jthO;5#)@5pIn04TlQ89X<)&!46lMjY(SF+5Z0z!feY&JOj7*F71OK#uD5JV1Lm()N;5Ln&9&YTy?X4 zecxN#1Mu;zorM%g&U`Y0K``p|3`tEFy`?7lIwnh;^0^2xRby_$b2MtSbu7C z{JA9z8Um2?zl8H|KlO|cVf~MW0L%WayoGY!b*{?52SmG?0D;L2{LYNtb5D^V^EQ??Ieeq>fWC;x`$ z9oU=pPT~sOx2OF|81s0-@ODtkdw6p!(S$uYj9(d&vpNIKXn?=V^_oR8 z8S09V)}6#6*hW{$37WpTj8u4qT$XJnsG!R}E5KXd8jmxY@I&J%e_sc=y0}u+BxTex z&_ifL1|X2HWf0e^)Ojbp;Kx%De&z6|S1Lbje@#Q4`xpZe^1;22Kwx&h7XePSeY=nf zd;shppaS2M|A7jO?3BkitnekZq;ZxxC&_cvawM`+=_6N<_?B`>(fFcV1)9?##W*Sp zD3tuDwWCVzz0y9v0b>?tSio^c6pwO{5Py_w?l>xPWO?%9nE2JGD`%BcM__MeyT@Jt zTC2Hg@KN@(!gmX6J3WcPcdME!5BL#-t>#yL4g64+wsH)-uUbyK86{TqVn(^5Pq;!- zUcHb0`DJ9l-^uY!YX)SKbRZ9~!G6d{I7{dtl>y@$Xr{=SsM!g@@1Jlo|630DkImG7 zeF(J((S!PLr@V#tflb%<+-;mN7DKnL-N3_{>CgU)0h;?eU;NjF4p$hd2~hPayI zwn#OfA-FV~H8qjjAko)JO46b^f^xQLZ7db&+zMTcRe(}#l~Rh?W;(Y>-Vg6LZ=+cT zvcXuTY{)aMTaBY(w)%rp`s{qJIXSm29yl$((fWi!@3wKL z#Q!Z->T`x)h_ZR~?}_q7dElxATc@kjllj;PAly@( zi6jH736-E1pUujW-luaj4q+qHF$c1VW^#_Qrtjl*D!q!JSogIq)}BD z^q#*M=8MWXnVZZyEj3~dLZc5UEws%jmani}(_5e<}W?^e(cB1iq4_8-_w=xAcq)hcy?a-icP zK?NY!j10aWvpIwGwoTiv!Q$0qC1)dzda2cGlagy61o51rtGdGuzXshVwm5kx^}j>| zUZ5-7iLe;uhM+04jZ*eaToOD)fZBny%sc=t{y0W!uOeUxz^BHSxcr5`hBuS-~}+O^|dy7DM<#pLUH1df(%*?IW6xap|%wp^L< z7N~yEzmuHj3VE}`L4oHFz1h$9PRQdxWVUgIXCn%auDtdF51i2p72)&vwXHHqfmV&t z#x7B2!)mj;Ka=UozDZA30`y(d9hQea8GG9PA0X3L676MMCC80nbPKdX|C%8BeIRZF zD6U`5$eEXI`A#BeJ~ga*e(u5LTKchoGzrzorA6IQCcbQ|Brh_$n^k17lUabCdWpK{ ziL`l3V>!c|L4cwac0+J0#^MY}l(L zfOk)RA?UmdOb)dG#CBBY1kM#Qy+9T)n{cJfD6zS+#uyJk7hKa#fZEoqPd`OZPZ=f0 zuC?QG3Xu4kTVp*@W9(Tw1(Lf!?{Sjg2@?#hfMfoz8ER}o{vJyGgbn*Y)eQA}PE3}+ zmmcw4Wafn>=JP+bzG}lfs61=+3+5Tue4q3c!#!L+ zN^14D@lIt}d-sCopYpe)M1A(G5$MEIT3wj~-Agz+hGI(^Wn)+y(-}Y+_V7ZMJ3Fhl zISJhqCuTR6xrZOWL-y>E1)?>5jec!a7Gfv}god`!iAE?x0b2T}8=*9oU}>*gA(UO0 zqFE=o9cFA4?s?!YZ+J^-JNvT(+x*~<9oXfM7uDX`8=-aMm{|5m8Ijl7JVAYzy}8ZN z1z@XnzyP;tNZYQjkF52{GOJ!Nxm5XV_*!!SGJFla$PQEi9&X^j_5{TRO0&e6mJ%af zlMd9fR{tCwB>`41`f8l{PSq1kRwnX+|1n=7<_jTY=rLdnt1=y)l)yEVSJ6|k0bIAz z(fn>uWpm`49ixy2dJ$?^?AuM`XDS5e-pdY~C(UV~*gI-gR@d&)YLS%zkD3`N8ApWUZU>vppAi9x3S~O}n)5Q8E zSqk(Vn>gz{r3|Z?j5w_`pf_8&EiuG4eiv};8ldkaJ_N%MdrO&TbRPk z_}>(hgPp6*H?F80Yk`utQ!>UC#}IHULy|iqlYMp8zSHudtYKW1*X;ECNg3H3zv!&L zyh)0u)w?&8qB}}rpgIR~jYz)s|2mOTKUbyx2fqJRoyb6xre2pb!Zui3R)E)CDdQT0 zwu?I|0bFuct;Bc}$2t}*qnPu&Nl>zfO2|dPft|!dPS;1BpJkYf3t+79W$p~<&sB17 zmhgE`wl}lzXxFA6IIt+9iE>^*pw%%a_hX-P5=?&tQgym2xt4xQTJra=s=~$e>)AC% zC{C|*T3Fj0?YXOpj9k&?rHJfSkZRUX=zB{(;z$pSavO|&^mZZ`gHLUSD$H`FtL^5M zv8D&zDAV3^yYLi!r`?NDz=0*zyCU=5Li*2j=!=2Vy(Y5#9v6)Z|xoLgDM1d&pj-ZwCk0>ro7!CpLf>o4BLR%mqLZs{1GVtOQ&9 z{$h_&gvd?V8?5O$)W%rznjjuy7^c+31AY=ssygW!G4EhOWe{X z|7x*Q<@hVIKVIE^yrXajF@&}e4)yi`)7B4ObOAbS4TVhuK-A7nED0^UUK3Zl_pXFV z0i61=XDQH@Ws@wt?|?*HW~4{KZOC8!{GG5${1>p*GM;~iv#|W@aTnPG*0gTE!wCh= zq>@*Y9CjRYR#yF${_hBF8T$!EMS zie6+`TEVw)(+fNaxsp##nQQQwc|puFEuV()x(8&N5UK&c_T6t_A8W<}joDSWC5~i) zU?SUX%{)b%-$`q`QDotRh6b?q*=rRTpUb{?9y%!^LFIP@R8MOa`az>{+64^Gsd}!y^7((Hzf)GjuU3exVs7@Kg=XaNl$C@7C82!}|B=+2drK5gq%2)z*qp;rtg0}y??N)Alq~bM+A5|=SZ+= z5({QSwcgK+S1EX~#p8>08Wq{2uzbj$c}J_#_&Y|eR+*q7Sb#actl~n{#x>}@`0NpG zS?!w(n|AKXjA*iraco;;@%A>#cUS@RxPdOG6C}`>_ItAyVZ5~iKFz1~Ai?qpu&Nw6 z0d>z7bNVLvBajdIVJ778EOP^Uln}_?92b~1zmd)-e*JqKk^g%d_aE%4f4Rxzb4PBW zAM|!AUjd0D!kn~elRt5EV%Y(&hu*%3bka+nuVpK-t3Igh!ZA({V?p%is*!x`{E=80 z#n_uV6JJsXt^ME~GMy8~av{+9GtKM&@`UW)@+K*fL7BkbfDpO^cmb%$vZbpmu9}g< F{~tJM=}Z6s literal 0 HcmV?d00001 diff --git a/examples/core/core_3d_camera_free.c b/examples/core/core_3d_camera_free.c index 59bd158a2..78200a642 100644 --- a/examples/core/core_3d_camera_free.c +++ b/examples/core/core_3d_camera_free.c @@ -72,7 +72,7 @@ int main(void) DrawText("- Mouse Wheel to Zoom in-out", 40, 40, 10, DARKGRAY); DrawText("- Mouse Wheel Pressed to Pan", 40, 60, 10, DARKGRAY); DrawText("- Alt + Mouse Wheel Pressed to Rotate", 40, 80, 10, DARKGRAY); - DrawText("- Alt + Ctrl + Mouse Wheel Pressed for Smooth Zoom", 40, 100, 10, DARKGRAY); + //DrawText("- Alt + Ctrl + Mouse Wheel Pressed for Smooth Zoom", 40, 100, 10, DARKGRAY); DrawText("- Z to zoom to (0, 0, 0)", 40, 120, 10, DARKGRAY); EndDrawing(); diff --git a/examples/core/core_split_screen.c b/examples/core/core_3d_camera_split_screen.c similarity index 68% rename from examples/core/core_split_screen.c rename to examples/core/core_3d_camera_split_screen.c index 50cfcf7ac..d625e1c0e 100644 --- a/examples/core/core_split_screen.c +++ b/examples/core/core_3d_camera_split_screen.c @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raylib [core] example - split screen +* raylib [core] example - 3d cmaera split screen * * Example originally created with raylib 3.7, last time updated with raylib 4.0 * @@ -15,32 +15,6 @@ #include "raylib.h" -Camera cameraPlayer1 = { 0 }; -Camera cameraPlayer2 = { 0 }; - -// Scene drawing -void DrawScene(void) -{ - int count = 5; - float spacing = 4; - - // Grid of cube trees on a plane to make a "world" - DrawPlane((Vector3){ 0, 0, 0 }, (Vector2){ 50, 50 }, BEIGE); // Simple world plane - - for (float x = -count*spacing; x <= count*spacing; x += spacing) - { - for (float z = -count*spacing; z <= count*spacing; z += spacing) - { - DrawCube((Vector3) { x, 1.5f, z }, 1, 1, 1, LIME); - DrawCube((Vector3) { x, 0.5f, z }, 0.25f, 1, 0.25f, BROWN); - } - } - - // Draw a cube at each player's position - DrawCube(cameraPlayer1.position, 1, 1, 1, RED); - DrawCube(cameraPlayer2.position, 1, 1, 1, BLUE); -} - //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ @@ -51,9 +25,10 @@ int main(void) const int screenWidth = 800; const int screenHeight = 450; - InitWindow(screenWidth, screenHeight, "raylib [core] example - split screen"); + InitWindow(screenWidth, screenHeight, "raylib [core] example - 3d camera split screen"); // Setup player 1 camera and screen + Camera cameraPlayer1 = { 0 }; cameraPlayer1.fovy = 45.0f; cameraPlayer1.up.y = 1.0f; cameraPlayer1.target.y = 1.0f; @@ -63,6 +38,7 @@ int main(void) RenderTexture screenPlayer1 = LoadRenderTexture(screenWidth/2, screenHeight); // Setup player two camera and screen + Camera cameraPlayer2 = { 0 }; cameraPlayer2.fovy = 45.0f; cameraPlayer2.up.y = 1.0f; cameraPlayer2.target.y = 3.0f; @@ -73,6 +49,10 @@ int main(void) // Build a flipped rectangle the size of the split view to use for drawing later Rectangle splitScreenRect = { 0.0f, 0.0f, (float)screenPlayer1.texture.width, (float)-screenPlayer1.texture.height }; + + // Grid data + int count = 5; + float spacing = 4; SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -116,26 +96,69 @@ int main(void) // Draw Player1 view to the render texture BeginTextureMode(screenPlayer1); ClearBackground(SKYBLUE); + BeginMode3D(cameraPlayer1); - DrawScene(); + + // Draw scene: grid of cube trees on a plane to make a "world" + DrawPlane((Vector3){ 0, 0, 0 }, (Vector2){ 50, 50 }, BEIGE); // Simple world plane + + for (float x = -count*spacing; x <= count*spacing; x += spacing) + { + for (float z = -count*spacing; z <= count*spacing; z += spacing) + { + DrawCube((Vector3) { x, 1.5f, z }, 1, 1, 1, LIME); + DrawCube((Vector3) { x, 0.5f, z }, 0.25f, 1, 0.25f, BROWN); + } + } + + // Draw a cube at each player's position + DrawCube(cameraPlayer1.position, 1, 1, 1, RED); + DrawCube(cameraPlayer2.position, 1, 1, 1, BLUE); + EndMode3D(); - DrawText("PLAYER1 W/S to move", 10, 10, 20, RED); + + DrawRectangle(0, 0, GetScreenWidth()/2, 40, Fade(RAYWHITE, 0.8f)); + DrawText("PLAYER1: W/S to move", 10, 10, 20, MAROON); + EndTextureMode(); // Draw Player2 view to the render texture BeginTextureMode(screenPlayer2); ClearBackground(SKYBLUE); + BeginMode3D(cameraPlayer2); - DrawScene(); + + // Draw scene: grid of cube trees on a plane to make a "world" + DrawPlane((Vector3){ 0, 0, 0 }, (Vector2){ 50, 50 }, BEIGE); // Simple world plane + + for (float x = -count*spacing; x <= count*spacing; x += spacing) + { + for (float z = -count*spacing; z <= count*spacing; z += spacing) + { + DrawCube((Vector3) { x, 1.5f, z }, 1, 1, 1, LIME); + DrawCube((Vector3) { x, 0.5f, z }, 0.25f, 1, 0.25f, BROWN); + } + } + + // Draw a cube at each player's position + DrawCube(cameraPlayer1.position, 1, 1, 1, RED); + DrawCube(cameraPlayer2.position, 1, 1, 1, BLUE); + EndMode3D(); - DrawText("PLAYER2 UP/DOWN to move", 10, 10, 20, BLUE); + + DrawRectangle(0, 0, GetScreenWidth()/2, 40, Fade(RAYWHITE, 0.8f)); + DrawText("PLAYER2: UP/DOWN to move", 10, 10, 20, DARKBLUE); + EndTextureMode(); // Draw both views render textures to the screen side by side BeginDrawing(); ClearBackground(BLACK); + DrawTextureRec(screenPlayer1.texture, splitScreenRect, (Vector2){ 0, 0 }, WHITE); DrawTextureRec(screenPlayer2.texture, splitScreenRect, (Vector2){ screenWidth/2.0f, 0 }, WHITE); + + DrawRectangle(GetScreenWidth()/2 - 2, 0, 4, GetScreenHeight(), LIGHTGRAY); EndDrawing(); } diff --git a/examples/core/core_3d_camera_split_screen.png b/examples/core/core_3d_camera_split_screen.png new file mode 100644 index 0000000000000000000000000000000000000000..bc323d6adbb22a04677a9435398234601dd45429 GIT binary patch literal 16165 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 D;+@s` literal 0 HcmV?d00001 diff --git a/examples/core/core_camera_2d_split_screen.c b/examples/core/core_camera_2d_split_screen.c deleted file mode 100644 index 5f5059569..000000000 --- a/examples/core/core_camera_2d_split_screen.c +++ /dev/null @@ -1,137 +0,0 @@ -/******************************************************************************************* -* -* raylib [core] example - split screen -* -* Addapted from the Split Screen example (https://github.com/raysan5/raylib/blob/master/examples/core/core_split_screen.c) -* -* Example originally created with raylib 4.5, last time updated with raylib 4.5 -* -* Example contributed by Jeffery Myers (@JeffM2501) and reviewed by Ramon Santamaria (@raysan5) -* -* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, -* BSD-like license that allows static linking with closed source software -* -* Copyright (c) 2021-2023 Jeffery Myers (@JeffM2501) -* -********************************************************************************************/ - -#include "raylib.h" - -#include - -#define PLAYER_SIZE 40 - -//------------------------------------------------------------------------------------ -// Program main entry point -//------------------------------------------------------------------------------------ -int main(void) -{ - // Initialization - //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 440; - - InitWindow(screenWidth, screenHeight, "raylib [core] example - camera 2D split screen"); - - Rectangle player1 = { 200, 200, PLAYER_SIZE, PLAYER_SIZE }; - Rectangle player2 = { 250, 200, PLAYER_SIZE, PLAYER_SIZE }; - - Camera2D camera1 = { 0 }; - camera1.target = (Vector2){ player1.x, player1.y }; - camera1.offset = (Vector2){ 200.0f, 200.0f }; - camera1.rotation = 0.0f; - camera1.zoom = 1.0f; - - Camera2D camera2 = { 0 }; - camera2.target = (Vector2){ player2.x, player2.y }; - camera2.offset = (Vector2){ 200.0f, 200.0f }; - camera2.rotation = 0.0f; - camera2.zoom = 1.0f; - - RenderTexture screenCamera1 = LoadRenderTexture(screenWidth / 2, screenHeight); - RenderTexture screenCamera2 = LoadRenderTexture(screenWidth / 2, screenHeight); - - // Build a flipped rectangle the size of the split view to use for drawing later - Rectangle splitScreenRect = { 0.0f, 0.0f, (float)screenCamera1.texture.width, (float)-screenCamera1.texture.height }; - - SetTargetFPS(60); // Set our game to run at 60 frames-per-second - //-------------------------------------------------------------------------------------- - void DrawScene(void) { - for (int i = 0; i < screenWidth/PLAYER_SIZE + 1; i++) - { - DrawLineV((Vector2){PLAYER_SIZE*i, 0}, (Vector2){PLAYER_SIZE*i, screenHeight}, LIGHTGRAY); - } - - for (int i = 0; i < screenHeight/PLAYER_SIZE + 1; i++) - { - DrawLineV((Vector2){0, PLAYER_SIZE*i}, (Vector2){screenWidth, PLAYER_SIZE*i}, LIGHTGRAY); - } - - for (int i = 0; i < screenWidth/PLAYER_SIZE; i++) - { - for (int j = 0; j < screenHeight/PLAYER_SIZE; j++) - { - char coordinate_str[8]; - snprintf(coordinate_str, sizeof(coordinate_str), "%d,%d", i, j); - DrawText(coordinate_str, 10 + PLAYER_SIZE*i, 10 + PLAYER_SIZE*j, 10, LIGHTGRAY); - } - } - - DrawRectangleRec(player1, RED); - DrawRectangleRec(player2, BLUE); - } - - // Main game loop - while (!WindowShouldClose()) // Detect window close button or ESC key - { - // Update - //---------------------------------------------------------------------------------- - if (IsKeyDown(KEY_S)) player1.y += 3; - else if (IsKeyDown(KEY_W)) player1.y -= 3; - if (IsKeyDown(KEY_D)) player1.x += 3; - else if (IsKeyDown(KEY_A)) player1.x -= 3; - - if (IsKeyDown(KEY_UP)) player2.y += 3; - else if (IsKeyDown(KEY_DOWN)) player2.y -= 3; - if (IsKeyDown(KEY_RIGHT)) player2.x += 3; - else if (IsKeyDown(KEY_LEFT)) player2.x -= 3; - - camera1.target = (Vector2){ player1.x, player1.y }; - camera2.target = (Vector2){ player2.x, player2.y }; - - // Draw - //---------------------------------------------------------------------------------- - BeginTextureMode(screenCamera1); - ClearBackground(RAYWHITE); - BeginMode2D(camera1); - DrawScene(); - EndMode2D(); - DrawText("PLAYER1 W/S/A/D to move", 10, 10, 15, RED); - EndTextureMode(); - - BeginTextureMode(screenCamera2); - ClearBackground(RAYWHITE); - BeginMode2D(camera2); - DrawScene(); - EndMode2D(); - DrawText("PLAYER2 UP/DOWN/LEFT/RIGHT to move", 10, 10, 15, BLUE); - EndTextureMode(); - - // Draw both views render textures to the screen side by side - BeginDrawing(); - ClearBackground(BLACK); - DrawTextureRec(screenCamera1.texture, splitScreenRect, (Vector2){ 0, 0 }, WHITE); - DrawTextureRec(screenCamera2.texture, splitScreenRect, (Vector2){ screenWidth/2.0f, 0 }, WHITE); - EndDrawing(); - } - - // De-Initialization - //-------------------------------------------------------------------------------------- - UnloadRenderTexture(screenCamera1); // Unload render texture - UnloadRenderTexture(screenCamera2); // Unload render texture - - CloseWindow(); // Close window and OpenGL context - //-------------------------------------------------------------------------------------- - - return 0; -} diff --git a/examples/core/core_camera_2d_split_screen.png b/examples/core/core_camera_2d_split_screen.png deleted file mode 100644 index ed5aaa58bfc2bb8124098d2b5c583961414bc218..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20010 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 diff --git a/examples/core/core_split_screen.png b/examples/core/core_split_screen.png deleted file mode 100644 index eace9027b0710f3a743641e6ca7d4930295d5603..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21609 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 0) + { + points[selectedPoint] = GetMousePosition(); + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) selectedPoint = -1; + } + + // TODO: Cubic Bezier spline control points logic + + + // Spline selection logic + if (IsKeyPressed(KEY_ONE)) splineType = 0; + else if (IsKeyPressed(KEY_TWO)) splineType = 1; + else if (IsKeyPressed(KEY_THREE)) splineType = 2; + else if (IsKeyPressed(KEY_FOUR)) splineType = 3; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + if (splineType == 0) // Linear + { + // Draw linear spline + for (int i = 0; i < pointCount - 1; i++) + { + DrawLineEx(points[i], points[i + 1], 2.0f, RED); + } + } + else if (splineType == 1) // B-Spline + { + // Draw b-spline + DrawLineBSpline(points, pointCount, 2.0f, RED); + //for (int i = 0; i < (pointCount - 3); i++) DrawLineBSplineSegment(points[i], points[i + 1], points[i + 2], points[i + 3], 24.0f, BLUE); + } + else if (splineType == 2) // CatmullRom Spline + { + // Draw spline: catmull-rom + DrawLineCatmullRom(points, pointCount, 2.0f, RED); + //for (int i = 0; i < (pointCount - 3); i++) DrawLineCatmullRomSegment(points[i], points[i + 1], points[i + 2], points[i + 3], 24.0f, Fade(BLUE, 0.4f)); + } + else if (splineType == 3) // Cubic Bezier + { + // Draw line bezier cubic (with control points) + for (int i = 0; i < pointCount - 1; i++) + { + DrawLineBezierCubic(points[i], points[i + 1], control[i].start, control[i + 1].end, 2.0f, RED); + + // TODO: Every cubic bezier point should have two control points + DrawCircleV(control[i].start, 4, GOLD); + DrawCircleV(control[i].end, 4, GOLD); + DrawLineEx(points[i], control[i].start, 1.0, LIGHTGRAY); + DrawLineEx(points[i + 1], control[i].end, 1.0, LIGHTGRAY); + } + } + + // Draw control points + for (int i = 0; i < pointCount; i++) + { + DrawCircleV(points[i], 6.0f, RED); + if ((splineType != 0) && (i < pointCount - 1)) DrawLineV(points[i], points[i + 1], GRAY); + } + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/projects/VS2022/examples/core_split_screen.vcxproj b/projects/VS2022/examples/core_2d_camera_split_screen.vcxproj similarity index 98% rename from projects/VS2022/examples/core_split_screen.vcxproj rename to projects/VS2022/examples/core_2d_camera_split_screen.vcxproj index 1005e2eaf..98ddc9ed3 100644 --- a/projects/VS2022/examples/core_split_screen.vcxproj +++ b/projects/VS2022/examples/core_2d_camera_split_screen.vcxproj @@ -35,11 +35,11 @@ - {946A1700-C7AA-46F0-AEF2-67C98B5722AC} + {CC62F7DB-D089-4677-8575-CAB7A7815C43} Win32Proj - core_split_screen + core_2d_camera_split_screen 10.0 - core_split_screen + core_2d_camera_split_screen @@ -374,7 +374,7 @@ - + diff --git a/projects/VS2022/examples/core_camera_2d_split_screen.vcxproj b/projects/VS2022/examples/core_3d_camera_split_screen.vcxproj similarity index 99% rename from projects/VS2022/examples/core_camera_2d_split_screen.vcxproj rename to projects/VS2022/examples/core_3d_camera_split_screen.vcxproj index 1efc58a65..f39ccc205 100644 --- a/projects/VS2022/examples/core_camera_2d_split_screen.vcxproj +++ b/projects/VS2022/examples/core_3d_camera_split_screen.vcxproj @@ -37,9 +37,9 @@ {946A1700-C7AA-46F0-AEF2-67C98B5722AC} Win32Proj - core_camera_2d_split_screen + core_3d_camera_split_screen 10.0 - core_camera_2d_split_screen + core_3d_camera_split_screen @@ -374,7 +374,7 @@ - + @@ -384,4 +384,4 @@ - + \ No newline at end of file diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 173637554..007f796f9 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -33,6 +33,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_3d_camera_free", "exam EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_3d_camera_mode", "examples\core_3d_camera_mode.vcxproj", "{6D1CA2F1-7FCA-4249-9220-075C2DF4F965}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_3d_camera_split_screen", "examples\core_3d_camera_split_screen.vcxproj", "{946A1700-C7AA-46F0-AEF2-67C98B5722AC}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_3d_picking", "examples\core_3d_picking.vcxproj", "{FD193822-3D5C-4161-A147-884C2ABDE483}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_custom_logging", "examples\core_custom_logging.vcxproj", "{20AD0AC9-9159-4744-99CC-6AC5779D6B87}" @@ -233,8 +235,6 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "easings_testbed", "examples EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rlgl_standalone", "examples\rlgl_standalone.vcxproj", "{C8765523-58F8-4C8E-9914-693396F6F0FF}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_split_screen", "examples\core_split_screen.vcxproj", "{946A1700-C7AA-46F0-AEF2-67C98B5722AC}" -EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_loading_vox", "examples\models_loading_vox.vcxproj", "{2F1B955B-275E-4D8E-8864-06FEC44D7912}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "models_loading_gltf", "examples\models_loading_gltf.vcxproj", "{F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71}" @@ -271,6 +271,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_hybrid_render", "ex EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_sound_multi", "examples\audio_sound_multi.vcxproj", "{F81C5819-85B4-4D2E-B6DC-104A7634461B}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_2d_camera_split_screen", "examples\core_2d_camera_split_screen.vcxproj", "{CC62F7DB-D089-4677-8575-CAB7A7815C43}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|x64 = Debug.DLL|x64 @@ -507,6 +509,22 @@ Global {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release|x64.Build.0 = Release|x64 {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release|x86.ActiveCfg = Release|Win32 {6D1CA2F1-7FCA-4249-9220-075C2DF4F965}.Release|x86.Build.0 = Release|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x64.ActiveCfg = Debug|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x64.Build.0 = Debug|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x86.ActiveCfg = Debug|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x86.Build.0 = Debug|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x64.ActiveCfg = Release|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x64.Build.0 = Release|x64 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x86.ActiveCfg = Release|Win32 + {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x86.Build.0 = Release|Win32 {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 {FD193822-3D5C-4161-A147-884C2ABDE483}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 @@ -1975,22 +1993,6 @@ Global {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|x64.Build.0 = Release|x64 {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|x86.ActiveCfg = Release|Win32 {C8765523-58F8-4C8E-9914-693396F6F0FF}.Release|x86.Build.0 = Release|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x64.ActiveCfg = Debug|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x64.Build.0 = Debug|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x86.ActiveCfg = Debug|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Debug|x86.Build.0 = Debug|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x64.ActiveCfg = Release|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x64.Build.0 = Release|x64 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x86.ActiveCfg = Release|Win32 - {946A1700-C7AA-46F0-AEF2-67C98B5722AC}.Release|x86.Build.0 = Release|Win32 {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 {2F1B955B-275E-4D8E-8864-06FEC44D7912}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 @@ -2279,6 +2281,22 @@ Global {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x64.Build.0 = Release|x64 {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x86.ActiveCfg = Release|Win32 {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x86.Build.0 = Release|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug|x64.ActiveCfg = Debug|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug|x64.Build.0 = Debug|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug|x86.ActiveCfg = Debug|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Debug|x86.Build.0 = Debug|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x64.ActiveCfg = Release|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x64.Build.0 = Release|x64 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x86.ActiveCfg = Release|Win32 + {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2297,6 +2315,7 @@ Global {557138B0-7BE2-4392-B2E2-B45734031A62} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {9EED87BB-527F-4D05-9384-6D16CFD627A8} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {6D1CA2F1-7FCA-4249-9220-075C2DF4F965} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {946A1700-C7AA-46F0-AEF2-67C98B5722AC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {FD193822-3D5C-4161-A147-884C2ABDE483} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {20AD0AC9-9159-4744-99CC-6AC5779D6B87} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {0199E349-0701-40BC-8A7F-06A54FFA3E7C} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} @@ -2397,7 +2416,6 @@ Global {FDE6080B-E203-4066-910D-AD0302566008} = {E9D708A5-9C1F-4B84-A795-C5F191801762} {E1B6D565-9D7C-46B7-9202-ECF54974DE50} = {E9D708A5-9C1F-4B84-A795-C5F191801762} {C8765523-58F8-4C8E-9914-693396F6F0FF} = {E9D708A5-9C1F-4B84-A795-C5F191801762} - {946A1700-C7AA-46F0-AEF2-67C98B5722AC} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} {2F1B955B-275E-4D8E-8864-06FEC44D7912} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {F5FC9279-DE63-4EF3-B31F-CFCEF9B11F71} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {F2DB2E59-76BF-4D81-859A-AFC289C046C0} = {8D3C83B7-F1E0-4C2E-9E34-EE5F6AB2502A} @@ -2416,6 +2434,7 @@ Global {70B35F59-AFC2-4D8F-8833-5314D2047A81} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {3755E9F4-CB48-4EC3-B561-3B85964EBDEF} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {F81C5819-85B4-4D2E-B6DC-104A7634461B} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} + {CC62F7DB-D089-4677-8575-CAB7A7815C43} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} From 10e4aa32f86cb40e72b3ab076d87de6bf649f215 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 8 Sep 2023 20:01:19 +0200 Subject: [PATCH 0262/1350] Update rtext.c --- src/rtext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtext.c b/src/rtext.c index 04d7a2a14..146fc68f4 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -788,7 +788,7 @@ Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyp } } - // Copy pixel data from fc.data to atlas + // Copy pixel data from glyph image to atlas for (int y = 0; y < glyphs[i].image.height; y++) { for (int x = 0; x < glyphs[i].image.width; x++) From b68d0850b1dee42d818e67d99f4affe1d890dba1 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 8 Sep 2023 20:01:52 +0200 Subject: [PATCH 0263/1350] Some code restructuring for input functions, consistency review --- src/rcore.c | 100 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 67 insertions(+), 33 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index fcb222bb3..2f112aee5 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -389,15 +389,15 @@ typedef struct CoreData { bool resizedLastFrame; // Check if window has been resized last frame bool eventWaiting; // Wait for events before ending frame - Point position; // Window position on screen (required on fullscreen toggle) + Point position; // Window position (required on fullscreen toggle) + Point previousPosition; // Window previous position (required on borderless windowed toggle) Size display; // Display width and height (monitor, device-screen, LCD, ...) Size screen; // Screen width and height (used render area) + Size previousScreen; // Screen previous width and height (required on borderless windowed toggle) Size currentFbo; // Current render width and height (depends on active fbo) Size render; // Framebuffer width and height (render area, including black bars if required) Point renderOffset; // Offset from render area (must be divided by 2) Matrix screenScale; // Matrix to scale screen (framebuffer rendering) - Point previousPosition; // Previous screen position (required on borderless windowed toggle) - Size previousScreen; // Previous screen size (required on borderless windowed toggle) char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) unsigned int dropFileCount; // Count dropped files strings @@ -3753,10 +3753,12 @@ void OpenURL(const char *url) // Check if a key has been pressed once bool IsKeyPressed(int key) { - if ((key < 0) || (key >= MAX_KEYBOARD_KEYS)) return false; bool pressed = false; - if ((CORE.Input.Keyboard.previousKeyState[key] == 0) && (CORE.Input.Keyboard.currentKeyState[key] == 1)) pressed = true; + if ((key > 0) && (key < MAX_KEYBOARD_KEYS)) + { + if ((CORE.Input.Keyboard.previousKeyState[key] == 0) && (CORE.Input.Keyboard.currentKeyState[key] == 1)) pressed = true; + } return pressed; } @@ -3764,26 +3766,38 @@ bool IsKeyPressed(int key) // Check if a key has been pressed again (only PLATFORM_DESKTOP) bool IsKeyPressedRepeat(int key) { - if ((key < 0) || (key >= MAX_KEYBOARD_KEYS)) return false; - if (CORE.Input.Keyboard.keyRepeatInFrame[key] == 1) return true; - else return false; + bool repeat = false; + + if ((key > 0) && (key < MAX_KEYBOARD_KEYS)) + { + if (CORE.Input.Keyboard.keyRepeatInFrame[key] == 1) repeat = true; + } + + return repeat; } // Check if a key is being pressed (key held down) bool IsKeyDown(int key) { - if ((key < 0) || (key >= MAX_KEYBOARD_KEYS)) return false; - if (CORE.Input.Keyboard.currentKeyState[key] == 1) return true; - else return false; + bool down = false; + + if ((key > 0) && (key < MAX_KEYBOARD_KEYS)) + { + if (CORE.Input.Keyboard.currentKeyState[key] == 1) down = true; + } + + return down; } // Check if a key has been released once bool IsKeyReleased(int key) { - if ((key < 0) || (key >= MAX_KEYBOARD_KEYS)) return false; bool released = false; - - if ((CORE.Input.Keyboard.previousKeyState[key] == 1) && (CORE.Input.Keyboard.currentKeyState[key] == 0)) released = true; + + if ((key > 0) && (key < MAX_KEYBOARD_KEYS)) + { + if ((CORE.Input.Keyboard.previousKeyState[key] == 1) && (CORE.Input.Keyboard.currentKeyState[key] == 0)) released = true; + } return released; } @@ -3791,9 +3805,14 @@ bool IsKeyReleased(int key) // Check if a key is NOT being pressed (key not held down) bool IsKeyUp(int key) { - if ((key < 0) || (key >= MAX_KEYBOARD_KEYS)) return false; - if (CORE.Input.Keyboard.currentKeyState[key] == 0) return true; - else return false; + bool up = false; + + if ((key > 0) && (key < MAX_KEYBOARD_KEYS)) + { + if (CORE.Input.Keyboard.currentKeyState[key] == 0) up = true; + } + + return up; } // Get the last key pressed @@ -3806,7 +3825,7 @@ int GetKeyPressed(void) // Get character from the queue head value = CORE.Input.Keyboard.keyPressedQueue[0]; - // Shift elements 1 step toward the head. + // Shift elements 1 step toward the head for (int i = 0; i < (CORE.Input.Keyboard.keyPressedQueueCount - 1); i++) CORE.Input.Keyboard.keyPressedQueue[i] = CORE.Input.Keyboard.keyPressedQueue[i + 1]; @@ -3828,7 +3847,7 @@ int GetCharPressed(void) // Get character from the queue head value = CORE.Input.Keyboard.charPressedQueue[0]; - // Shift elements 1 step toward the head. + // Shift elements 1 step toward the head for (int i = 0; i < (CORE.Input.Keyboard.charPressedQueueCount - 1); i++) CORE.Input.Keyboard.charPressedQueue[i] = CORE.Input.Keyboard.charPressedQueue[i + 1]; @@ -3864,18 +3883,23 @@ bool IsGamepadAvailable(int gamepad) // Get gamepad internal name id const char *GetGamepadName(int gamepad) { + const char *name = NULL; + #if defined(PLATFORM_DESKTOP) - if (CORE.Input.Gamepad.ready[gamepad]) return glfwGetJoystickName(gamepad); - else return NULL; + if (CORE.Input.Gamepad.ready[gamepad]) name = glfwGetJoystickName(gamepad); #endif #if defined(PLATFORM_DRM) - if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); - return CORE.Input.Gamepad.name[gamepad]; + if (CORE.Input.Gamepad.ready[gamepad]) + { + ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); + name = CORE.Input.Gamepad.name[gamepad]; + } #endif #if defined(PLATFORM_WEB) - return CORE.Input.Gamepad.name[gamepad]; + name = CORE.Input.Gamepad.name[gamepad]; #endif - return NULL; + + return name; } // Get gamepad axis count @@ -3915,12 +3939,12 @@ bool IsGamepadButtonPressed(int gamepad, int button) // Check if a gamepad button is being pressed bool IsGamepadButtonDown(int gamepad, int button) { - bool result = false; + bool down = false; if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) && - (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 1)) result = true; + (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 1)) down = true; - return result; + return down; } // Check if a gamepad button has NOT been pressed once @@ -3937,12 +3961,12 @@ bool IsGamepadButtonReleased(int gamepad, int button) // Check if a gamepad button is NOT being pressed bool IsGamepadButtonUp(int gamepad, int button) { - bool result = false; + bool up = false; if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (button < MAX_GAMEPAD_BUTTONS) && - (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 0)) result = true; + (CORE.Input.Gamepad.currentButtonState[gamepad][button] == 0)) up = true; - return result; + return up; } // Get the last gamepad button pressed @@ -3983,7 +4007,7 @@ bool IsMouseButtonDown(int button) if (CORE.Input.Mouse.currentButtonState[button] == 1) down = true; - // Map touches to mouse buttons checking + // NOTE: Touches are considered like mouse buttons if (CORE.Input.Touch.currentTouchState[button] == 1) down = true; return down; @@ -4005,7 +4029,14 @@ bool IsMouseButtonReleased(int button) // Check if a mouse button is NOT being pressed bool IsMouseButtonUp(int button) { - return !IsMouseButtonDown(button); + bool up = false; + + if (CORE.Input.Mouse.currentButtonState[button] == 0) up = true; + + // NOTE: Touches are considered like mouse buttons + if (CORE.Input.Touch.currentTouchState[button] == 0) up = true; + + return up; } // Get mouse position X @@ -4032,10 +4063,13 @@ int GetMouseY(void) Vector2 GetMousePosition(void) { Vector2 position = { 0 }; + + // TODO: Review touch position on PLATFORM_WEB #if defined(PLATFORM_ANDROID) //|| defined(PLATFORM_WEB) position = GetTouchPosition(0); #else + // NOTE: On PLATFORM_WEB, even on canvas scaling, mouse position is proportionally returned position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; #endif From 37f60e75e72eebd5ff687b8857bf9043d291b0df Mon Sep 17 00:00:00 2001 From: MichaelFiber <42419558+michaelfiber@users.noreply.github.com> Date: Sat, 9 Sep 2023 03:47:07 -0400 Subject: [PATCH 0264/1350] Remove unneeded #if (#3301) Co-authored-by: MichaelFiber --- src/rcore.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 2f112aee5..8df087f83 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -265,11 +265,9 @@ #include // Linux: Keycodes constants definition (KEY_A, ...) #include // Linux: Joystick support library -#if defined(PLATFORM_DRM) #include // Generic Buffer Management (native platform for EGL on DRM) #include // Direct Rendering Manager user-level library interface #include // Direct Rendering Manager mode setting (KMS) interface -#endif #include "EGL/egl.h" // Native platform windowing system interface #include "EGL/eglext.h" // EGL extensions From b8cd10264b6d34ff4b09ccdd0b0f7b254cf3b122 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Sat, 9 Sep 2023 11:32:28 -0400 Subject: [PATCH 0265/1350] Revert "Disable UBSAN in zig builds. (#3292)" (#3303) This reverts commit a316f9e7fc7f8e06852a40544e57f89018672ee4. Issue #1891 was fixed again, so this is no longer needed. --- src/build.zig | 1 - 1 file changed, 1 deletion(-) diff --git a/src/build.zig b/src/build.zig index 09d954bf5..27250f5ff 100644 --- a/src/build.zig +++ b/src/build.zig @@ -6,7 +6,6 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built "-std=gnu99", "-D_GNU_SOURCE", "-DGL_SILENCE_DEPRECATION=199309L", - "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 }; const raylib = b.addStaticLibrary(.{ From 30f8dd6e377ba022a70ebdbac78a10f5b27af0eb Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Mon, 11 Sep 2023 13:00:30 -0400 Subject: [PATCH 0266/1350] rtextures: Fix ImageDraw() source clipping when drawing beyond top left (#3306) --- src/rtextures.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index 4e2fdbd91..c86ebac39 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3481,7 +3481,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color // Destination rectangle out-of-bounds security checks if (dstRec.x < 0) { - srcRec.x = -dstRec.x; + srcRec.x -= dstRec.x; srcRec.width += dstRec.x; dstRec.x = 0; } @@ -3489,7 +3489,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color if (dstRec.y < 0) { - srcRec.y = -dstRec.y; + srcRec.y -= dstRec.y; srcRec.height += dstRec.y; dstRec.y = 0; } From e75f85ce58ccc73a54ec8116f4890247587727ab Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 11 Sep 2023 19:01:24 +0200 Subject: [PATCH 0267/1350] REVIEWED: `TextToPascal()` issue when first char is uppercase --- src/rtext.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rtext.c b/src/rtext.c index 146fc68f4..fb8440131 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1676,6 +1676,7 @@ const char *TextToPascal(const char *text) { // Upper case first character if ((text[0] >= 'a') && (text[0] <= 'z')) buffer[0] = text[0] - 32; + else buffer[0] = text[0]; // Check for next separator to upper case another character for (int i = 1, j = 1; (i < MAX_TEXT_BUFFER_LENGTH - 1) && (text[j] != '\0'); i++, j++) From 9d230d753b5dfb9c4f56b065833ac5ddaf265d8c Mon Sep 17 00:00:00 2001 From: Peter0x44 Date: Mon, 11 Sep 2023 17:03:33 +0000 Subject: [PATCH 0268/1350] Implement FLAG_WINDOW_RESIZABLE for web (#3305) Fixes #3231 --- src/rcore.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 8df087f83..beb611040 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -914,9 +914,9 @@ void InitWindow(int width, int height, const char *title) // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas) //emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); // Check Resize event (note this is done on the window since most browsers don't support this on #canvas) - //emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); // Trigger this once to get initial window sizing - //EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); + EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); // Support keyboard events -> Not used, GLFW.JS takes care of that //emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); @@ -6165,8 +6165,8 @@ static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUi return 1; // The event was consumed by the callback handler } -EM_JS(int, GetCanvasWidth, (), { return canvas.clientWidth; }); -EM_JS(int, GetCanvasHeight, (), { return canvas.clientHeight; }); +EM_JS(int, GetWindowInnerWidth, (), { return window.innerWidth; }); +EM_JS(int, GetWindowInnerHeight, (), { return window.innerHeight; }); // Register DOM element resize event static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData) @@ -6176,8 +6176,8 @@ static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent * // This event is called whenever the window changes sizes, // so the size of the canvas object is explicitly retrieved below - int width = GetCanvasWidth(); - int height = GetCanvasHeight(); + int width = GetWindowInnerWidth(); + int height = GetWindowInnerHeight(); emscripten_set_canvas_element_size("#canvas",width,height); SetupViewport(width, height); // Reset viewport and projection matrix for new size From 3ab7f70e3129be963812a9b4133278e8a5e41797 Mon Sep 17 00:00:00 2001 From: Kenta <106167071+Its-Kenta@users.noreply.github.com> Date: Mon, 11 Sep 2023 22:19:43 +0100 Subject: [PATCH 0269/1350] Update BINDINGS.md (#3307) Fix Kaylib binding. Reroute to a new repository. Binding renamed. --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index bdd5e2558..c26fa873e 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -37,7 +37,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-j | 4.0 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | Zlib | https://github.com/CreedVI/Raylib-J | | raylib.jl | 4.2 | [Julia](https://julialang.org/) | Zlib | https://github.com/irishgreencitrus/raylib.jl | | kaylib | 3.7 | [Kotlin/native](https://kotlinlang.org) | ? | https://github.com/electronstudio/kaylib | -| kaylib | **4.5**| [Kotlin/native](https://kotlinlang.org) | Zlib | https://codeberg.org/Soutaisei/Kaylib | +| KaylibKit | **4.5**| [Kotlin/native](https://kotlinlang.org) | Zlib | https://codeberg.org/Kenta/KaylibKit | | raylib-lua | **4.5** | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | | raylua | 4.0 | [Lua](http://www.lua.org/) | MIT | https://github.com/Rabios/raylua | | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | From 2e7a7877a51ca33487642bd7ed0367b017b54486 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 12 Sep 2023 15:11:16 +0200 Subject: [PATCH 0270/1350] Update webassembly.yml --- .github/workflows/webassembly.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/webassembly.yml b/.github/workflows/webassembly.yml index b50cd8c46..9374cb9eb 100644 --- a/.github/workflows/webassembly.yml +++ b/.github/workflows/webassembly.yml @@ -27,7 +27,7 @@ jobs: uses: actions/checkout@master - name: Setup emsdk - uses: mymindstorm/setup-emsdk@v11 + uses: mymindstorm/setup-emsdk@v12 with: version: 3.1.30 actions-cache-folder: 'emsdk-cache' From 2b1849e57d23f3ba53a8ee5200303694f7a3a63a Mon Sep 17 00:00:00 2001 From: bohonghuang <1281299809@qq.com> Date: Wed, 13 Sep 2023 22:35:37 +0800 Subject: [PATCH 0271/1350] Add claw-raylib to BINDINGS.md (#3310) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index c26fa873e..28e2c542e 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -12,6 +12,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | Raylib-CsLo | 4.2 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo | | cl-raylib | 4.0 | [Common Lisp](https://common-lisp.net/) | MIT | https://github.com/longlene/cl-raylib | | claylib/wrap | **4.5** | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | +| claw-raylib | **auto** | [Common Lisp](https://common-lisp.net/) | Apache-2.0 | https://github.com/bohonghuang/claw-raylib | | chez-raylib | **auto** | [Chez Scheme](https://cisco.github.io/ChezScheme/) | GPLv3 | https://github.com/Yunoinsky/chez-raylib | | raylib-cr | **4.6-dev (5e1a81)** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | | ray-cyber | 4.5 | [Cyber](https://cyberscript.dev) | MIT | https://github.com/fubark/ray-cyber | From 719365f209d046f3fccefce2974b3dcb2e75cc8f Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 13 Sep 2023 11:37:11 -0300 Subject: [PATCH 0272/1350] Add SetWindowMaxSize for desktop and web (#3309) * Add SetWindowMaxSize for desktop and web * Remove SizeInt and respective adjustments --- src/raylib.h | 1 + src/rcore.c | 72 +++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 4f3e35380..94e79a5f7 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -961,6 +961,7 @@ RLAPI void SetWindowTitle(const char *title); // Set title f RLAPI void SetWindowPosition(int x, int y); // Set window position on screen (only PLATFORM_DESKTOP) RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window RLAPI void SetWindowMinSize(int width, int height); // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE) +RLAPI void SetWindowMaxSize(int width, int height); // Set window maximum dimensions (for FLAG_WINDOW_RESIZABLE) RLAPI void SetWindowSize(int width, int height); // Set window dimensions RLAPI void SetWindowOpacity(float opacity); // Set window opacity [0.0f..1.0f] (only PLATFORM_DESKTOP) RLAPI void SetWindowFocused(void); // Set window focused (only PLATFORM_DESKTOP) diff --git a/src/rcore.c b/src/rcore.c index beb611040..771a8380d 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -395,9 +395,11 @@ typedef struct CoreData { Size currentFbo; // Current render width and height (depends on active fbo) Size render; // Framebuffer width and height (render area, including black bars if required) Point renderOffset; // Offset from render area (must be divided by 2) + Size windowMin; // Window minimum width and height (for resizable window) + Size windowMax; // Window maximum width and height (for resizable window) Matrix screenScale; // Matrix to scale screen (framebuffer rendering) - char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) + char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) unsigned int dropFileCount; // Count dropped files strings } Window; @@ -1787,9 +1789,36 @@ void SetWindowMonitor(int monitor) // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMinSize(int width, int height) { + CORE.Window.windowMin.width = width; + CORE.Window.windowMin.height = height; #if defined(PLATFORM_DESKTOP) - const GLFWvidmode *mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); - glfwSetWindowSizeLimits(CORE.Window.handle, width, height, mode->width, mode->height); + int minWidth = (CORE.Window.windowMin.width == 0) ? GLFW_DONT_CARE : CORE.Window.windowMin.width; + int minHeight = (CORE.Window.windowMin.height == 0) ? GLFW_DONT_CARE : CORE.Window.windowMin.height; + int maxWidth = (CORE.Window.windowMax.width == 0) ? GLFW_DONT_CARE : CORE.Window.windowMax.width; + int maxHeight = (CORE.Window.windowMax.height == 0) ? GLFW_DONT_CARE : CORE.Window.windowMax.height; + glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); +#endif +#if defined(PLATFORM_WEB) + // Trigger the resize event once to update the window minimum width and height + if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); +#endif +} + +// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMaxSize(int width, int height) +{ + CORE.Window.windowMax.width = width; + CORE.Window.windowMax.height = height; +#if defined(PLATFORM_DESKTOP) + int minWidth = (CORE.Window.windowMin.width == 0) ? GLFW_DONT_CARE : CORE.Window.windowMin.width; + int minHeight = (CORE.Window.windowMin.height == 0) ? GLFW_DONT_CARE : CORE.Window.windowMin.height; + int maxWidth = (CORE.Window.windowMax.width == 0) ? GLFW_DONT_CARE : CORE.Window.windowMax.width; + int maxHeight = (CORE.Window.windowMax.height == 0) ? GLFW_DONT_CARE : CORE.Window.windowMax.height; + glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); +#endif +#if defined(PLATFORM_WEB) + // Trigger the resize event once to update the window maximum width and height + if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); #endif } @@ -3182,7 +3211,7 @@ bool DirectoryExists(const char *dirPath) int GetFileLength(const char *fileName) { int size = 0; - + // NOTE: On Unix-like systems, it can by used the POSIX system call: stat(), // but depending on the platform that call could not be available //struct stat result = { 0 }; @@ -3195,11 +3224,11 @@ int GetFileLength(const char *fileName) { fseek(file, 0L, SEEK_END); long int fileSize = ftell(file); - + // Check for size overflow (INT_MAX) if (fileSize > 2147483647) TRACELOG(LOG_WARNING, "[%s] File size overflows expected limit, do not use GetFileLength()", fileName); else size = (int)fileSize; - + fclose(file); } @@ -3765,12 +3794,12 @@ bool IsKeyPressed(int key) bool IsKeyPressedRepeat(int key) { bool repeat = false; - + if ((key > 0) && (key < MAX_KEYBOARD_KEYS)) { if (CORE.Input.Keyboard.keyRepeatInFrame[key] == 1) repeat = true; } - + return repeat; } @@ -3778,12 +3807,12 @@ bool IsKeyPressedRepeat(int key) bool IsKeyDown(int key) { bool down = false; - + if ((key > 0) && (key < MAX_KEYBOARD_KEYS)) { if (CORE.Input.Keyboard.currentKeyState[key] == 1) down = true; } - + return down; } @@ -3791,7 +3820,7 @@ bool IsKeyDown(int key) bool IsKeyReleased(int key) { bool released = false; - + if ((key > 0) && (key < MAX_KEYBOARD_KEYS)) { if ((CORE.Input.Keyboard.previousKeyState[key] == 1) && (CORE.Input.Keyboard.currentKeyState[key] == 0)) released = true; @@ -3804,12 +3833,12 @@ bool IsKeyReleased(int key) bool IsKeyUp(int key) { bool up = false; - + if ((key > 0) && (key < MAX_KEYBOARD_KEYS)) { if (CORE.Input.Keyboard.currentKeyState[key] == 0) up = true; } - + return up; } @@ -3887,7 +3916,7 @@ const char *GetGamepadName(int gamepad) if (CORE.Input.Gamepad.ready[gamepad]) name = glfwGetJoystickName(gamepad); #endif #if defined(PLATFORM_DRM) - if (CORE.Input.Gamepad.ready[gamepad]) + if (CORE.Input.Gamepad.ready[gamepad]) { ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); name = CORE.Input.Gamepad.name[gamepad]; @@ -4061,7 +4090,7 @@ int GetMouseY(void) Vector2 GetMousePosition(void) { Vector2 position = { 0 }; - + // TODO: Review touch position on PLATFORM_WEB #if defined(PLATFORM_ANDROID) //|| defined(PLATFORM_WEB) @@ -4220,6 +4249,12 @@ static bool InitGraphicsDevice(int width, int height) CORE.Window.screen.height = height; // User desired height CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + // Set the window minimum and maximum default values to 0 + CORE.Window.windowMin.width = 0; + CORE.Window.windowMin.height = 0; + CORE.Window.windowMax.width = 0; + CORE.Window.windowMax.height = 0; + // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... // ...in top-down or left-right to match display aspect ratio (no weird scaling) @@ -6178,6 +6213,13 @@ static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent * // so the size of the canvas object is explicitly retrieved below int width = GetWindowInnerWidth(); int height = GetWindowInnerHeight(); + + if (width < CORE.Window.windowMin.width) width = CORE.Window.windowMin.width; + else if (width > CORE.Window.windowMax.width && CORE.Window.windowMax.width > 0) width = CORE.Window.windowMax.width; + + if (height < CORE.Window.windowMin.height) height = CORE.Window.windowMin.height; + else if (height > CORE.Window.windowMax.height && CORE.Window.windowMax.height > 0) height = CORE.Window.windowMax.height; + emscripten_set_canvas_element_size("#canvas",width,height); SetupViewport(width, height); // Reset viewport and projection matrix for new size From 528b8799550f8a9d759ef5db8c86d19923a79794 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 13 Sep 2023 17:05:22 +0200 Subject: [PATCH 0273/1350] Update rtextures.c --- src/rtextures.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/rtextures.c b/src/rtextures.c index c86ebac39..a6741ee2a 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -322,9 +322,10 @@ Image LoadImageRaw(const char *fileName, int width, int height, int format, int Image LoadImageSvg(const char *fileNameOrString, int width, int height) { Image image = { 0 }; + +#if defined(SUPPORT_FILEFORMAT_SVG) bool isSvgStringValid = false; -#if defined(SUPPORT_FILEFORMAT_SVG) // Validate fileName or string if (fileNameOrString != NULL) { From 97ef81914ce5fe34b79e263814b65292a31edf42 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 13 Sep 2023 17:05:38 +0200 Subject: [PATCH 0274/1350] Reviewed parameters for consistency --- src/raylib.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 94e79a5f7..07b64aeb6 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -918,8 +918,8 @@ typedef enum { // Callbacks to hook some internal functions // WARNING: These callbacks are intended for advance users typedef void (*TraceLogCallback)(int logLevel, const char *text, va_list args); // Logging: Redirect trace log messages -typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, unsigned int *bytesRead); // FileIO: Load binary data -typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, unsigned int bytesToWrite); // FileIO: Save binary data +typedef unsigned char *(*LoadFileDataCallback)(const char *fileName, int *dataSize); // FileIO: Load binary data +typedef bool (*SaveFileDataCallback)(const char *fileName, void *data, int dataSize); // FileIO: Save binary data typedef char *(*LoadFileTextCallback)(const char *fileName); // FileIO: Load text data typedef bool (*SaveFileTextCallback)(const char *fileName, char *text); // FileIO: Save text data From 8a1779b2ad2d316532fb2ca32ed9939439b492b9 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Thu, 14 Sep 2023 06:57:23 -0300 Subject: [PATCH 0275/1350] Rename windowM* to screenM* (#3312) --- src/rcore.c | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 771a8380d..0d3c6e12a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -395,8 +395,8 @@ typedef struct CoreData { Size currentFbo; // Current render width and height (depends on active fbo) Size render; // Framebuffer width and height (render area, including black bars if required) Point renderOffset; // Offset from render area (must be divided by 2) - Size windowMin; // Window minimum width and height (for resizable window) - Size windowMax; // Window maximum width and height (for resizable window) + Size screenMin; // Screen minimum width and height (for resizable window) + Size screenMax; // Screen maximum width and height (for resizable window) Matrix screenScale; // Matrix to scale screen (framebuffer rendering) char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) @@ -1789,13 +1789,13 @@ void SetWindowMonitor(int monitor) // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMinSize(int width, int height) { - CORE.Window.windowMin.width = width; - CORE.Window.windowMin.height = height; + CORE.Window.screenMin.width = width; + CORE.Window.screenMin.height = height; #if defined(PLATFORM_DESKTOP) - int minWidth = (CORE.Window.windowMin.width == 0) ? GLFW_DONT_CARE : CORE.Window.windowMin.width; - int minHeight = (CORE.Window.windowMin.height == 0) ? GLFW_DONT_CARE : CORE.Window.windowMin.height; - int maxWidth = (CORE.Window.windowMax.width == 0) ? GLFW_DONT_CARE : CORE.Window.windowMax.width; - int maxHeight = (CORE.Window.windowMax.height == 0) ? GLFW_DONT_CARE : CORE.Window.windowMax.height; + int minWidth = (CORE.Window.screenMin.width == 0) ? GLFW_DONT_CARE : CORE.Window.screenMin.width; + int minHeight = (CORE.Window.screenMin.height == 0) ? GLFW_DONT_CARE : CORE.Window.screenMin.height; + int maxWidth = (CORE.Window.screenMax.width == 0) ? GLFW_DONT_CARE : CORE.Window.screenMax.width; + int maxHeight = (CORE.Window.screenMax.height == 0) ? GLFW_DONT_CARE : CORE.Window.screenMax.height; glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); #endif #if defined(PLATFORM_WEB) @@ -1807,13 +1807,13 @@ void SetWindowMinSize(int width, int height) // Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMaxSize(int width, int height) { - CORE.Window.windowMax.width = width; - CORE.Window.windowMax.height = height; + CORE.Window.screenMax.width = width; + CORE.Window.screenMax.height = height; #if defined(PLATFORM_DESKTOP) - int minWidth = (CORE.Window.windowMin.width == 0) ? GLFW_DONT_CARE : CORE.Window.windowMin.width; - int minHeight = (CORE.Window.windowMin.height == 0) ? GLFW_DONT_CARE : CORE.Window.windowMin.height; - int maxWidth = (CORE.Window.windowMax.width == 0) ? GLFW_DONT_CARE : CORE.Window.windowMax.width; - int maxHeight = (CORE.Window.windowMax.height == 0) ? GLFW_DONT_CARE : CORE.Window.windowMax.height; + int minWidth = (CORE.Window.screenMin.width == 0) ? GLFW_DONT_CARE : CORE.Window.screenMin.width; + int minHeight = (CORE.Window.screenMin.height == 0) ? GLFW_DONT_CARE : CORE.Window.screenMin.height; + int maxWidth = (CORE.Window.screenMax.width == 0) ? GLFW_DONT_CARE : CORE.Window.screenMax.width; + int maxHeight = (CORE.Window.screenMax.height == 0) ? GLFW_DONT_CARE : CORE.Window.screenMax.height; glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); #endif #if defined(PLATFORM_WEB) @@ -4249,11 +4249,11 @@ static bool InitGraphicsDevice(int width, int height) CORE.Window.screen.height = height; // User desired height CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - // Set the window minimum and maximum default values to 0 - CORE.Window.windowMin.width = 0; - CORE.Window.windowMin.height = 0; - CORE.Window.windowMax.width = 0; - CORE.Window.windowMax.height = 0; + // Set the screen minimum and maximum default values to 0 + CORE.Window.screenMin.width = 0; + CORE.Window.screenMin.height = 0; + CORE.Window.screenMax.width = 0; + CORE.Window.screenMax.height = 0; // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... // ...in top-down or left-right to match display aspect ratio (no weird scaling) @@ -6214,11 +6214,11 @@ static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent * int width = GetWindowInnerWidth(); int height = GetWindowInnerHeight(); - if (width < CORE.Window.windowMin.width) width = CORE.Window.windowMin.width; - else if (width > CORE.Window.windowMax.width && CORE.Window.windowMax.width > 0) width = CORE.Window.windowMax.width; + if (width < CORE.Window.screenMin.width) width = CORE.Window.screenMin.width; + else if (width > CORE.Window.screenMax.width && CORE.Window.screenMax.width > 0) width = CORE.Window.screenMax.width; - if (height < CORE.Window.windowMin.height) height = CORE.Window.windowMin.height; - else if (height > CORE.Window.windowMax.height && CORE.Window.windowMax.height > 0) height = CORE.Window.windowMax.height; + if (height < CORE.Window.screenMin.height) height = CORE.Window.screenMin.height; + else if (height > CORE.Window.screenMax.height && CORE.Window.screenMax.height > 0) height = CORE.Window.screenMax.height; emscripten_set_canvas_element_size("#canvas",width,height); From 18b22523cc8cbc5c6ef4ab090f3284095e78c7dc Mon Sep 17 00:00:00 2001 From: turborium <45082001+turborium@users.noreply.github.com> Date: Fri, 15 Sep 2023 14:32:50 +0400 Subject: [PATCH 0276/1350] Update BINDINGS.md (#3317) Update TurboRaylib bindings --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 28e2c542e..b02bba8ab 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -50,7 +50,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-odin | **4.5** | [Odin](https://odin-lang.org/) | BSD-3Clause | https://github.com/odin-lang/Odin/tree/master/vendor/raylib | | raylib_odin_bindings | 4.0-dev | [Odin](https://odin-lang.org/) | MIT | https://github.com/Deathbat2190/raylib_odin_bindings | | raylib-ocaml | 4.2 | [OCaml](https://ocaml.org/) | MIT | https://github.com/tjammer/raylib-ocaml | -| TurboRaylib | 4.2 | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT | https://github.com/turborium/TurboRaylib | +| TurboRaylib | **4.5** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT | https://github.com/turborium/TurboRaylib | | Ray4Laz | **4.5** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/GuvaCode/Ray4Laz | | Raylib.4.0.Pascal | 4.0 | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/sysrpl/Raylib.4.0.Pascal | | pyraylib | 3.7 | [Python](https://www.python.org/) | Zlib | https://github.com/Ho011/pyraylib | From 06986f36b3ef80894e207725077cfe396fe839ef Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 15 Sep 2023 17:04:07 +0200 Subject: [PATCH 0277/1350] Update rmodels.c --- src/rmodels.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index 9616f0ceb..1f8568625 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5564,7 +5564,7 @@ static Model LoadVOX(const char *fileName) #if defined(SUPPORT_FILEFORMAT_M3D) // Hook LoadFileData()/UnloadFileData() calls to M3D loaders -unsigned char *m3d_loaderhook(char *fn, unsigned int *len) { return LoadFileData((const char *)fn, len); } +unsigned char *m3d_loaderhook(char *fn, unsigned int *len) { return LoadFileData((const char *)fn, (int *)len); } void m3d_freehook(void *data) { UnloadFileData((unsigned char *)data); } // Load M3D mesh data From f9c6ca468a32d5e9c33bd8cc040d33731356c8ce Mon Sep 17 00:00:00 2001 From: Wilson Silva Date: Sun, 17 Sep 2023 00:56:38 +0700 Subject: [PATCH 0278/1350] Update BINDINGS.md with vaiorabbit/raylib-bindings (#3318) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index b02bba8ab..0eec1dc24 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -114,6 +114,7 @@ These are older raylib bindings that are more than 2 versions old or have not be | raylib-pas | 3.0 | [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language)) | https://github.com/tazdij/raylib-pas | | raylib-pascal | 2.0 | [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language)) | https://github.com/drezgames/raylib-pascal | | Graphics-Raylib | 1.4 | [Perl](https://www.perl.org/) | https://github.com/athreef/Graphics-Raylib | +| raylib-bindings | 4.5 | [Ruby](https://www.ruby-lang.org/en/) | https://github.com/vaiorabbit/raylib-bindings | | raylib-ruby | 2.6 | [Ruby](https://www.ruby-lang.org/en/) | https://github.com/a0/raylib-ruby | | raylib-ruby-ffi | 2.0 | [Ruby](https://www.ruby-lang.org/en/) | https://github.com/D3nX/raylib-ruby-ffi | | raylib-mruby | 2.5-dev | [mruby](https://github.com/mruby/mruby) | https://github.com/lihaochen910/raylib-mruby | From 33cc18ed51ae9279dab4eac9f98bf303feebebe5 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 17 Sep 2023 13:32:44 +0200 Subject: [PATCH 0279/1350] Update BINDINGS.md --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 0eec1dc24..118c0ab9d 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -44,6 +44,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | | Raylib.nelua | **4.5** | [nelua](https://nelua.io/) | Zlib | https://github.com/Its-Kenta/Raylib-Nelua | | NimraylibNow! | 4.2 | [Nim](https://nim-lang.org/) | MIT | https://github.com/greenfork/nimraylib_now | +| raylib-bindings | **4.5** | [Ruby](https://www.ruby-lang.org/en/) | https://github.com/vaiorabbit/raylib-bindings | | raylib-Forever | auto | [Nim](https://nim-lang.org/) | ? | https://github.com/Guevara-chan/Raylib-Forever | | naylib | auto | [Nim](https://nim-lang.org/) | MIT | https://github.com/planetis-m/naylib | | node-raylib | **4.5** | [Node.js](https://nodejs.org/en/) | Zlib | https://github.com/RobLoach/node-raylib | @@ -114,7 +115,6 @@ These are older raylib bindings that are more than 2 versions old or have not be | raylib-pas | 3.0 | [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language)) | https://github.com/tazdij/raylib-pas | | raylib-pascal | 2.0 | [Pascal](https://en.wikipedia.org/wiki/Pascal_(programming_language)) | https://github.com/drezgames/raylib-pascal | | Graphics-Raylib | 1.4 | [Perl](https://www.perl.org/) | https://github.com/athreef/Graphics-Raylib | -| raylib-bindings | 4.5 | [Ruby](https://www.ruby-lang.org/en/) | https://github.com/vaiorabbit/raylib-bindings | | raylib-ruby | 2.6 | [Ruby](https://www.ruby-lang.org/en/) | https://github.com/a0/raylib-ruby | | raylib-ruby-ffi | 2.0 | [Ruby](https://www.ruby-lang.org/en/) | https://github.com/D3nX/raylib-ruby-ffi | | raylib-mruby | 2.5-dev | [mruby](https://github.com/mruby/mruby) | https://github.com/lihaochen910/raylib-mruby | From acf211a5fa1f90cae7a8f8832e3a675720b02f73 Mon Sep 17 00:00:00 2001 From: Brian E <72316548+Brian-ED@users.noreply.github.com> Date: Sun, 17 Sep 2023 19:16:14 +0100 Subject: [PATCH 0280/1350] general syntax fixes (#3319) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Prettified a comment * fixed broken indentation caused by another commit. the commit renamed a bool to int and broke indentation: 233cf3970c9148c4963cfdea20e49e761104f9de * Changed 0.001 and 0.00001 to EPSILON This commit is untested. I don't know what consequences this has. Since the commits that added these numbers were before epsilon was added, I have assumed that epsilon could replace them. * Prettied up indentation in a few places * removed spacing around *, standardizing it. * I may have gotten overboard with indentation * removed a few useless parenthesis * Added fortran-raylib * Fix examples/others/rlgl_standalone.c compilation issue (#3242) * Update BINDINGS.md * Ignore unused return value of GetCodepointNext in GetCodepointCount (#3241) * Ignore unused return value of GetCodepointNext in GetCodepointCount Removes the last warning from non-external libraries when compiling with the default build configuration on x64 Linux. * Remove unnecessary void cast in GetCodepointCount * Fix #3246 * Revert "Fix #3246" This reverts commit e4dcbd518091a5854a517ea4cfc3f7e2d29de1a7. * Fix text_unicode.c example crashing (#3250) * Fix text_unicode.c example crashing * Adjust the text_unicode.c example crashing fix * tweaks * add build.zig options for individual modules (#3254) * Add `IsKeyPressedRepeat` (desktop only) (#3245) Since the key pressed are handle by comparing current vs previous state (ie frame), a special way is needed to handle key repeats. * Reviewed `IsKeyPressedRepeat()` #3248 * Update rcore.c (#3255) * Match CMakeOptions.txt options default values (#3258) * Fix SetClipboardText for web (#3257) * [Image] Validate that ImageDrawRectangleRec is drawing entirely inside the image (#3264) * Add a function to clone a sound and share data with another sound. * rename items based on feedback * PR Feedback, use custom unload for sound alias, not variant of normal sound unloading * sound_multi example * Validate that image rect drawing is inside the image so we don't overflow a buffer * remove files that should not have been added. * remove changes that should not have been * revert * adsfasdfsdfsdf * Add Vector3 Projecting and Rejection to Raymath (#3263) * Update raymath.h * formatting * [Feature] IsKey... safety checks and more (#3256) * [Feature] Add GetKeyRepeat * Update rcore.c * Simpler design, only one repeat per frame * Update config.h * Update rcore.c * Add KEYBOARD_KEYS_MASK * Update config.h * reversions * Update rcore.c * Update rcore.c * change docs * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update raylib.h * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Fix bug where default shaders was not linking. (#3261) * Formating review * Add missing cmake options (#3267) * Fix CMake extraneous -lglfw (#3266) Closes #3265. The problem: LIBS_PRIVATE is a list of library names (used by pkg-config), but the shared library of the same name doesn't always exist. * Fix example/models/models_loading_gltf.c controls (#3268) * Fix example/models/models_loading_m3d.c controls (#3269) * Remove e from secondes (#3270) * Fix example/audio/audio_module_player.c help instructions and small bug (#3272) * Fix example/audio/audio_module_player.c help instructions and small bug * Update example/audio/audio_module_player.png screenshot * Use type name instead of valid specifier long long --> long long int * REVIEWED: `GetFileLength()`, added comment #3262 * Update examples/models/models_loading_gltf.png;m3d.png screenshots (#3273) * Remove a duplicated screenshot and add missing one (#3275) * Add examples/shaders/shaders_lightmap.c to Makefiles (#3276) * Fix examples/others/easings_testbed.c help instructions and small tweak (#3277) * Fix examples/shaders/shaders_texture_outline.c help instructions (#3278) * Fix examples/shapes/shapes_collision_area.c help instructions (#3279) * RENAMED: LoadFont*() parameter names for consistency and coherence * Fix uninitialized thread-locals in stbi #3282 (#3283) * REVIEWED: Added `SetTextLineSpacing()` to multiline examples * REVIEWED: Data size type consistency between functions #3168 * Some tweaks * Use internal default allocators, instead of user-exposed ones * Added rudimentary SVG support. (#2738) * Added rudimentary SVG support. Added 2 functions ImageLoadSvg and ImageLoadSvgWithSize. * Added an example on how to use ImageLoadSvgWithSize and adjusted Makefiles accordingly. * Added actual correct example file. * Reviewed the code to keep the raylib coding conventions in mind. Moved the LoadImageSvg() code into LoadImage() guarded by SUPPORT_FILEFORMAT_SVG. Renamed LoadImageSvgWithSize() to LoadImageSvg(). Added a LoadImageSvgFromString() function to parse the loaded SVG into an actual image. This does the bulk of the work. * Fixed typo. --------- Co-authored-by: Ray * REVIEWED: `LoadImageSvg()` * REVIEWED: `LoadImageSvg()` * Add SUPPORT_FILEFORMAT_SVG to cmake (#3284) * Fix examples/textures/textures_fog_of_war.c help instructions (#3285) * Fix examples/textures/textures_image_rotate.c help instructions (#3286) * Update rtextures.c * Fix #3247 * Update config.h * Fix #3293 * Disable UBSAN in zig builds. (#3292) Zig debug builds automatically enable ubsan. As the fix for #1891 had to be reverted, debug builds using zig will crash like so: ``` Illegal instruction at address 0x3237d2 raylib/src/rlgl.h:3690:91: 0x3237d2 in rlDrawVertexArrayElements (/home/rcorre/src/raylib-zig-template/raylib/src/rcore.c) glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)buffer + offset); ``` This disables UBSAN when using zig to build raylib. * Update README.md (#3290) specially -> especially * Update cmake SUPPORT_FILEFORMAT_SVG default value (#3291) * Mouse offset and scaling must be considered also on web! * Update rcore.c * Update Makefile : clean raygui.c & physac.c (#3296) * Remove PLATFORM_RPI (#3232) * Remove PLATFORM_RPI * remove build artifacts --------- Co-authored-by: MichaelFiber Co-authored-by: Ray * Review to avoid UBSAN complaining #1891 * added raylib-raku to bindings (#3299) * examples: core: adds 2D camera two player split screen (#3298) * Reviewed examples for consistency * Update rtext.c * Some code restructuring for input functions, consistency review * Remove unneeded #if (#3301) Co-authored-by: MichaelFiber * Revert "Disable UBSAN in zig builds. (#3292)" (#3303) This reverts commit a316f9e7fc7f8e06852a40544e57f89018672ee4. Issue #1891 was fixed again, so this is no longer needed. * rtextures: Fix ImageDraw() source clipping when drawing beyond top left (#3306) * REVIEWED: `TextToPascal()` issue when first char is uppercase * Implement FLAG_WINDOW_RESIZABLE for web (#3305) Fixes #3231 * Update BINDINGS.md (#3307) Fix Kaylib binding. Reroute to a new repository. Binding renamed. * Update webassembly.yml * Add claw-raylib to BINDINGS.md (#3310) * Add SetWindowMaxSize for desktop and web (#3309) * Add SetWindowMaxSize for desktop and web * Remove SizeInt and respective adjustments * Update rtextures.c * Reviewed parameters for consistency * Rename windowM* to screenM* (#3312) * Update BINDINGS.md (#3317) Update TurboRaylib bindings * Update rmodels.c * Update BINDINGS.md with vaiorabbit/raylib-bindings (#3318) * fixed spelling mistake * put back parenthesis * reverted major allignment changes * reverted parser output changes * reverted one more indentation change --------- Co-authored-by: Brian-E Co-authored-by: Ray Co-authored-by: ubkp <118854183+ubkp@users.noreply.github.com> Co-authored-by: ashn <60763262+ashn-dot-dev@users.noreply.github.com> Co-authored-by: actondev (Christos) Co-authored-by: vitopigno <103512727+VitusVeit@users.noreply.github.com> Co-authored-by: Asdqwe Co-authored-by: Jeffery Myers Co-authored-by: Ethan Simpson Co-authored-by: Nickolas McDonald <43690021+n77y@users.noreply.github.com> Co-authored-by: Branimir Ričko Co-authored-by: iacore <74560659+iacore@users.noreply.github.com> Co-authored-by: Ethan Conneely Co-authored-by: Johannes Barthelmes <615914+jbarthelmes@users.noreply.github.com> Co-authored-by: bXi Co-authored-by: Ryan Roden-Corrent Co-authored-by: Ikko Eltociear Ashimine Co-authored-by: SuperUserNameMan Co-authored-by: MichaelFiber <42419558+michaelfiber@users.noreply.github.com> Co-authored-by: MichaelFiber Co-authored-by: Dan Vu Co-authored-by: Gabriel dos Santos Sanches Co-authored-by: Rob Loach Co-authored-by: Peter0x44 Co-authored-by: Kenta <106167071+Its-Kenta@users.noreply.github.com> Co-authored-by: bohonghuang <1281299809@qq.com> Co-authored-by: turborium <45082001+turborium@users.noreply.github.com> Co-authored-by: Wilson Silva --- src/raylib.h | 2 +- src/raymath.h | 67 +++++++++++++++++++++++++-------------------------- 2 files changed, 34 insertions(+), 35 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 07b64aeb6..98e5c48a0 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1093,7 +1093,7 @@ RLAPI const char *GetFileNameWithoutExt(const char *filePath); // Get filenam RLAPI const char *GetDirectoryPath(const char *filePath); // Get full path for a given fileName with path (uses static string) RLAPI const char *GetPrevDirectoryPath(const char *dirPath); // Get previous directory path for a given path (uses static string) RLAPI const char *GetWorkingDirectory(void); // Get current working directory (uses static string) -RLAPI const char *GetApplicationDirectory(void); // Get the directory if the running application (uses static string) +RLAPI const char *GetApplicationDirectory(void); // Get the directory of the running application (uses static string) RLAPI bool ChangeDirectory(const char *dir); // Change working directory, return true on success RLAPI bool IsPathFile(const char *path); // Check if a given path is a file or a directory RLAPI FilePathList LoadDirectoryFiles(const char *dirPath); // Load directory filepaths diff --git a/src/raymath.h b/src/raymath.h index 1fab43aad..48bae01a9 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -820,7 +820,7 @@ RMAPI Vector3 Vector3RotateByAxisAngle(Vector3 v, Vector3 axis, float angle) Vector3 result = v; // Vector3Normalize(axis); - float length = sqrtf(axis.x * axis.x + axis.y * axis.y + axis.z * axis.z); + float length = sqrtf(axis.x*axis.x + axis.y*axis.y + axis.z*axis.z); if (length == 0.0f) length = 1.0f; float ilength = 1.0f / length; axis.x *= ilength; @@ -829,19 +829,19 @@ RMAPI Vector3 Vector3RotateByAxisAngle(Vector3 v, Vector3 axis, float angle) angle /= 2.0f; float a = sinf(angle); - float b = axis.x * a; - float c = axis.y * a; - float d = axis.z * a; + float b = axis.x*a; + float c = axis.y*a; + float d = axis.z*a; a = cosf(angle); Vector3 w = { b, c, d }; // Vector3CrossProduct(w, v) - Vector3 wv = { w.y * v.z - w.z * v.y, w.z * v.x - w.x * v.z, w.x * v.y - w.y * v.x }; + Vector3 wv = { w.y*v.z - w.z*v.y, w.z*v.x - w.x*v.z, w.x*v.y - w.y*v.x }; // Vector3CrossProduct(w, wv) - Vector3 wwv = { w.y * wv.z - w.z * wv.y, w.z * wv.x - w.x * wv.z, w.x * wv.y - w.y * wv.x }; + Vector3 wwv = { w.y*wv.z - w.z*wv.y, w.z*wv.x - w.x*wv.z, w.x*wv.y - w.y*wv.x }; - // Vector3Scale(wv, 2 * a) + // Vector3Scale(wv, 2*a) a *= 2; wv.x *= a; wv.y *= a; @@ -1091,18 +1091,17 @@ RMAPI Vector3 Vector3ClampValue(Vector3 v, float min, float max) RMAPI int Vector3Equals(Vector3 p, Vector3 q) { int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && - ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && - ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))); + ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && + ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))); return result; } -// Compute the direction of a refracted ray where v specifies the -// normalized direction of the incoming ray, n specifies the -// normalized normal vector of the interface of two optical media, -// and r specifies the ratio of the refractive index of the medium -// from where the ray comes to the refractive index of the medium -// on the other side of the surface +// Compute the direction of a refracted ray +// v: normalized direction of the incoming ray +// n: normalized normal vector of the interface of two optical media +// r: ratio of the refractive index of the medium from where the ray comes +// to the refractive index of the medium on the other side of the surface RMAPI Vector3 Vector3Refract(Vector3 v, Vector3 n, float r) { Vector3 result = { 0 }; @@ -1862,7 +1861,7 @@ RMAPI Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) float halfTheta = acosf(cosHalfTheta); float sinHalfTheta = sqrtf(1.0f - cosHalfTheta*cosHalfTheta); - if (fabsf(sinHalfTheta) < 0.001f) + if (fabsf(sinHalfTheta) < EPSILON) { result.x = (q1.x*0.5f + q2.x*0.5f); result.y = (q1.y*0.5f + q2.y*0.5f); @@ -1917,9 +1916,9 @@ RMAPI Quaternion QuaternionFromMatrix(Matrix mat) { Quaternion result = { 0 }; - float fourWSquaredMinus1 = mat.m0 + mat.m5 + mat.m10; - float fourXSquaredMinus1 = mat.m0 - mat.m5 - mat.m10; - float fourYSquaredMinus1 = mat.m5 - mat.m0 - mat.m10; + float fourWSquaredMinus1 = mat.m0 + mat.m5 + mat.m10; + float fourXSquaredMinus1 = mat.m0 - mat.m5 - mat.m10; + float fourYSquaredMinus1 = mat.m5 - mat.m0 - mat.m10; float fourZSquaredMinus1 = mat.m10 - mat.m0 - mat.m5; int biggestIndex = 0; @@ -1942,34 +1941,34 @@ RMAPI Quaternion QuaternionFromMatrix(Matrix mat) biggestIndex = 3; } - float biggestVal = sqrtf(fourBiggestSquaredMinus1 + 1.0f) * 0.5f; + float biggestVal = sqrtf(fourBiggestSquaredMinus1 + 1.0f)*0.5f; float mult = 0.25f / biggestVal; switch (biggestIndex) { case 0: result.w = biggestVal; - result.x = (mat.m6 - mat.m9) * mult; - result.y = (mat.m8 - mat.m2) * mult; - result.z = (mat.m1 - mat.m4) * mult; + result.x = (mat.m6 - mat.m9)*mult; + result.y = (mat.m8 - mat.m2)*mult; + result.z = (mat.m1 - mat.m4)*mult; break; case 1: result.x = biggestVal; - result.w = (mat.m6 - mat.m9) * mult; - result.y = (mat.m1 + mat.m4) * mult; - result.z = (mat.m8 + mat.m2) * mult; + result.w = (mat.m6 - mat.m9)*mult; + result.y = (mat.m1 + mat.m4)*mult; + result.z = (mat.m8 + mat.m2)*mult; break; case 2: result.y = biggestVal; - result.w = (mat.m8 - mat.m2) * mult; - result.x = (mat.m1 + mat.m4) * mult; - result.z = (mat.m6 + mat.m9) * mult; + result.w = (mat.m8 - mat.m2)*mult; + result.x = (mat.m1 + mat.m4)*mult; + result.z = (mat.m6 + mat.m9)*mult; break; case 3: result.z = biggestVal; - result.w = (mat.m1 - mat.m4) * mult; - result.x = (mat.m8 + mat.m2) * mult; - result.y = (mat.m6 + mat.m9) * mult; + result.w = (mat.m1 - mat.m4)*mult; + result.x = (mat.m8 + mat.m2)*mult; + result.y = (mat.m6 + mat.m9)*mult; break; } @@ -2075,7 +2074,7 @@ RMAPI void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle float resAngle = 2.0f*acosf(q.w); float den = sqrtf(1.0f - q.w*q.w); - if (den > 0.0001f) + if (den > EPSILON) { resAxis.x = q.x/den; resAxis.y = q.y/den; @@ -2158,7 +2157,7 @@ RMAPI int QuaternionEquals(Quaternion p, Quaternion q) ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && ((fabsf(p.w - q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))))) || - (((fabsf(p.x + q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && + (((fabsf(p.x + q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && ((fabsf(p.y + q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && ((fabsf(p.z + q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && ((fabsf(p.w + q.w)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.w), fabsf(q.w)))))); From 97c4333803234845c0535d4c53985268aeaf4798 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 17 Sep 2023 20:42:45 +0200 Subject: [PATCH 0281/1350] REVIEWED: `UnloadRenderTexture()`, additional check --- src/rtextures.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index a6741ee2a..8dc49fac5 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3793,8 +3793,11 @@ void UnloadRenderTexture(RenderTexture2D target) { if (target.id > 0) { - // Color texture attached to FBO is deleted - rlUnloadTexture(target.texture.id); + if (target.texture.id > 0) + { + // Color texture attached to FBO is deleted + rlUnloadTexture(target.texture.id); + } // NOTE: Depth texture/renderbuffer is automatically // queried and deleted before deleting framebuffer From 421ae5bbd8da0e52b32b943232def3c291145338 Mon Sep 17 00:00:00 2001 From: Tobias Mock Date: Sun, 17 Sep 2023 22:52:34 +0200 Subject: [PATCH 0282/1350] Update raylib-ocaml bindings to 4.5 version (#3322) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 118c0ab9d..9fe86919d 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -50,7 +50,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | node-raylib | **4.5** | [Node.js](https://nodejs.org/en/) | Zlib | https://github.com/RobLoach/node-raylib | | raylib-odin | **4.5** | [Odin](https://odin-lang.org/) | BSD-3Clause | https://github.com/odin-lang/Odin/tree/master/vendor/raylib | | raylib_odin_bindings | 4.0-dev | [Odin](https://odin-lang.org/) | MIT | https://github.com/Deathbat2190/raylib_odin_bindings | -| raylib-ocaml | 4.2 | [OCaml](https://ocaml.org/) | MIT | https://github.com/tjammer/raylib-ocaml | +| raylib-ocaml | **4.5** | [OCaml](https://ocaml.org/) | MIT | https://github.com/tjammer/raylib-ocaml | | TurboRaylib | **4.5** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | MIT | https://github.com/turborium/TurboRaylib | | Ray4Laz | **4.5** | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/GuvaCode/Ray4Laz | | Raylib.4.0.Pascal | 4.0 | [Free Pascal](https://en.wikipedia.org/wiki/Free_Pascal)| Zlib | https://github.com/sysrpl/Raylib.4.0.Pascal | From b9acbbbc66abfa7ffb26ece2f3c860743693f6a6 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 18 Sep 2023 11:50:37 +0200 Subject: [PATCH 0283/1350] Fix #3323 --- src/rcore.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 0d3c6e12a..cba3bbc12 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -6966,10 +6966,10 @@ static void *EventThread(void *arg) // Update touch point count CORE.Input.Touch.pointCount = 0; - if (CORE.Input.Touch.position[0].x >= 0) CORE.Input.Touch.pointCount++; - if (CORE.Input.Touch.position[1].x >= 0) CORE.Input.Touch.pointCount++; - if (CORE.Input.Touch.position[2].x >= 0) CORE.Input.Touch.pointCount++; - if (CORE.Input.Touch.position[3].x >= 0) CORE.Input.Touch.pointCount++; + for (int i = 0; i < MAX_TOUCH_POINTS; i++) + { + if (CORE.Input.Touch.position[i].x >= 0) CORE.Input.Touch.pointCount++; + } #if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_DRM if (gestureUpdate) From 5c9cc3f9f77d64a9fe9cc0d374da8ed4741fcaa5 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 18 Sep 2023 12:07:40 +0200 Subject: [PATCH 0284/1350] Reviewed #3323 --- src/rcore.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index cba3bbc12..0cfaed70f 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -6979,15 +6979,11 @@ static void *EventThread(void *arg) gestureEvent.touchAction = touchAction; gestureEvent.pointCount = CORE.Input.Touch.pointCount; - gestureEvent.pointId[0] = 0; - gestureEvent.pointId[1] = 1; - gestureEvent.pointId[2] = 2; - gestureEvent.pointId[3] = 3; - - gestureEvent.position[0] = CORE.Input.Touch.position[0]; - gestureEvent.position[1] = CORE.Input.Touch.position[1]; - gestureEvent.position[2] = CORE.Input.Touch.position[2]; - gestureEvent.position[3] = CORE.Input.Touch.position[3]; + for (int i = 0; i < MAX_TOUCH_POINTS; i++) + { + gestureEvent.pointId[i] = i; + gestureEvent.position[i] = CORE.Input.Touch.position[i]; + } ProcessGestureEvent(gestureEvent); } From 4d2906b0a5766823cbd9808431656140dae881b9 Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Mon, 18 Sep 2023 18:50:22 +0200 Subject: [PATCH 0285/1350] Move mutex initialization before `ma_device_start()` (#3325) --- src/raudio.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 4627cdf5c..61e00a386 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -474,6 +474,16 @@ void InitAudioDevice(void) return; } + // Mixing happens on a separate thread which means we need to synchronize. I'm using a mutex here to make things simple, but may + // want to look at something a bit smarter later on to keep everything real-time, if that's necessary. + if (ma_mutex_init(&AUDIO.System.lock) != MA_SUCCESS) + { + TRACELOG(LOG_WARNING, "AUDIO: Failed to create mutex for mixing"); + ma_device_uninit(&AUDIO.System.device); + ma_context_uninit(&AUDIO.System.context); + return; + } + // Keep the device running the whole time. May want to consider doing something a bit smarter and only have the device running // while there's at least one sound being played. result = ma_device_start(&AUDIO.System.device); @@ -485,16 +495,6 @@ void InitAudioDevice(void) return; } - // Mixing happens on a separate thread which means we need to synchronize. I'm using a mutex here to make things simple, but may - // want to look at something a bit smarter later on to keep everything real-time, if that's necessary. - if (ma_mutex_init(&AUDIO.System.lock) != MA_SUCCESS) - { - TRACELOG(LOG_WARNING, "AUDIO: Failed to create mutex for mixing"); - ma_device_uninit(&AUDIO.System.device); - ma_context_uninit(&AUDIO.System.context); - return; - } - TRACELOG(LOG_INFO, "AUDIO: Device initialized successfully"); TRACELOG(LOG_INFO, " > Backend: miniaudio / %s", ma_get_backend_name(AUDIO.System.context.backend)); TRACELOG(LOG_INFO, " > Format: %s -> %s", ma_get_format_name(AUDIO.System.device.playback.format), ma_get_format_name(AUDIO.System.device.playback.internalFormat)); From eb461512a7191896acda33330e643b0dd60365b3 Mon Sep 17 00:00:00 2001 From: Christopher Odom Date: Mon, 18 Sep 2023 13:39:12 -0400 Subject: [PATCH 0286/1350] Added UBSAN complaint fix to rLoadTexture #1891 (#3321) --- src/rlgl.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 6dee60f95..bef4fdc2d 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3009,11 +3009,15 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, TRACELOGD("TEXTURE: Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset); + // NOTE: Added pointer math separately from function to avoid UBSAN complaining + unsigned char *dataPtr = (unsigned char*)data; + if (mipOffset > 0) dataPtr = (unsigned char*)data + mipOffset; + if (glInternalFormat != -1) { - if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, glFormat, glType, (unsigned char *)data + mipOffset); + if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, glFormat, glType, dataPtr); #if !defined(GRAPHICS_API_OPENGL_11) - else glCompressedTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, mipSize, (unsigned char *)data + mipOffset); + else glCompressedTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, mipSize, dataPtr); #endif #if defined(GRAPHICS_API_OPENGL_33) From b3359276654c410033c8d194c0188d2257029f1d Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 18 Sep 2023 19:43:10 +0200 Subject: [PATCH 0287/1350] Reviewed PR #3321 --- src/rlgl.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index bef4fdc2d..fe69afd6f 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2997,7 +2997,10 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipWidth = width; int mipHeight = height; - int mipOffset = 0; // Mipmap data offset + int mipOffset = 0; // Mipmap data offset, only used for tracelog + + // NOTE: Added pointer math separately from function to avoid UBSAN complaining + unsigned char *dataPtr = (unsigned char *)data; // Load the different mipmap levels for (int i = 0; i < mipmapCount; i++) @@ -3009,10 +3012,6 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, TRACELOGD("TEXTURE: Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset); - // NOTE: Added pointer math separately from function to avoid UBSAN complaining - unsigned char *dataPtr = (unsigned char*)data; - if (mipOffset > 0) dataPtr = (unsigned char*)data + mipOffset; - if (glInternalFormat != -1) { if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, glFormat, glType, dataPtr); @@ -3040,7 +3039,8 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, mipWidth /= 2; mipHeight /= 2; - mipOffset += mipSize; + mipOffset += mipSize; // Increment offset position to next mipmap + dataPtr += mipSize; // Increment data pointer to next mipmap // Security check for NPOT textures if (mipWidth < 1) mipWidth = 1; From c9020ece5da5dcb4760b32080fdeddbba96d885c Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 19 Sep 2023 18:52:40 +0200 Subject: [PATCH 0288/1350] Update linux.yml --- .github/workflows/linux.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 3c1ef62b8..d740250e0 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -22,7 +22,7 @@ jobs: build: permissions: contents: write # for actions/upload-release-asset to upload release asset - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 strategy: fail-fast: false max-parallel: 1 From 36e99860ee0c09b87d110f3def6997930d80232e Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 20 Sep 2023 21:22:04 +0200 Subject: [PATCH 0289/1350] Added note about WebGL warning --- src/rlgl.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index fe69afd6f..9ed484408 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3403,7 +3403,7 @@ void *rlReadTexturePixels(unsigned int id, int width, int height, int format) // Attach our texture to FBO glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, id, 0); - + // We read data as RGBA because FBO texture is configured as RGBA, despite binding another texture format pixels = (unsigned char *)RL_MALLOC(rlGetPixelDataSize(width, height, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); @@ -3541,11 +3541,14 @@ bool rlFramebufferComplete(unsigned int id) void rlUnloadFramebuffer(unsigned int id) { #if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES2)) && defined(RLGL_RENDER_TEXTURES_HINT) - // Query depth attachment to automatically delete texture/renderbuffer int depthType = 0, depthId = 0; glBindFramebuffer(GL_FRAMEBUFFER, id); // Bind framebuffer to query depth texture type glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE, &depthType); + + // TODO: Review warning retrieving object name in WebGL + // WARNING: WebGL: INVALID_ENUM: getFramebufferAttachmentParameter: invalid parameter name + // https://registry.khronos.org/webgl/specs/latest/1.0/ glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, &depthId); unsigned int depthIdU = (unsigned int)depthId; From a2b3b1ebe43cdf394b49f901cbaedb2c87959168 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 20 Sep 2023 21:23:43 +0200 Subject: [PATCH 0290/1350] REVIEWED: `CompressData()`, possible stack overflow `struct sdefl` is almost 1MB, in some platforms it could generated a stack overflow, for example WebAssembly, where by default stack is only 65KB! --- src/rcore.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 0cfaed70f..26ff79d9a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3589,11 +3589,13 @@ unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDa #if defined(SUPPORT_COMPRESSION_API) // Compress data and generate a valid DEFLATE stream - struct sdefl sdefl = { 0 }; - int bounds = sdefl_bound(dataSize); + struct sdefl *sdefl = RL_CALLOC(1, sizeof(struct sdefl)); // WARNING: Possible stack overflow, struct sdefl is almost 1MB + int bounds = dataSize*2;//sdefl_bound(dataSize); compData = (unsigned char *)RL_CALLOC(bounds, 1); - *compDataSize = sdeflate(&sdefl, compData, data, dataSize, COMPRESSION_QUALITY_DEFLATE); // Compression level 8, same as stbiw - + + *compDataSize = sdeflate(sdefl, compData, data, dataSize, COMPRESSION_QUALITY_DEFLATE); // Compression level 8, same as stbiw + RL_FREE(sdefl); + TRACELOG(LOG_INFO, "SYSTEM: Compress data: Original size: %i -> Comp. size: %i", dataSize, *compDataSize); #endif From 577a8de7c059504178a6e5a749d52f543ba199fc Mon Sep 17 00:00:00 2001 From: Brian E <72316548+Brian-ED@users.noreply.github.com> Date: Thu, 21 Sep 2023 22:48:48 +0100 Subject: [PATCH 0291/1350] [raymath] Added macros for EPSILON on each function it's used in (#3330) * Added macros for EPSILON This is so the functions can be easily copied and used. * used `#if !defined()` instead of `#ifndef` --------- Co-authored-by: Brian-E --- src/raymath.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/raymath.h b/src/raymath.h index 48bae01a9..db04c51ee 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -214,6 +214,10 @@ RMAPI float Wrap(float value, float min, float max) // Check whether two given floats are almost equal RMAPI int FloatEquals(float x, float y) { +#if !defined(EPSILON) + #define EPSILON 0.000001f +#endif + int result = (fabsf(x - y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(x), fabsf(y)))); return result; @@ -506,6 +510,10 @@ RMAPI Vector2 Vector2ClampValue(Vector2 v, float min, float max) // Check whether two given vectors are almost equal RMAPI int Vector2Equals(Vector2 p, Vector2 q) { +#if !defined(EPSILON) + #define EPSILON 0.000001f +#endif + int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))); @@ -1090,6 +1098,10 @@ RMAPI Vector3 Vector3ClampValue(Vector3 v, float min, float max) // Check whether two given vectors are almost equal RMAPI int Vector3Equals(Vector3 p, Vector3 q) { +#if !defined(EPSILON) + #define EPSILON 0.000001f +#endif + int result = ((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))); @@ -1846,6 +1858,10 @@ RMAPI Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount) { Quaternion result = { 0 }; +#if !defined(EPSILON) + #define EPSILON 0.000001f +#endif + float cosHalfTheta = q1.x*q2.x + q1.y*q2.y + q1.z*q2.z + q1.w*q2.w; if (cosHalfTheta < 0) @@ -2153,6 +2169,10 @@ RMAPI Quaternion QuaternionTransform(Quaternion q, Matrix mat) // Check whether two given quaternions are almost equal RMAPI int QuaternionEquals(Quaternion p, Quaternion q) { +#if !defined(EPSILON) + #define EPSILON 0.000001f +#endif + int result = (((fabsf(p.x - q.x)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.x), fabsf(q.x))))) && ((fabsf(p.y - q.y)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.y), fabsf(q.y))))) && ((fabsf(p.z - q.z)) <= (EPSILON*fmaxf(1.0f, fmaxf(fabsf(p.z), fabsf(q.z))))) && From 477f5e5436e57f4dc4cadd4b8a2c9d78ff3d0c0e Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 21 Sep 2023 23:54:59 +0200 Subject: [PATCH 0292/1350] Update miniaudio v0.11.16 --> v0.11.18 --- src/external/miniaudio.h | 22009 ++++++++++++++++++------------------- 1 file changed, 10572 insertions(+), 11437 deletions(-) diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h index 35eaafdcf..181f45289 100644 --- a/src/external/miniaudio.h +++ b/src/external/miniaudio.h @@ -1,6 +1,6 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.16 - 2023-05-15 +miniaudio - v0.11.18 - 2023-08-07 David Reid - mackron@gmail.com @@ -538,6 +538,20 @@ you'll need to disable run-time linking with `MA_NO_RUNTIME_LINKING` and link wi The Emscripten build emits Web Audio JavaScript directly and should compile cleanly out of the box. You cannot use `-std=c*` compiler flags, nor `-ansi`. +You can enable the use of AudioWorkets by defining `MA_ENABLE_AUDIO_WORKLETS` and then compiling +with the following options: + + -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY + +An example for compiling with AudioWorklet support might look like this: + + emcc program.c -o bin/program.html -DMA_ENABLE_AUDIO_WORKLETS -sAUDIO_WORKLET=1 -sWASM_WORKERS=1 -sASYNCIFY + +To run locally, you'll need to use emrun: + + emrun bin/program.html + + 2.7. Build Options ------------------ @@ -2460,37 +2474,18 @@ used. The same general process applies to detachment. See `ma_node_attach_output 8. Decoding =========== The `ma_decoder` API is used for reading audio files. Decoders are completely decoupled from -devices and can be used independently. The following formats are supported: +devices and can be used independently. Built-in support is included for the following formats: - +---------+------------------+----------+ - | Format | Decoding Backend | Built-In | - +---------+------------------+----------+ - | WAV | dr_wav | Yes | - | MP3 | dr_mp3 | Yes | - | FLAC | dr_flac | Yes | - | Vorbis | stb_vorbis | No | - +---------+------------------+----------+ + +---------+ + | Format | + +---------+ + | WAV | + | MP3 | + | FLAC | + +---------+ -Vorbis is supported via stb_vorbis which can be enabled by including the header section before the -implementation of miniaudio, like the following: - - ```c - #define STB_VORBIS_HEADER_ONLY - #include "extras/stb_vorbis.c" // Enables Vorbis decoding. - - #define MINIAUDIO_IMPLEMENTATION - #include "miniaudio.h" - - // The stb_vorbis implementation must come after the implementation of miniaudio. - #undef STB_VORBIS_HEADER_ONLY - #include "extras/stb_vorbis.c" - ``` - -A copy of stb_vorbis is included in the "extras" folder in the miniaudio repository (https://github.com/mackron/miniaudio). - -Built-in decoders are amalgamated into the implementation section of miniaudio. You can disable the -built-in decoders by specifying one or more of the following options before the miniaudio -implementation: +You can disable the built-in decoders by specifying one or more of the following options before the +miniaudio implementation: ```c #define MA_NO_WAV @@ -2498,8 +2493,8 @@ implementation: #define MA_NO_FLAC ``` -Disabling built-in decoding libraries is useful if you use these libraries independently of the -`ma_decoder` API. +miniaudio supports the ability to plug in custom decoders. See the section below for details on how +to use custom decoders. A decoder can be initialized from a file with `ma_decoder_init_file()`, a block of memory with `ma_decoder_init_memory()`, or from data delivered via callbacks with `ma_decoder_init()`. Here is @@ -2640,8 +2635,7 @@ opportunity to clean up and internal data. 9. Encoding =========== -The `ma_encoding` API is used for writing audio files. The only supported output format is WAV -which is achieved via dr_wav which is amalgamated into the implementation section of miniaudio. +The `ma_encoding` API is used for writing audio files. The only supported output format is WAV. This can be disabled by specifying the following option before the implementation of miniaudio: ```c @@ -3722,7 +3716,7 @@ extern "C" { #define MA_VERSION_MAJOR 0 #define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 16 +#define MA_VERSION_REVISION 18 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #if defined(_MSC_VER) && !defined(__clang__) @@ -3914,6 +3908,13 @@ typedef ma_uint16 wchar_t; #ifdef _MSC_VER #define MA_INLINE __forceinline + + /* noinline was introduced in Visual Studio 2005. */ + #if _MSC_VER >= 1400 + #define MA_NO_INLINE __declspec(noinline) + #else + #define MA_NO_INLINE + #endif #elif defined(__GNUC__) /* I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when @@ -3930,45 +3931,59 @@ typedef ma_uint16 wchar_t; #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) #define MA_INLINE MA_GNUC_INLINE_HINT __attribute__((always_inline)) + #define MA_NO_INLINE __attribute__((noinline)) #else #define MA_INLINE MA_GNUC_INLINE_HINT + #define MA_NO_INLINE __attribute__((noinline)) #endif #elif defined(__WATCOMC__) #define MA_INLINE __inline + #define MA_NO_INLINE #else #define MA_INLINE + #define MA_NO_INLINE +#endif + +/* MA_DLL is not officially supported. You're on your own if you want to use this. */ +#if defined(MA_DLL) + #if defined(_WIN32) + #define MA_DLL_IMPORT __declspec(dllimport) + #define MA_DLL_EXPORT __declspec(dllexport) + #define MA_DLL_PRIVATE static + #else + #if defined(__GNUC__) && __GNUC__ >= 4 + #define MA_DLL_IMPORT __attribute__((visibility("default"))) + #define MA_DLL_EXPORT __attribute__((visibility("default"))) + #define MA_DLL_PRIVATE __attribute__((visibility("hidden"))) + #else + #define MA_DLL_IMPORT + #define MA_DLL_EXPORT + #define MA_DLL_PRIVATE static + #endif + #endif #endif #if !defined(MA_API) #if defined(MA_DLL) - #if defined(_WIN32) - #define MA_DLL_IMPORT __declspec(dllimport) - #define MA_DLL_EXPORT __declspec(dllexport) - #define MA_DLL_PRIVATE static - #else - #if defined(__GNUC__) && __GNUC__ >= 4 - #define MA_DLL_IMPORT __attribute__((visibility("default"))) - #define MA_DLL_EXPORT __attribute__((visibility("default"))) - #define MA_DLL_PRIVATE __attribute__((visibility("hidden"))) - #else - #define MA_DLL_IMPORT - #define MA_DLL_EXPORT - #define MA_DLL_PRIVATE static - #endif - #endif - #if defined(MINIAUDIO_IMPLEMENTATION) || defined(MA_IMPLEMENTATION) #define MA_API MA_DLL_EXPORT #else #define MA_API MA_DLL_IMPORT #endif - #define MA_PRIVATE MA_DLL_PRIVATE #else #define MA_API extern + #endif +#endif + +#if !defined(MA_STATIC) + #if defined(MA_DLL) + #define MA_PRIVATE MA_DLL_PRIVATE + #else #define MA_PRIVATE static #endif #endif + /* SIMD alignment in bytes. Currently set to 32 bytes in preparation for future AVX optimizations. */ #define MA_SIMD_ALIGNMENT 32 @@ -4166,28 +4181,31 @@ typedef enum MA_CANCELLED = -51, MA_MEMORY_ALREADY_MAPPED = -52, + /* General non-standard errors. */ + MA_CRC_MISMATCH = -100, + /* General miniaudio-specific errors. */ - MA_FORMAT_NOT_SUPPORTED = -100, - MA_DEVICE_TYPE_NOT_SUPPORTED = -101, - MA_SHARE_MODE_NOT_SUPPORTED = -102, - MA_NO_BACKEND = -103, - MA_NO_DEVICE = -104, - MA_API_NOT_FOUND = -105, - MA_INVALID_DEVICE_CONFIG = -106, - MA_LOOP = -107, - MA_BACKEND_NOT_ENABLED = -108, + MA_FORMAT_NOT_SUPPORTED = -200, + MA_DEVICE_TYPE_NOT_SUPPORTED = -201, + MA_SHARE_MODE_NOT_SUPPORTED = -202, + MA_NO_BACKEND = -203, + MA_NO_DEVICE = -204, + MA_API_NOT_FOUND = -205, + MA_INVALID_DEVICE_CONFIG = -206, + MA_LOOP = -207, + MA_BACKEND_NOT_ENABLED = -208, /* State errors. */ - MA_DEVICE_NOT_INITIALIZED = -200, - MA_DEVICE_ALREADY_INITIALIZED = -201, - MA_DEVICE_NOT_STARTED = -202, - MA_DEVICE_NOT_STOPPED = -203, + MA_DEVICE_NOT_INITIALIZED = -300, + MA_DEVICE_ALREADY_INITIALIZED = -301, + MA_DEVICE_NOT_STARTED = -302, + MA_DEVICE_NOT_STOPPED = -303, /* Operation errors. */ - MA_FAILED_TO_INIT_BACKEND = -300, - MA_FAILED_TO_OPEN_BACKEND_DEVICE = -301, - MA_FAILED_TO_START_BACKEND_DEVICE = -302, - MA_FAILED_TO_STOP_BACKEND_DEVICE = -303 + MA_FAILED_TO_INIT_BACKEND = -400, + MA_FAILED_TO_OPEN_BACKEND_DEVICE = -401, + MA_FAILED_TO_START_BACKEND_DEVICE = -402, + MA_FAILED_TO_STOP_BACKEND_DEVICE = -403 } ma_result; @@ -5039,13 +5057,14 @@ typedef struct float volumeBeg; /* If volumeBeg and volumeEnd is equal to 1, no fading happens (ma_fader_process_pcm_frames() will run as a passthrough). */ float volumeEnd; ma_uint64 lengthInFrames; /* The total length of the fade. */ - ma_uint64 cursorInFrames; /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). */ + ma_int64 cursorInFrames; /* The current time in frames. Incremented by ma_fader_process_pcm_frames(). Signed because it'll be offset by startOffsetInFrames in set_fade_ex(). */ } ma_fader; MA_API ma_result ma_fader_init(const ma_fader_config* pConfig, ma_fader* pFader); MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, const void* pFramesIn, ma_uint64 frameCount); MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate); MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames); +MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames); MA_API float ma_fader_get_current_volume(const ma_fader* pFader); @@ -7090,7 +7109,7 @@ struct ma_device_config /* -The callback for handling device enumeration. This is fired from `ma_context_enumerated_devices()`. +The callback for handling device enumeration. This is fired from `ma_context_enumerate_devices()`. Parameters @@ -7664,6 +7683,8 @@ struct ma_context ma_proc RegOpenKeyExA; ma_proc RegCloseKey; ma_proc RegQueryValueExA; + + /*HRESULT*/ long CoInitializeResult; } win32; #endif #ifdef MA_POSIX @@ -7943,21 +7964,12 @@ struct ma_device struct { /* AudioWorklets path. */ - /* EMSCRIPTEN_WEBAUDIO_T */ int audioContextPlayback; - /* EMSCRIPTEN_WEBAUDIO_T */ int audioContextCapture; - /* EMSCRIPTEN_AUDIO_WORKLET_NODE_T */ int workletNodePlayback; - /* EMSCRIPTEN_AUDIO_WORKLET_NODE_T */ int workletNodeCapture; - size_t intermediaryBufferSizeInFramesPlayback; - size_t intermediaryBufferSizeInFramesCapture; - float* pIntermediaryBufferPlayback; - float* pIntermediaryBufferCapture; - void* pStackBufferPlayback; - void* pStackBufferCapture; - ma_bool32 isInitialized; - - /* ScriptProcessorNode path. */ - int indexPlayback; /* We use a factory on the JavaScript side to manage devices and use an index for JS/C interop. */ - int indexCapture; + /* EMSCRIPTEN_WEBAUDIO_T */ int audioContext; + /* EMSCRIPTEN_WEBAUDIO_T */ int audioWorklet; + float* pIntermediaryBuffer; + void* pStackBuffer; + ma_result initResult; /* Set to MA_BUSY while initialization is in progress. */ + int deviceIndex; /* We store the device in a list on the JavaScript side. This is used to map our C object to the JS object. */ } webaudio; #endif #ifdef MA_SUPPORT_NULL @@ -10045,7 +10057,7 @@ struct ma_encoder ma_encoder_uninit_proc onUninit; ma_encoder_write_pcm_frames_proc onWritePCMFrames; void* pUserData; - void* pInternalEncoder; /* <-- The drwav/drflac/stb_vorbis/etc. objects. */ + void* pInternalEncoder; union { struct @@ -10110,6 +10122,33 @@ MA_API ma_result ma_waveform_set_frequency(ma_waveform* pWaveform, double freque MA_API ma_result ma_waveform_set_type(ma_waveform* pWaveform, ma_waveform_type type); MA_API ma_result ma_waveform_set_sample_rate(ma_waveform* pWaveform, ma_uint32 sampleRate); +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + double dutyCycle; + double amplitude; + double frequency; +} ma_pulsewave_config; + +MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency); + +typedef struct +{ + ma_waveform waveform; + ma_pulsewave_config config; +} ma_pulsewave; + +MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform); +MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform); +MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); +MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex); +MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude); +MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency); +MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate); +MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle); + typedef enum { ma_noise_type_white, @@ -11035,6 +11074,15 @@ typedef struct MA_ATOMIC(4, ma_bool32) isSpatializationDisabled; /* Set to false by default. When set to false, will not have spatialisation applied. */ MA_ATOMIC(4, ma_uint32) pinnedListenerIndex; /* The index of the listener this node should always use for spatialization. If set to MA_LISTENER_INDEX_CLOSEST the engine will use the closest listener. */ + /* When setting a fade, it's not done immediately in ma_sound_set_fade(). It's deferred to the audio thread which means we need to store the settings here. */ + struct + { + ma_atomic_float volumeBeg; + ma_atomic_float volumeEnd; + ma_atomic_uint64 fadeLengthInFrames; /* <-- Defaults to (~(ma_uint64)0) which is used to indicate that no fade should be applied. */ + ma_atomic_uint64 absoluteGlobalTimeInFrames; /* <-- The time to start the fade. */ + } fadeSettings; + /* Memory management. */ ma_bool8 _ownsHeap; void* _pHeap; @@ -11115,6 +11163,8 @@ typedef ma_sound ma_sound_group; MA_API ma_sound_group_config ma_sound_group_config_init(void); /* Deprecated. Will be removed in version 0.12. Use ma_sound_config_2() instead. */ MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine); /* Will be renamed to ma_sound_config_init() in version 0.12. */ +typedef void (* ma_engine_process_proc)(void* pUserData, float* pFramesOut, ma_uint64 frameCount); + typedef struct { #if !defined(MA_NO_RESOURCE_MANAGER) @@ -11124,6 +11174,7 @@ typedef struct ma_context* pContext; ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */ ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */ + ma_device_data_proc dataCallback; /* Can be null. Can be used to provide a custom device data callback. */ ma_device_notification_proc notificationCallback; #endif ma_log* pLog; /* When set to NULL, will use the context's log. */ @@ -11140,6 +11191,8 @@ typedef struct ma_bool32 noDevice; /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */ ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ ma_vfs* pResourceManagerVFS; /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */ + ma_engine_process_proc onProcess; /* Fired at the end of each call to ma_engine_read_pcm_frames(). For engine's that manage their own internal device (the default configuration), this will be fired from the audio thread, and you do not need to call ma_engine_read_pcm_frames() manually in order to trigger this. */ + void* pProcessUserData; /* User data that's passed into onProcess. */ } ma_engine_config; MA_API ma_engine_config ma_engine_config_init(void); @@ -11167,6 +11220,8 @@ struct ma_engine ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */ ma_uint32 defaultVolumeSmoothTimeInPCMFrames; ma_mono_expansion_mode monoExpansionMode; + ma_engine_process_proc onProcess; + void* pProcessUserData; }; MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEngine); @@ -11191,7 +11246,9 @@ MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine); MA_API ma_result ma_engine_start(ma_engine* pEngine); MA_API ma_result ma_engine_stop(ma_engine* pEngine); MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume); +MA_API float ma_engine_get_volume(ma_engine* pEngine); MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB); +MA_API float ma_engine_get_gain_db(ma_engine* pEngine); MA_API ma_uint32 ma_engine_get_listener_count(const ma_engine* pEngine); MA_API ma_uint32 ma_engine_find_closest_listener(const ma_engine* pEngine, float absolutePosX, float absolutePosY, float absolutePosZ); @@ -11225,6 +11282,8 @@ MA_API ma_engine* ma_sound_get_engine(const ma_sound* pSound); MA_API ma_data_source* ma_sound_get_data_source(const ma_sound* pSound); MA_API ma_result ma_sound_start(ma_sound* pSound); MA_API ma_result ma_sound_stop(ma_sound* pSound); +MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */ +MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInFrames); /* Will overwrite any scheduled stop and fade. */ MA_API void ma_sound_set_volume(ma_sound* pSound, float volume); MA_API float ma_sound_get_volume(const ma_sound* pSound); MA_API void ma_sound_set_pan(ma_sound* pSound, float pan); @@ -11267,13 +11326,18 @@ MA_API void ma_sound_set_directional_attenuation_factor(ma_sound* pSound, float MA_API float ma_sound_get_directional_attenuation_factor(const ma_sound* pSound); MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames); MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds); +MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames); +MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds); MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound); MA_API void ma_sound_set_start_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); MA_API void ma_sound_set_start_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInFrames); MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds); +MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames); +MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds); MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound); MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound); +MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound); MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping); MA_API ma_bool32 ma_sound_is_looping(const ma_sound* pSound); MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound); @@ -11419,6 +11483,7 @@ IMPLEMENTATION #endif +/* Architecture Detection */ #if !defined(MA_64BIT) && !defined(MA_32BIT) #ifdef _WIN32 #ifdef _WIN64 @@ -11448,12 +11513,18 @@ IMPLEMENTATION #endif #endif -/* Architecture Detection */ +#if defined(__arm__) || defined(_M_ARM) +#define MA_ARM32 +#endif +#if defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) +#define MA_ARM64 +#endif + #if defined(__x86_64__) || defined(_M_X64) #define MA_X64 #elif defined(__i386) || defined(_M_IX86) #define MA_X86 -#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) +#elif defined(MA_ARM32) || defined(MA_ARM64) #define MA_ARM #endif @@ -11547,7 +11618,7 @@ IMPLEMENTATION What's basically happening is that we're saving and restoring the ebx register manually. */ - #if defined(DRFLAC_X86) && defined(__PIC__) + #if defined(MA_X86) && defined(__PIC__) __asm__ __volatile__ ( "xchg{l} {%%}ebx, %k1;" "cpuid;" @@ -12296,8 +12367,11 @@ Return Values: 34: ERANGE Not using symbolic constants for errors because I want to avoid #including errno.h + +These are marked as no-inline because of some bad code generation by Clang. None of these functions +are used in any performance-critical code within miniaudio. */ -MA_API int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) +MA_API MA_NO_INLINE int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) { size_t i; @@ -12325,7 +12399,7 @@ MA_API int ma_strcpy_s(char* dst, size_t dstSizeInBytes, const char* src) return 34; } -MA_API int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src) +MA_API MA_NO_INLINE int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src) { size_t i; @@ -12354,7 +12428,7 @@ MA_API int ma_wcscpy_s(wchar_t* dst, size_t dstCap, const wchar_t* src) } -MA_API int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) +MA_API MA_NO_INLINE int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) { size_t maxcount; size_t i; @@ -12388,7 +12462,7 @@ MA_API int ma_strncpy_s(char* dst, size_t dstSizeInBytes, const char* src, size_ return 34; } -MA_API int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) +MA_API MA_NO_INLINE int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) { char* dstorig; @@ -12430,7 +12504,7 @@ MA_API int ma_strcat_s(char* dst, size_t dstSizeInBytes, const char* src) return 0; } -MA_API int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) +MA_API MA_NO_INLINE int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_t count) { char* dstorig; @@ -12476,7 +12550,7 @@ MA_API int ma_strncat_s(char* dst, size_t dstSizeInBytes, const char* src, size_ return 0; } -MA_API int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) +MA_API MA_NO_INLINE int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) { int sign; unsigned int valueU; @@ -12545,7 +12619,7 @@ MA_API int ma_itoa_s(int value, char* dst, size_t dstSizeInBytes, int radix) return 0; } -MA_API int ma_strcmp(const char* str1, const char* str2) +MA_API MA_NO_INLINE int ma_strcmp(const char* str1, const char* str2) { if (str1 == str2) return 0; @@ -12568,7 +12642,7 @@ MA_API int ma_strcmp(const char* str1, const char* str2) return ((unsigned char*)str1)[0] - ((unsigned char*)str2)[0]; } -MA_API int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB) +MA_API MA_NO_INLINE int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* srcB) { int result; @@ -12585,7 +12659,7 @@ MA_API int ma_strappend(char* dst, size_t dstSize, const char* srcA, const char* return result; } -MA_API char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API MA_NO_INLINE char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAllocationCallbacks) { size_t sz; char* dst; @@ -12605,7 +12679,7 @@ MA_API char* ma_copy_string(const char* src, const ma_allocation_callbacks* pAll return dst; } -MA_API wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks) +MA_API MA_NO_INLINE wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbacks* pAllocationCallbacks) { size_t sz = wcslen(src)+1; wchar_t* dst = (wchar_t*)ma_malloc(sz * sizeof(*dst), pAllocationCallbacks); @@ -13944,9 +14018,8 @@ static MA_INLINE ma_int32 ma_dither_s32(ma_dither_mode ditherMode, ma_int32 dith Atomics **************************************************************************************************************************************************************/ -/* c89atomic.h begin */ -#ifndef c89atomic_h -#define c89atomic_h +/* ma_atomic.h begin */ +#ifndef ma_atomic_h #if defined(__cplusplus) extern "C" { #endif @@ -13957,149 +14030,83 @@ extern "C" { #pragma GCC diagnostic ignored "-Wc++11-long-long" #endif #endif -typedef signed char c89atomic_int8; -typedef unsigned char c89atomic_uint8; -typedef signed short c89atomic_int16; -typedef unsigned short c89atomic_uint16; -typedef signed int c89atomic_int32; -typedef unsigned int c89atomic_uint32; -#if defined(_MSC_VER) && !defined(__clang__) - typedef signed __int64 c89atomic_int64; - typedef unsigned __int64 c89atomic_uint64; -#else - typedef signed long long c89atomic_int64; - typedef unsigned long long c89atomic_uint64; -#endif -typedef int c89atomic_memory_order; -typedef unsigned char c89atomic_bool; -#if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT) -#ifdef _WIN32 -#ifdef _WIN64 -#define C89ATOMIC_64BIT -#else -#define C89ATOMIC_32BIT -#endif -#endif -#endif -#if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT) -#ifdef __GNUC__ -#ifdef __LP64__ -#define C89ATOMIC_64BIT -#else -#define C89ATOMIC_32BIT -#endif -#endif -#endif -#if !defined(C89ATOMIC_64BIT) && !defined(C89ATOMIC_32BIT) -#include -#if INTPTR_MAX == INT64_MAX -#define C89ATOMIC_64BIT -#else -#define C89ATOMIC_32BIT -#endif -#endif -#if defined(__arm__) || defined(_M_ARM) -#define C89ATOMIC_ARM32 -#endif -#if defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) -#define C89ATOMIC_ARM64 -#endif -#if defined(__x86_64__) || defined(_M_X64) -#define C89ATOMIC_X64 -#elif defined(__i386) || defined(_M_IX86) -#define C89ATOMIC_X86 -#elif defined(C89ATOMIC_ARM32) || defined(C89ATOMIC_ARM64) -#define C89ATOMIC_ARM -#endif -#if defined(_MSC_VER) - #define C89ATOMIC_INLINE __forceinline -#elif defined(__GNUC__) - #if defined(__STRICT_ANSI__) - #define C89ATOMIC_INLINE __inline__ __attribute__((always_inline)) - #else - #define C89ATOMIC_INLINE inline __attribute__((always_inline)) - #endif -#elif defined(__WATCOMC__) || defined(__DMC__) - #define C89ATOMIC_INLINE __inline -#else - #define C89ATOMIC_INLINE -#endif -#define C89ATOMIC_HAS_8 -#define C89ATOMIC_HAS_16 -#define C89ATOMIC_HAS_32 -#define C89ATOMIC_HAS_64 +typedef int ma_atomic_memory_order; +#define MA_ATOMIC_HAS_8 +#define MA_ATOMIC_HAS_16 +#define MA_ATOMIC_HAS_32 +#define MA_ATOMIC_HAS_64 #if (defined(_MSC_VER) ) || defined(__WATCOMC__) || defined(__DMC__) - #define C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, intrin, c89atomicType, msvcType) \ - c89atomicType result; \ + #define MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, intrin, ma_atomicType, msvcType) \ + ma_atomicType result; \ switch (order) \ { \ - case c89atomic_memory_order_relaxed: \ + case ma_atomic_memory_order_relaxed: \ { \ - result = (c89atomicType)intrin##_nf((volatile msvcType*)dst, (msvcType)src); \ + result = (ma_atomicType)intrin##_nf((volatile msvcType*)dst, (msvcType)src); \ } break; \ - case c89atomic_memory_order_consume: \ - case c89atomic_memory_order_acquire: \ + case ma_atomic_memory_order_consume: \ + case ma_atomic_memory_order_acquire: \ { \ - result = (c89atomicType)intrin##_acq((volatile msvcType*)dst, (msvcType)src); \ + result = (ma_atomicType)intrin##_acq((volatile msvcType*)dst, (msvcType)src); \ } break; \ - case c89atomic_memory_order_release: \ + case ma_atomic_memory_order_release: \ { \ - result = (c89atomicType)intrin##_rel((volatile msvcType*)dst, (msvcType)src); \ + result = (ma_atomicType)intrin##_rel((volatile msvcType*)dst, (msvcType)src); \ } break; \ - case c89atomic_memory_order_acq_rel: \ - case c89atomic_memory_order_seq_cst: \ + case ma_atomic_memory_order_acq_rel: \ + case ma_atomic_memory_order_seq_cst: \ default: \ { \ - result = (c89atomicType)intrin((volatile msvcType*)dst, (msvcType)src); \ + result = (ma_atomicType)intrin((volatile msvcType*)dst, (msvcType)src); \ } break; \ } \ return result; - #define C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, desired, order, intrin, c89atomicType, msvcType) \ - c89atomicType result; \ + #define MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, expected, desired, order, intrin, ma_atomicType, msvcType) \ + ma_atomicType result; \ switch (order) \ { \ - case c89atomic_memory_order_relaxed: \ + case ma_atomic_memory_order_relaxed: \ { \ - result = (c89atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + result = (ma_atomicType)intrin##_nf((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ } break; \ - case c89atomic_memory_order_consume: \ - case c89atomic_memory_order_acquire: \ + case ma_atomic_memory_order_consume: \ + case ma_atomic_memory_order_acquire: \ { \ - result = (c89atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + result = (ma_atomicType)intrin##_acq((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ } break; \ - case c89atomic_memory_order_release: \ + case ma_atomic_memory_order_release: \ { \ - result = (c89atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + result = (ma_atomicType)intrin##_rel((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ } break; \ - case c89atomic_memory_order_acq_rel: \ - case c89atomic_memory_order_seq_cst: \ + case ma_atomic_memory_order_acq_rel: \ + case ma_atomic_memory_order_seq_cst: \ default: \ { \ - result = (c89atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ + result = (ma_atomicType)intrin((volatile msvcType*)ptr, (msvcType)expected, (msvcType)desired); \ } break; \ } \ return result; - #define c89atomic_memory_order_relaxed 0 - #define c89atomic_memory_order_consume 1 - #define c89atomic_memory_order_acquire 2 - #define c89atomic_memory_order_release 3 - #define c89atomic_memory_order_acq_rel 4 - #define c89atomic_memory_order_seq_cst 5 - #if _MSC_VER < 1600 && defined(C89ATOMIC_X86) - #define C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY + #define ma_atomic_memory_order_relaxed 0 + #define ma_atomic_memory_order_consume 1 + #define ma_atomic_memory_order_acquire 2 + #define ma_atomic_memory_order_release 3 + #define ma_atomic_memory_order_acq_rel 4 + #define ma_atomic_memory_order_seq_cst 5 + #if _MSC_VER < 1600 && defined(MA_X86) + #define MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY #endif #if _MSC_VER < 1600 - #undef C89ATOMIC_HAS_8 - #undef C89ATOMIC_HAS_16 + #undef MA_ATOMIC_HAS_8 + #undef MA_ATOMIC_HAS_16 #endif - #if !defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #if !defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) #include #endif - #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired) + #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) { - c89atomic_uint8 result = 0; + ma_uint8 result = 0; __asm { mov ecx, dst mov al, expected @@ -14110,10 +14117,10 @@ typedef unsigned char c89atomic_bool; return result; } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) { - c89atomic_uint16 result = 0; + ma_uint16 result = 0; __asm { mov ecx, dst mov ax, expected @@ -14124,10 +14131,10 @@ typedef unsigned char c89atomic_bool; return result; } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) { - c89atomic_uint32 result = 0; + ma_uint32 result = 0; __asm { mov ecx, dst mov eax, expected @@ -14138,11 +14145,11 @@ typedef unsigned char c89atomic_bool; return result; } #endif - #if defined(C89ATOMIC_HAS_64) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired) + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) { - c89atomic_uint32 resultEAX = 0; - c89atomic_uint32 resultEDX = 0; + ma_uint32 resultEAX = 0; + ma_uint32 resultEDX = 0; __asm { mov esi, dst mov eax, dword ptr expected @@ -14153,28 +14160,28 @@ typedef unsigned char c89atomic_bool; mov resultEAX, eax mov resultEDX, edx } - return ((c89atomic_uint64)resultEDX << 32) | resultEAX; + return ((ma_uint64)resultEDX << 32) | resultEAX; } #endif #else - #if defined(C89ATOMIC_HAS_8) - #define c89atomic_compare_and_swap_8( dst, expected, desired) (c89atomic_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected) + #if defined(MA_ATOMIC_HAS_8) + #define ma_atomic_compare_and_swap_8( dst, expected, desired) (ma_uint8 )_InterlockedCompareExchange8((volatile char*)dst, (char)desired, (char)expected) #endif - #if defined(C89ATOMIC_HAS_16) - #define c89atomic_compare_and_swap_16(dst, expected, desired) (c89atomic_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected) + #if defined(MA_ATOMIC_HAS_16) + #define ma_atomic_compare_and_swap_16(dst, expected, desired) (ma_uint16)_InterlockedCompareExchange16((volatile short*)dst, (short)desired, (short)expected) #endif - #if defined(C89ATOMIC_HAS_32) - #define c89atomic_compare_and_swap_32(dst, expected, desired) (c89atomic_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected) + #if defined(MA_ATOMIC_HAS_32) + #define ma_atomic_compare_and_swap_32(dst, expected, desired) (ma_uint32)_InterlockedCompareExchange((volatile long*)dst, (long)desired, (long)expected) #endif - #if defined(C89ATOMIC_HAS_64) - #define c89atomic_compare_and_swap_64(dst, expected, desired) (c89atomic_uint64)_InterlockedCompareExchange64((volatile c89atomic_int64*)dst, (c89atomic_int64)desired, (c89atomic_int64)expected) + #if defined(MA_ATOMIC_HAS_64) + #define ma_atomic_compare_and_swap_64(dst, expected, desired) (ma_uint64)_InterlockedCompareExchange64((volatile ma_int64*)dst, (ma_int64)desired, (ma_int64)expected) #endif #endif - #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - c89atomic_uint8 result = 0; + ma_uint8 result = 0; (void)order; __asm { mov ecx, dst @@ -14185,10 +14192,10 @@ typedef unsigned char c89atomic_bool; return result; } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - c89atomic_uint16 result = 0; + ma_uint16 result = 0; (void)order; __asm { mov ecx, dst @@ -14199,10 +14206,10 @@ typedef unsigned char c89atomic_bool; return result; } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - c89atomic_uint32 result = 0; + ma_uint32 result = 0; (void)order; __asm { mov ecx, dst @@ -14214,68 +14221,68 @@ typedef unsigned char c89atomic_bool; } #endif #else - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange8, c89atomic_uint8, char); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange8, ma_uint8, char); #else (void)order; - return (c89atomic_uint8)_InterlockedExchange8((volatile char*)dst, (char)src); + return (ma_uint8)_InterlockedExchange8((volatile char*)dst, (char)src); #endif } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange16, c89atomic_uint16, short); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange16, ma_uint16, short); #else (void)order; - return (c89atomic_uint16)_InterlockedExchange16((volatile short*)dst, (short)src); + return (ma_uint16)_InterlockedExchange16((volatile short*)dst, (short)src); #endif } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange, c89atomic_uint32, long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange, ma_uint32, long); #else (void)order; - return (c89atomic_uint32)_InterlockedExchange((volatile long*)dst, (long)src); + return (ma_uint32)_InterlockedExchange((volatile long*)dst, (long)src); #endif } #endif - #if defined(C89ATOMIC_HAS_64) && defined(C89ATOMIC_64BIT) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) + static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, c89atomic_uint64, long long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchange64, ma_uint64, long long); #else (void)order; - return (c89atomic_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src); + return (ma_uint64)_InterlockedExchange64((volatile long long*)dst, (long long)src); #endif } #else #endif #endif - #if defined(C89ATOMIC_HAS_64) && !defined(C89ATOMIC_64BIT) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) + static MA_INLINE ma_uint64 __stdcall ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - c89atomic_uint64 oldValue; + ma_uint64 oldValue; do { oldValue = *dst; - } while (c89atomic_compare_and_swap_64(dst, oldValue, src) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, src) != oldValue); (void)order; return oldValue; } #endif - #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - c89atomic_uint8 result = 0; + ma_uint8 result = 0; (void)order; __asm { mov ecx, dst @@ -14286,10 +14293,10 @@ typedef unsigned char c89atomic_bool; return result; } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - c89atomic_uint16 result = 0; + ma_uint16 result = 0; (void)order; __asm { mov ecx, dst @@ -14300,10 +14307,10 @@ typedef unsigned char c89atomic_bool; return result; } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - c89atomic_uint32 result = 0; + ma_uint32 result = 0; (void)order; __asm { mov ecx, dst @@ -14315,67 +14322,67 @@ typedef unsigned char c89atomic_bool; } #endif #else - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, c89atomic_uint8, char); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd8, ma_uint8, char); #else (void)order; - return (c89atomic_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src); + return (ma_uint8)_InterlockedExchangeAdd8((volatile char*)dst, (char)src); #endif } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd16, c89atomic_uint16, short); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd16, ma_uint16, short); #else (void)order; - return (c89atomic_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src); + return (ma_uint16)_InterlockedExchangeAdd16((volatile short*)dst, (short)src); #endif } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd, c89atomic_uint32, long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd, ma_uint32, long); #else (void)order; - return (c89atomic_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src); + return (ma_uint32)_InterlockedExchangeAdd((volatile long*)dst, (long)src); #endif } #endif - #if defined(C89ATOMIC_HAS_64) && defined(C89ATOMIC_64BIT) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_64) && defined(MA_64BIT) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, c89atomic_uint64, long long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedExchangeAdd64, ma_uint64, long long); #else (void)order; - return (c89atomic_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src); + return (ma_uint64)_InterlockedExchangeAdd64((volatile long long*)dst, (long long)src); #endif } #else #endif #endif - #if defined(C89ATOMIC_HAS_64) && !defined(C89ATOMIC_64BIT) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_64) && !defined(MA_64BIT) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; + ma_uint64 oldValue; + ma_uint64 newValue; do { oldValue = *dst; newValue = oldValue + src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif - #if defined(C89ATOMIC_MSVC_USE_INLINED_ASSEMBLY) - static C89ATOMIC_INLINE void __stdcall c89atomic_thread_fence(c89atomic_memory_order order) + #if defined(MA_ATOMIC_MSVC_USE_INLINED_ASSEMBLY) + static MA_INLINE void __stdcall ma_atomic_thread_fence(ma_atomic_memory_order order) { (void)order; __asm { @@ -14383,1067 +14390,1067 @@ typedef unsigned char c89atomic_bool; } } #else - #if defined(C89ATOMIC_X64) - #define c89atomic_thread_fence(order) __faststorefence(), (void)order - #elif defined(C89ATOMIC_ARM64) - #define c89atomic_thread_fence(order) __dmb(_ARM64_BARRIER_ISH), (void)order + #if defined(MA_X64) + #define ma_atomic_thread_fence(order) __faststorefence(), (void)order + #elif defined(MA_ARM64) + #define ma_atomic_thread_fence(order) __dmb(_ARM64_BARRIER_ISH), (void)order #else - static C89ATOMIC_INLINE void c89atomic_thread_fence(c89atomic_memory_order order) + static MA_INLINE void ma_atomic_thread_fence(ma_atomic_memory_order order) { - volatile c89atomic_uint32 barrier = 0; - c89atomic_fetch_add_explicit_32(&barrier, 0, order); + volatile ma_uint32 barrier = 0; + ma_atomic_fetch_add_explicit_32(&barrier, 0, order); } #endif #endif - #define c89atomic_compiler_fence() c89atomic_thread_fence(c89atomic_memory_order_seq_cst) - #define c89atomic_signal_fence(order) c89atomic_thread_fence(order) - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8* ptr, c89atomic_memory_order order) + #define ma_atomic_compiler_fence() ma_atomic_thread_fence(ma_atomic_memory_order_seq_cst) + #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, c89atomic_uint8, char); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange8, ma_uint8, char); #else (void)order; - return c89atomic_compare_and_swap_8((volatile c89atomic_uint8*)ptr, 0, 0); + return ma_atomic_compare_and_swap_8((volatile ma_uint8*)ptr, 0, 0); #endif } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile const c89atomic_uint16* ptr, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, c89atomic_uint16, short); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange16, ma_uint16, short); #else (void)order; - return c89atomic_compare_and_swap_16((volatile c89atomic_uint16*)ptr, 0, 0); + return ma_atomic_compare_and_swap_16((volatile ma_uint16*)ptr, 0, 0); #endif } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile const c89atomic_uint32* ptr, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, c89atomic_uint32, long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange, ma_uint32, long); #else (void)order; - return c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)ptr, 0, 0); + return ma_atomic_compare_and_swap_32((volatile ma_uint32*)ptr, 0, 0); #endif } #endif - #if defined(C89ATOMIC_HAS_64) - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile const c89atomic_uint64* ptr, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, c89atomic_uint64, long long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC_COMPARE_EXCHANGE(ptr, 0, 0, order, _InterlockedCompareExchange64, ma_uint64, long long); #else (void)order; - return c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)ptr, 0, 0); + return ma_atomic_compare_and_swap_64((volatile ma_uint64*)ptr, 0, 0); #endif } #endif - #if defined(C89ATOMIC_HAS_8) - #define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order) + #if defined(MA_ATOMIC_HAS_8) + #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) #endif - #if defined(C89ATOMIC_HAS_16) - #define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order) + #if defined(MA_ATOMIC_HAS_16) + #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) #endif - #if defined(C89ATOMIC_HAS_32) - #define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order) + #if defined(MA_ATOMIC_HAS_32) + #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) #endif - #if defined(C89ATOMIC_HAS_64) - #define c89atomic_store_explicit_64(dst, src, order) (void)c89atomic_exchange_explicit_64(dst, src, order) + #if defined(MA_ATOMIC_HAS_64) + #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) #endif - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; + ma_uint8 oldValue; + ma_uint8 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue - src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint8)(oldValue - src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; + ma_uint16 oldValue; + ma_uint16 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue - src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint16)(oldValue - src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; + ma_uint32 oldValue; + ma_uint32 newValue; do { oldValue = *dst; newValue = oldValue - src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif - #if defined(C89ATOMIC_HAS_64) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; + ma_uint64 oldValue; + ma_uint64 newValue; do { oldValue = *dst; newValue = oldValue - src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, c89atomic_uint8, char); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd8, ma_uint8, char); #else - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; + ma_uint8 oldValue; + ma_uint8 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue & src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint8)(oldValue & src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, c89atomic_uint16, short); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd16, ma_uint16, short); #else - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; + ma_uint16 oldValue; + ma_uint16 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue & src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint16)(oldValue & src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, c89atomic_uint32, long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd, ma_uint32, long); #else - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; + ma_uint32 oldValue; + ma_uint32 newValue; do { oldValue = *dst; newValue = oldValue & src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_64) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, c89atomic_uint64, long long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedAnd64, ma_uint64, long long); #else - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; + ma_uint64 oldValue; + ma_uint64 newValue; do { oldValue = *dst; newValue = oldValue & src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, c89atomic_uint8, char); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor8, ma_uint8, char); #else - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; + ma_uint8 oldValue; + ma_uint8 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue ^ src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint8)(oldValue ^ src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, c89atomic_uint16, short); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor16, ma_uint16, short); #else - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; + ma_uint16 oldValue; + ma_uint16 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue ^ src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint16)(oldValue ^ src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, c89atomic_uint32, long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor, ma_uint32, long); #else - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; + ma_uint32 oldValue; + ma_uint32 newValue; do { oldValue = *dst; newValue = oldValue ^ src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_64) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, c89atomic_uint64, long long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedXor64, ma_uint64, long long); #else - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; + ma_uint64 oldValue; + ma_uint64 newValue; do { oldValue = *dst; newValue = oldValue ^ src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_uint8 __stdcall c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_uint8 __stdcall ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, c89atomic_uint8, char); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr8, ma_uint8, char); #else - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; + ma_uint8 oldValue; + ma_uint8 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue | src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint8)(oldValue | src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_uint16 __stdcall c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_uint16 __stdcall ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, c89atomic_uint16, short); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr16, ma_uint16, short); #else - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; + ma_uint16 oldValue; + ma_uint16 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue | src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint16)(oldValue | src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_uint32 __stdcall c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_uint32 __stdcall ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, c89atomic_uint32, long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr, ma_uint32, long); #else - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; + ma_uint32 oldValue; + ma_uint32 newValue; do { oldValue = *dst; newValue = oldValue | src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_64) - static C89ATOMIC_INLINE c89atomic_uint64 __stdcall c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_uint64 __stdcall ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_ARM) - C89ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, c89atomic_uint64, long long); + #if defined(MA_ARM) + MA_ATOMIC_MSVC_ARM_INTRINSIC(dst, src, order, _InterlockedOr64, ma_uint64, long long); #else - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; + ma_uint64 oldValue; + ma_uint64 newValue; do { oldValue = *dst; newValue = oldValue | src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; #endif } #endif - #if defined(C89ATOMIC_HAS_8) - #define c89atomic_test_and_set_explicit_8( dst, order) c89atomic_exchange_explicit_8 (dst, 1, order) + #if defined(MA_ATOMIC_HAS_8) + #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) #endif - #if defined(C89ATOMIC_HAS_16) - #define c89atomic_test_and_set_explicit_16(dst, order) c89atomic_exchange_explicit_16(dst, 1, order) + #if defined(MA_ATOMIC_HAS_16) + #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) #endif - #if defined(C89ATOMIC_HAS_32) - #define c89atomic_test_and_set_explicit_32(dst, order) c89atomic_exchange_explicit_32(dst, 1, order) + #if defined(MA_ATOMIC_HAS_32) + #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) #endif - #if defined(C89ATOMIC_HAS_64) - #define c89atomic_test_and_set_explicit_64(dst, order) c89atomic_exchange_explicit_64(dst, 1, order) + #if defined(MA_ATOMIC_HAS_64) + #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) #endif - #if defined(C89ATOMIC_HAS_8) - #define c89atomic_clear_explicit_8( dst, order) c89atomic_store_explicit_8 (dst, 0, order) + #if defined(MA_ATOMIC_HAS_8) + #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) #endif - #if defined(C89ATOMIC_HAS_16) - #define c89atomic_clear_explicit_16(dst, order) c89atomic_store_explicit_16(dst, 0, order) + #if defined(MA_ATOMIC_HAS_16) + #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) #endif - #if defined(C89ATOMIC_HAS_32) - #define c89atomic_clear_explicit_32(dst, order) c89atomic_store_explicit_32(dst, 0, order) + #if defined(MA_ATOMIC_HAS_32) + #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) #endif - #if defined(C89ATOMIC_HAS_64) - #define c89atomic_clear_explicit_64(dst, order) c89atomic_store_explicit_64(dst, 0, order) + #if defined(MA_ATOMIC_HAS_64) + #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) #endif - #if defined(C89ATOMIC_HAS_8) - typedef c89atomic_uint8 c89atomic_flag; - #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_8(ptr, order) - #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_8(ptr, order) - #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order) + #if defined(MA_ATOMIC_HAS_8) + typedef ma_uint8 ma_atomic_flag; + #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) + #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) + #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) #else - typedef c89atomic_uint32 c89atomic_flag; - #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_32(ptr, order) - #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_32(ptr, order) - #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_32(ptr, order) + typedef ma_uint32 ma_atomic_flag; + #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_32(ptr, order) + #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_32(ptr, order) + #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_32(ptr, order) #endif #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) - #define C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE - #define C89ATOMIC_HAS_NATIVE_IS_LOCK_FREE - #define c89atomic_memory_order_relaxed __ATOMIC_RELAXED - #define c89atomic_memory_order_consume __ATOMIC_CONSUME - #define c89atomic_memory_order_acquire __ATOMIC_ACQUIRE - #define c89atomic_memory_order_release __ATOMIC_RELEASE - #define c89atomic_memory_order_acq_rel __ATOMIC_ACQ_REL - #define c89atomic_memory_order_seq_cst __ATOMIC_SEQ_CST - #define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory") - #define c89atomic_thread_fence(order) __atomic_thread_fence(order) - #define c89atomic_signal_fence(order) __atomic_signal_fence(order) - #define c89atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr) - #define c89atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr) - #define c89atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr) - #define c89atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr) - #define c89atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order) - #define c89atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order) - #define c89atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order) - #define c89atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order) - #define c89atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order) - #define c89atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order) - #define c89atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order) - #define c89atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order) - #define c89atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order) - #define c89atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order) - #define c89atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order) - #define c89atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order) - #define c89atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order) - #define c89atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order) - #define c89atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order) - #define c89atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order) - #define c89atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order) - #define c89atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order) - #define c89atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order) - #define c89atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order) - #define c89atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) - #define c89atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order) - #define c89atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order) - #define c89atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order) - #define c89atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order) - #define c89atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order) - #define c89atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define c89atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define c89atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order) - #define c89atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order) - #define c89atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order) - #define c89atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order) - #define c89atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order) - #define c89atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order) - #define c89atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define c89atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define c89atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order) - #define c89atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order) - #define c89atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order) - #define c89atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order) - #define c89atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order) - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired) + #define MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE + #define MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE + #define ma_atomic_memory_order_relaxed __ATOMIC_RELAXED + #define ma_atomic_memory_order_consume __ATOMIC_CONSUME + #define ma_atomic_memory_order_acquire __ATOMIC_ACQUIRE + #define ma_atomic_memory_order_release __ATOMIC_RELEASE + #define ma_atomic_memory_order_acq_rel __ATOMIC_ACQ_REL + #define ma_atomic_memory_order_seq_cst __ATOMIC_SEQ_CST + #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") + #define ma_atomic_thread_fence(order) __atomic_thread_fence(order) + #define ma_atomic_signal_fence(order) __atomic_signal_fence(order) + #define ma_atomic_is_lock_free_8(ptr) __atomic_is_lock_free(1, ptr) + #define ma_atomic_is_lock_free_16(ptr) __atomic_is_lock_free(2, ptr) + #define ma_atomic_is_lock_free_32(ptr) __atomic_is_lock_free(4, ptr) + #define ma_atomic_is_lock_free_64(ptr) __atomic_is_lock_free(8, ptr) + #define ma_atomic_test_and_set_explicit_8( dst, order) __atomic_exchange_n(dst, 1, order) + #define ma_atomic_test_and_set_explicit_16(dst, order) __atomic_exchange_n(dst, 1, order) + #define ma_atomic_test_and_set_explicit_32(dst, order) __atomic_exchange_n(dst, 1, order) + #define ma_atomic_test_and_set_explicit_64(dst, order) __atomic_exchange_n(dst, 1, order) + #define ma_atomic_clear_explicit_8( dst, order) __atomic_store_n(dst, 0, order) + #define ma_atomic_clear_explicit_16(dst, order) __atomic_store_n(dst, 0, order) + #define ma_atomic_clear_explicit_32(dst, order) __atomic_store_n(dst, 0, order) + #define ma_atomic_clear_explicit_64(dst, order) __atomic_store_n(dst, 0, order) + #define ma_atomic_store_explicit_8( dst, src, order) __atomic_store_n(dst, src, order) + #define ma_atomic_store_explicit_16(dst, src, order) __atomic_store_n(dst, src, order) + #define ma_atomic_store_explicit_32(dst, src, order) __atomic_store_n(dst, src, order) + #define ma_atomic_store_explicit_64(dst, src, order) __atomic_store_n(dst, src, order) + #define ma_atomic_load_explicit_8( dst, order) __atomic_load_n(dst, order) + #define ma_atomic_load_explicit_16(dst, order) __atomic_load_n(dst, order) + #define ma_atomic_load_explicit_32(dst, order) __atomic_load_n(dst, order) + #define ma_atomic_load_explicit_64(dst, order) __atomic_load_n(dst, order) + #define ma_atomic_exchange_explicit_8( dst, src, order) __atomic_exchange_n(dst, src, order) + #define ma_atomic_exchange_explicit_16(dst, src, order) __atomic_exchange_n(dst, src, order) + #define ma_atomic_exchange_explicit_32(dst, src, order) __atomic_exchange_n(dst, src, order) + #define ma_atomic_exchange_explicit_64(dst, src, order) __atomic_exchange_n(dst, src, order) + #define ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 0, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) __atomic_compare_exchange_n(dst, expected, desired, 1, successOrder, failureOrder) + #define ma_atomic_fetch_add_explicit_8( dst, src, order) __atomic_fetch_add(dst, src, order) + #define ma_atomic_fetch_add_explicit_16(dst, src, order) __atomic_fetch_add(dst, src, order) + #define ma_atomic_fetch_add_explicit_32(dst, src, order) __atomic_fetch_add(dst, src, order) + #define ma_atomic_fetch_add_explicit_64(dst, src, order) __atomic_fetch_add(dst, src, order) + #define ma_atomic_fetch_sub_explicit_8( dst, src, order) __atomic_fetch_sub(dst, src, order) + #define ma_atomic_fetch_sub_explicit_16(dst, src, order) __atomic_fetch_sub(dst, src, order) + #define ma_atomic_fetch_sub_explicit_32(dst, src, order) __atomic_fetch_sub(dst, src, order) + #define ma_atomic_fetch_sub_explicit_64(dst, src, order) __atomic_fetch_sub(dst, src, order) + #define ma_atomic_fetch_or_explicit_8( dst, src, order) __atomic_fetch_or(dst, src, order) + #define ma_atomic_fetch_or_explicit_16(dst, src, order) __atomic_fetch_or(dst, src, order) + #define ma_atomic_fetch_or_explicit_32(dst, src, order) __atomic_fetch_or(dst, src, order) + #define ma_atomic_fetch_or_explicit_64(dst, src, order) __atomic_fetch_or(dst, src, order) + #define ma_atomic_fetch_xor_explicit_8( dst, src, order) __atomic_fetch_xor(dst, src, order) + #define ma_atomic_fetch_xor_explicit_16(dst, src, order) __atomic_fetch_xor(dst, src, order) + #define ma_atomic_fetch_xor_explicit_32(dst, src, order) __atomic_fetch_xor(dst, src, order) + #define ma_atomic_fetch_xor_explicit_64(dst, src, order) __atomic_fetch_xor(dst, src, order) + #define ma_atomic_fetch_and_explicit_8( dst, src, order) __atomic_fetch_and(dst, src, order) + #define ma_atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order) + #define ma_atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order) + #define ma_atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order) + static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) { __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired) + static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) { __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired) + static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) { __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired) + static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) { __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); return expected; } - typedef c89atomic_uint8 c89atomic_flag; - #define c89atomic_flag_test_and_set_explicit(dst, order) (c89atomic_bool)__atomic_test_and_set(dst, order) - #define c89atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order) - #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order) + typedef ma_uint8 ma_atomic_flag; + #define ma_atomic_flag_test_and_set_explicit(dst, order) (ma_bool32)__atomic_test_and_set(dst, order) + #define ma_atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order) + #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) #else - #define c89atomic_memory_order_relaxed 1 - #define c89atomic_memory_order_consume 2 - #define c89atomic_memory_order_acquire 3 - #define c89atomic_memory_order_release 4 - #define c89atomic_memory_order_acq_rel 5 - #define c89atomic_memory_order_seq_cst 6 - #define c89atomic_compiler_fence() __asm__ __volatile__("":::"memory") + #define ma_atomic_memory_order_relaxed 1 + #define ma_atomic_memory_order_consume 2 + #define ma_atomic_memory_order_acquire 3 + #define ma_atomic_memory_order_release 4 + #define ma_atomic_memory_order_acq_rel 5 + #define ma_atomic_memory_order_seq_cst 6 + #define ma_atomic_compiler_fence() __asm__ __volatile__("":::"memory") #if defined(__GNUC__) - #define c89atomic_thread_fence(order) __sync_synchronize(), (void)order - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + #define ma_atomic_thread_fence(order) __sync_synchronize(), (void)order + static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - if (order > c89atomic_memory_order_acquire) { + if (order > ma_atomic_memory_order_acquire) { __sync_synchronize(); } return __sync_lock_test_and_set(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - c89atomic_uint16 oldValue; + ma_uint16 oldValue; do { oldValue = *dst; } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - c89atomic_uint32 oldValue; + ma_uint32 oldValue; do { oldValue = *dst; } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - c89atomic_uint64 oldValue; + ma_uint64 oldValue; do { oldValue = *dst; } while (__sync_val_compare_and_swap(dst, oldValue, src) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_add(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_add(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_add(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_add(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_sub(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_sub(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_sub(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_sub(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_or(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_or(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_or(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_or(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_xor(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_xor(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_xor(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_xor(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_and(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_and(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_and(dst, src); } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { (void)order; return __sync_fetch_and_and(dst, src); } - #define c89atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define ma_atomic_compare_and_swap_8( dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define ma_atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define ma_atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + #define ma_atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) #else - #if defined(C89ATOMIC_X86) - #define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc") - #elif defined(C89ATOMIC_X64) - #define c89atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc") + #if defined(MA_X86) + #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addl $0, (%%esp)" ::: "memory", "cc") + #elif defined(MA_X64) + #define ma_atomic_thread_fence(order) __asm__ __volatile__("lock; addq $0, (%%rsp)" ::: "memory", "cc") #else #error Unsupported architecture. Please submit a feature request. #endif - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired) + static MA_INLINE ma_uint8 ma_atomic_compare_and_swap_8(volatile ma_uint8* dst, ma_uint8 expected, ma_uint8 desired) { - c89atomic_uint8 result; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + ma_uint8 result; + #if defined(MA_X86) || defined(MA_X64) __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired) + static MA_INLINE ma_uint16 ma_atomic_compare_and_swap_16(volatile ma_uint16* dst, ma_uint16 expected, ma_uint16 desired) { - c89atomic_uint16 result; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + ma_uint16 result; + #if defined(MA_X86) || defined(MA_X64) __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired) + static MA_INLINE ma_uint32 ma_atomic_compare_and_swap_32(volatile ma_uint32* dst, ma_uint32 expected, ma_uint32 desired) { - c89atomic_uint32 result; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + ma_uint32 result; + #if defined(MA_X86) || defined(MA_X64) __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired) + static MA_INLINE ma_uint64 ma_atomic_compare_and_swap_64(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) { - volatile c89atomic_uint64 result; - #if defined(C89ATOMIC_X86) - c89atomic_uint32 resultEAX; - c89atomic_uint32 resultEDX; + volatile ma_uint64 result; + #if defined(MA_X86) + ma_uint32 resultEAX; + ma_uint32 resultEDX; __asm__ __volatile__("push %%ebx; xchg %5, %%ebx; lock; cmpxchg8b %0; pop %%ebx" : "+m"(*dst), "=a"(resultEAX), "=d"(resultEDX) : "a"(expected & 0xFFFFFFFF), "d"(expected >> 32), "r"(desired & 0xFFFFFFFF), "c"(desired >> 32) : "cc"); - result = ((c89atomic_uint64)resultEDX << 32) | resultEAX; - #elif defined(C89ATOMIC_X64) + result = ((ma_uint64)resultEDX << 32) | resultEAX; + #elif defined(MA_X64) __asm__ __volatile__("lock; cmpxchg %3, %0" : "+m"(*dst), "=a"(result) : "a"(expected), "d"(desired) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_exchange_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_exchange_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - c89atomic_uint8 result = 0; + ma_uint8 result = 0; (void)order; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + #if defined(MA_X86) || defined(MA_X64) __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_exchange_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_exchange_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - c89atomic_uint16 result = 0; + ma_uint16 result = 0; (void)order; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + #if defined(MA_X86) || defined(MA_X64) __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_exchange_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_exchange_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - c89atomic_uint32 result; + ma_uint32 result; (void)order; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + #if defined(MA_X86) || defined(MA_X64) __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_exchange_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_exchange_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - c89atomic_uint64 result; + ma_uint64 result; (void)order; - #if defined(C89ATOMIC_X86) + #if defined(MA_X86) do { result = *dst; - } while (c89atomic_compare_and_swap_64(dst, result, src) != result); - #elif defined(C89ATOMIC_X64) + } while (ma_atomic_compare_and_swap_64(dst, result, src) != result); + #elif defined(MA_X64) __asm__ __volatile__("lock; xchg %1, %0" : "+m"(*dst), "=a"(result) : "a"(src)); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_add_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_fetch_add_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - c89atomic_uint8 result; + ma_uint8 result; (void)order; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + #if defined(MA_X86) || defined(MA_X64) __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_add_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_fetch_add_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - c89atomic_uint16 result; + ma_uint16 result; (void)order; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + #if defined(MA_X86) || defined(MA_X64) __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_add_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_fetch_add_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - c89atomic_uint32 result; + ma_uint32 result; (void)order; - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + #if defined(MA_X86) || defined(MA_X64) __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); #else #error Unsupported architecture. Please submit a feature request. #endif return result; } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_add_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_fetch_add_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - #if defined(C89ATOMIC_X86) - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; + #if defined(MA_X86) + ma_uint64 oldValue; + ma_uint64 newValue; (void)order; do { oldValue = *dst; newValue = oldValue + src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); return oldValue; - #elif defined(C89ATOMIC_X64) - c89atomic_uint64 result; + #elif defined(MA_X64) + ma_uint64 result; (void)order; __asm__ __volatile__("lock; xadd %1, %0" : "+m"(*dst), "=a"(result) : "a"(src) : "cc"); return result; #endif } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_sub_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_fetch_sub_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; + ma_uint8 oldValue; + ma_uint8 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue - src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint8)(oldValue - src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_sub_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_fetch_sub_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; + ma_uint16 oldValue; + ma_uint16 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue - src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint16)(oldValue - src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_sub_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_fetch_sub_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; + ma_uint32 oldValue; + ma_uint32 newValue; do { oldValue = *dst; newValue = oldValue - src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_sub_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_fetch_sub_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; + ma_uint64 oldValue; + ma_uint64 newValue; do { oldValue = *dst; newValue = oldValue - src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_and_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_fetch_and_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; + ma_uint8 oldValue; + ma_uint8 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue & src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint8)(oldValue & src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_and_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_fetch_and_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; + ma_uint16 oldValue; + ma_uint16 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue & src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint16)(oldValue & src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_and_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_fetch_and_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; + ma_uint32 oldValue; + ma_uint32 newValue; do { oldValue = *dst; newValue = oldValue & src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_and_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_fetch_and_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; + ma_uint64 oldValue; + ma_uint64 newValue; do { oldValue = *dst; newValue = oldValue & src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_xor_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_fetch_xor_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; + ma_uint8 oldValue; + ma_uint8 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue ^ src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint8)(oldValue ^ src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_xor_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_fetch_xor_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; + ma_uint16 oldValue; + ma_uint16 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue ^ src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint16)(oldValue ^ src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_xor_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_fetch_xor_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; + ma_uint32 oldValue; + ma_uint32 newValue; do { oldValue = *dst; newValue = oldValue ^ src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_xor_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_fetch_xor_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; + ma_uint64 oldValue; + ma_uint64 newValue; do { oldValue = *dst; newValue = oldValue ^ src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_fetch_or_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8 src, c89atomic_memory_order order) + static MA_INLINE ma_uint8 ma_atomic_fetch_or_explicit_8(volatile ma_uint8* dst, ma_uint8 src, ma_atomic_memory_order order) { - c89atomic_uint8 oldValue; - c89atomic_uint8 newValue; + ma_uint8 oldValue; + ma_uint8 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint8)(oldValue | src); - } while (c89atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint8)(oldValue | src); + } while (ma_atomic_compare_and_swap_8(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_fetch_or_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16 src, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_fetch_or_explicit_16(volatile ma_uint16* dst, ma_uint16 src, ma_atomic_memory_order order) { - c89atomic_uint16 oldValue; - c89atomic_uint16 newValue; + ma_uint16 oldValue; + ma_uint16 newValue; do { oldValue = *dst; - newValue = (c89atomic_uint16)(oldValue | src); - } while (c89atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); + newValue = (ma_uint16)(oldValue | src); + } while (ma_atomic_compare_and_swap_16(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_fetch_or_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32 src, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_fetch_or_explicit_32(volatile ma_uint32* dst, ma_uint32 src, ma_atomic_memory_order order) { - c89atomic_uint32 oldValue; - c89atomic_uint32 newValue; + ma_uint32 oldValue; + ma_uint32 newValue; do { oldValue = *dst; newValue = oldValue | src; - } while (c89atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_32(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_fetch_or_explicit_64(volatile c89atomic_uint64* dst, c89atomic_uint64 src, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_fetch_or_explicit_64(volatile ma_uint64* dst, ma_uint64 src, ma_atomic_memory_order order) { - c89atomic_uint64 oldValue; - c89atomic_uint64 newValue; + ma_uint64 oldValue; + ma_uint64 newValue; do { oldValue = *dst; newValue = oldValue | src; - } while (c89atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); + } while (ma_atomic_compare_and_swap_64(dst, oldValue, newValue) != oldValue); (void)order; return oldValue; } #endif - #define c89atomic_signal_fence(order) c89atomic_thread_fence(order) - static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_load_explicit_8(volatile const c89atomic_uint8* ptr, c89atomic_memory_order order) + #define ma_atomic_signal_fence(order) ma_atomic_thread_fence(order) + static MA_INLINE ma_uint8 ma_atomic_load_explicit_8(volatile const ma_uint8* ptr, ma_atomic_memory_order order) { (void)order; - return c89atomic_compare_and_swap_8((c89atomic_uint8*)ptr, 0, 0); + return ma_atomic_compare_and_swap_8((ma_uint8*)ptr, 0, 0); } - static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_load_explicit_16(volatile const c89atomic_uint16* ptr, c89atomic_memory_order order) + static MA_INLINE ma_uint16 ma_atomic_load_explicit_16(volatile const ma_uint16* ptr, ma_atomic_memory_order order) { (void)order; - return c89atomic_compare_and_swap_16((c89atomic_uint16*)ptr, 0, 0); + return ma_atomic_compare_and_swap_16((ma_uint16*)ptr, 0, 0); } - static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_load_explicit_32(volatile const c89atomic_uint32* ptr, c89atomic_memory_order order) + static MA_INLINE ma_uint32 ma_atomic_load_explicit_32(volatile const ma_uint32* ptr, ma_atomic_memory_order order) { (void)order; - return c89atomic_compare_and_swap_32((c89atomic_uint32*)ptr, 0, 0); + return ma_atomic_compare_and_swap_32((ma_uint32*)ptr, 0, 0); } - static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_load_explicit_64(volatile const c89atomic_uint64* ptr, c89atomic_memory_order order) + static MA_INLINE ma_uint64 ma_atomic_load_explicit_64(volatile const ma_uint64* ptr, ma_atomic_memory_order order) { (void)order; - return c89atomic_compare_and_swap_64((c89atomic_uint64*)ptr, 0, 0); + return ma_atomic_compare_and_swap_64((ma_uint64*)ptr, 0, 0); } - #define c89atomic_store_explicit_8( dst, src, order) (void)c89atomic_exchange_explicit_8 (dst, src, order) - #define c89atomic_store_explicit_16(dst, src, order) (void)c89atomic_exchange_explicit_16(dst, src, order) - #define c89atomic_store_explicit_32(dst, src, order) (void)c89atomic_exchange_explicit_32(dst, src, order) - #define c89atomic_store_explicit_64(dst, src, order) (void)c89atomic_exchange_explicit_64(dst, src, order) - #define c89atomic_test_and_set_explicit_8( dst, order) c89atomic_exchange_explicit_8 (dst, 1, order) - #define c89atomic_test_and_set_explicit_16(dst, order) c89atomic_exchange_explicit_16(dst, 1, order) - #define c89atomic_test_and_set_explicit_32(dst, order) c89atomic_exchange_explicit_32(dst, 1, order) - #define c89atomic_test_and_set_explicit_64(dst, order) c89atomic_exchange_explicit_64(dst, 1, order) - #define c89atomic_clear_explicit_8( dst, order) c89atomic_store_explicit_8 (dst, 0, order) - #define c89atomic_clear_explicit_16(dst, order) c89atomic_store_explicit_16(dst, 0, order) - #define c89atomic_clear_explicit_32(dst, order) c89atomic_store_explicit_32(dst, 0, order) - #define c89atomic_clear_explicit_64(dst, order) c89atomic_store_explicit_64(dst, 0, order) - typedef c89atomic_uint8 c89atomic_flag; - #define c89atomic_flag_test_and_set_explicit(ptr, order) (c89atomic_bool)c89atomic_test_and_set_explicit_8(ptr, order) - #define c89atomic_flag_clear_explicit(ptr, order) c89atomic_clear_explicit_8(ptr, order) - #define c89atoimc_flag_load_explicit(ptr, order) c89atomic_load_explicit_8(ptr, order) + #define ma_atomic_store_explicit_8( dst, src, order) (void)ma_atomic_exchange_explicit_8 (dst, src, order) + #define ma_atomic_store_explicit_16(dst, src, order) (void)ma_atomic_exchange_explicit_16(dst, src, order) + #define ma_atomic_store_explicit_32(dst, src, order) (void)ma_atomic_exchange_explicit_32(dst, src, order) + #define ma_atomic_store_explicit_64(dst, src, order) (void)ma_atomic_exchange_explicit_64(dst, src, order) + #define ma_atomic_test_and_set_explicit_8( dst, order) ma_atomic_exchange_explicit_8 (dst, 1, order) + #define ma_atomic_test_and_set_explicit_16(dst, order) ma_atomic_exchange_explicit_16(dst, 1, order) + #define ma_atomic_test_and_set_explicit_32(dst, order) ma_atomic_exchange_explicit_32(dst, 1, order) + #define ma_atomic_test_and_set_explicit_64(dst, order) ma_atomic_exchange_explicit_64(dst, 1, order) + #define ma_atomic_clear_explicit_8( dst, order) ma_atomic_store_explicit_8 (dst, 0, order) + #define ma_atomic_clear_explicit_16(dst, order) ma_atomic_store_explicit_16(dst, 0, order) + #define ma_atomic_clear_explicit_32(dst, order) ma_atomic_store_explicit_32(dst, 0, order) + #define ma_atomic_clear_explicit_64(dst, order) ma_atomic_store_explicit_64(dst, 0, order) + typedef ma_uint8 ma_atomic_flag; + #define ma_atomic_flag_test_and_set_explicit(ptr, order) (ma_bool32)ma_atomic_test_and_set_explicit_8(ptr, order) + #define ma_atomic_flag_clear_explicit(ptr, order) ma_atomic_clear_explicit_8(ptr, order) + #define c89atoimc_flag_load_explicit(ptr, order) ma_atomic_load_explicit_8(ptr, order) #endif -#if !defined(C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) - #if defined(C89ATOMIC_HAS_8) - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8* expected, c89atomic_uint8 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) +#if !defined(MA_ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) + #if defined(MA_ATOMIC_HAS_8) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_8(volatile ma_uint8* dst, ma_uint8* expected, ma_uint8 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - c89atomic_uint8 expectedValue; - c89atomic_uint8 result; + ma_uint8 expectedValue; + ma_uint8 result; (void)successOrder; (void)failureOrder; - expectedValue = c89atomic_load_explicit_8(expected, c89atomic_memory_order_seq_cst); - result = c89atomic_compare_and_swap_8(dst, expectedValue, desired); + expectedValue = ma_atomic_load_explicit_8(expected, ma_atomic_memory_order_seq_cst); + result = ma_atomic_compare_and_swap_8(dst, expectedValue, desired); if (result == expectedValue) { return 1; } else { - c89atomic_store_explicit_8(expected, result, failureOrder); + ma_atomic_store_explicit_8(expected, result, failureOrder); return 0; } } #endif - #if defined(C89ATOMIC_HAS_16) - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16* expected, c89atomic_uint16 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + #if defined(MA_ATOMIC_HAS_16) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_16(volatile ma_uint16* dst, ma_uint16* expected, ma_uint16 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - c89atomic_uint16 expectedValue; - c89atomic_uint16 result; + ma_uint16 expectedValue; + ma_uint16 result; (void)successOrder; (void)failureOrder; - expectedValue = c89atomic_load_explicit_16(expected, c89atomic_memory_order_seq_cst); - result = c89atomic_compare_and_swap_16(dst, expectedValue, desired); + expectedValue = ma_atomic_load_explicit_16(expected, ma_atomic_memory_order_seq_cst); + result = ma_atomic_compare_and_swap_16(dst, expectedValue, desired); if (result == expectedValue) { return 1; } else { - c89atomic_store_explicit_16(expected, result, failureOrder); + ma_atomic_store_explicit_16(expected, result, failureOrder); return 0; } } #endif - #if defined(C89ATOMIC_HAS_32) - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32* expected, c89atomic_uint32 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + #if defined(MA_ATOMIC_HAS_32) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_32(volatile ma_uint32* dst, ma_uint32* expected, ma_uint32 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - c89atomic_uint32 expectedValue; - c89atomic_uint32 result; + ma_uint32 expectedValue; + ma_uint32 result; (void)successOrder; (void)failureOrder; - expectedValue = c89atomic_load_explicit_32(expected, c89atomic_memory_order_seq_cst); - result = c89atomic_compare_and_swap_32(dst, expectedValue, desired); + expectedValue = ma_atomic_load_explicit_32(expected, ma_atomic_memory_order_seq_cst); + result = ma_atomic_compare_and_swap_32(dst, expectedValue, desired); if (result == expectedValue) { return 1; } else { - c89atomic_store_explicit_32(expected, result, failureOrder); + ma_atomic_store_explicit_32(expected, result, failureOrder); return 0; } } #endif - #if defined(C89ATOMIC_HAS_64) - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_64(volatile c89atomic_uint64* dst, volatile c89atomic_uint64* expected, c89atomic_uint64 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + #if defined(MA_ATOMIC_HAS_64) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_64(volatile ma_uint64* dst, volatile ma_uint64* expected, ma_uint64 desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - c89atomic_uint64 expectedValue; - c89atomic_uint64 result; + ma_uint64 expectedValue; + ma_uint64 result; (void)successOrder; (void)failureOrder; - expectedValue = c89atomic_load_explicit_64(expected, c89atomic_memory_order_seq_cst); - result = c89atomic_compare_and_swap_64(dst, expectedValue, desired); + expectedValue = ma_atomic_load_explicit_64(expected, ma_atomic_memory_order_seq_cst); + result = ma_atomic_compare_and_swap_64(dst, expectedValue, desired); if (result == expectedValue) { return 1; } else { - c89atomic_store_explicit_64(expected, result, failureOrder); + ma_atomic_store_explicit_64(expected, result, failureOrder); return 0; } } #endif - #define c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) - #define c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8 (dst, expected, desired, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, successOrder, failureOrder) + #define ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, successOrder, failureOrder) #endif -#if !defined(C89ATOMIC_HAS_NATIVE_IS_LOCK_FREE) - static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_8(volatile void* ptr) +#if !defined(MA_ATOMIC_HAS_NATIVE_IS_LOCK_FREE) + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_8(volatile void* ptr) { (void)ptr; return 1; } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_16(volatile void* ptr) + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_16(volatile void* ptr) { (void)ptr; return 1; } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_32(volatile void* ptr) + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_32(volatile void* ptr) { (void)ptr; return 1; } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_64(volatile void* ptr) + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_64(volatile void* ptr) { (void)ptr; - #if defined(C89ATOMIC_64BIT) + #if defined(MA_64BIT) return 1; #else - #if defined(C89ATOMIC_X86) || defined(C89ATOMIC_X64) + #if defined(MA_X86) || defined(MA_X64) return 1; #else return 0; @@ -15451,432 +15458,432 @@ typedef unsigned char c89atomic_bool; #endif } #endif -#if defined(C89ATOMIC_64BIT) - static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_ptr(volatile void** ptr) +#if defined(MA_64BIT) + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) { - return c89atomic_is_lock_free_64((volatile c89atomic_uint64*)ptr); + return ma_atomic_is_lock_free_64((volatile ma_uint64*)ptr); } - static C89ATOMIC_INLINE void* c89atomic_load_explicit_ptr(volatile void** ptr, c89atomic_memory_order order) + static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order) { - return (void*)c89atomic_load_explicit_64((volatile c89atomic_uint64*)ptr, order); + return (void*)ma_atomic_load_explicit_64((volatile ma_uint64*)ptr, order); } - static C89ATOMIC_INLINE void c89atomic_store_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) + static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) { - c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order); + ma_atomic_store_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); } - static C89ATOMIC_INLINE void* c89atomic_exchange_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) + static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) { - return (void*)c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)src, order); + return (void*)ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, (ma_uint64)src, order); } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - return c89atomic_compare_exchange_strong_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder); + return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - return c89atomic_compare_exchange_weak_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder); + return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder); } - static C89ATOMIC_INLINE void* c89atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) + static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) { - return (void*)c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64)expected, (c89atomic_uint64)desired); + return (void*)ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)desired); } -#elif defined(C89ATOMIC_32BIT) - static C89ATOMIC_INLINE c89atomic_bool c89atomic_is_lock_free_ptr(volatile void** ptr) +#elif defined(MA_32BIT) + static MA_INLINE ma_bool32 ma_atomic_is_lock_free_ptr(volatile void** ptr) { - return c89atomic_is_lock_free_32((volatile c89atomic_uint32*)ptr); + return ma_atomic_is_lock_free_32((volatile ma_uint32*)ptr); } - static C89ATOMIC_INLINE void* c89atomic_load_explicit_ptr(volatile void** ptr, c89atomic_memory_order order) + static MA_INLINE void* ma_atomic_load_explicit_ptr(volatile void** ptr, ma_atomic_memory_order order) { - return (void*)c89atomic_load_explicit_32((volatile c89atomic_uint32*)ptr, order); + return (void*)ma_atomic_load_explicit_32((volatile ma_uint32*)ptr, order); } - static C89ATOMIC_INLINE void c89atomic_store_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) + static MA_INLINE void ma_atomic_store_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) { - c89atomic_store_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order); + ma_atomic_store_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); } - static C89ATOMIC_INLINE void* c89atomic_exchange_explicit_ptr(volatile void** dst, void* src, c89atomic_memory_order order) + static MA_INLINE void* ma_atomic_exchange_explicit_ptr(volatile void** dst, void* src, ma_atomic_memory_order order) { - return (void*)c89atomic_exchange_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)src, order); + return (void*)ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, (ma_uint32)src, order); } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - return c89atomic_compare_exchange_strong_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder); + return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); } - static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_ptr(volatile void** dst, void** expected, void* desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - return c89atomic_compare_exchange_weak_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder); + return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder); } - static C89ATOMIC_INLINE void* c89atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) + static MA_INLINE void* ma_atomic_compare_and_swap_ptr(volatile void** dst, void* expected, void* desired) { - return (void*)c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32)expected, (c89atomic_uint32)desired); + return (void*)ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)desired); } #else #error Unsupported architecture. #endif -#define c89atomic_flag_test_and_set(ptr) c89atomic_flag_test_and_set_explicit(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_flag_clear(ptr) c89atomic_flag_clear_explicit(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_store_ptr(dst, src) c89atomic_store_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst) -#define c89atomic_load_ptr(ptr) c89atomic_load_explicit_ptr((volatile void**)ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_ptr(dst, src) c89atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_ptr(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_ptr(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_8( ptr) c89atomic_test_and_set_explicit_8( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_16(ptr) c89atomic_test_and_set_explicit_16(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_32(ptr) c89atomic_test_and_set_explicit_32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_64(ptr) c89atomic_test_and_set_explicit_64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_8( ptr) c89atomic_clear_explicit_8( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_16(ptr) c89atomic_clear_explicit_16(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_32(ptr) c89atomic_clear_explicit_32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_64(ptr) c89atomic_clear_explicit_64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_store_8( dst, src) c89atomic_store_explicit_8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_16(dst, src) c89atomic_store_explicit_16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_32(dst, src) c89atomic_store_explicit_32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_64(dst, src) c89atomic_store_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_load_8( ptr) c89atomic_load_explicit_8( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_16(ptr) c89atomic_load_explicit_16(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_32(ptr) c89atomic_load_explicit_32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_64(ptr) c89atomic_load_explicit_64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_8( dst, src) c89atomic_exchange_explicit_8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_16(dst, src) c89atomic_exchange_explicit_16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_32(dst, src) c89atomic_exchange_explicit_32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_64(dst, src) c89atomic_exchange_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_8( dst, expected, desired) c89atomic_compare_exchange_strong_explicit_8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_16(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_32(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_64(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_8( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_16( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_32( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_64( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_8( dst, src) c89atomic_fetch_add_explicit_8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_16(dst, src) c89atomic_fetch_add_explicit_16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_32(dst, src) c89atomic_fetch_add_explicit_32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_64(dst, src) c89atomic_fetch_add_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_8( dst, src) c89atomic_fetch_sub_explicit_8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_16(dst, src) c89atomic_fetch_sub_explicit_16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_32(dst, src) c89atomic_fetch_sub_explicit_32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_64(dst, src) c89atomic_fetch_sub_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_8( dst, src) c89atomic_fetch_or_explicit_8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_16(dst, src) c89atomic_fetch_or_explicit_16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_32(dst, src) c89atomic_fetch_or_explicit_32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_64(dst, src) c89atomic_fetch_or_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_8( dst, src) c89atomic_fetch_xor_explicit_8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_16(dst, src) c89atomic_fetch_xor_explicit_16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_32(dst, src) c89atomic_fetch_xor_explicit_32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_64(dst, src) c89atomic_fetch_xor_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_8( dst, src) c89atomic_fetch_and_explicit_8 (dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_16(dst, src) c89atomic_fetch_and_explicit_16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_32(dst, src) c89atomic_fetch_and_explicit_32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_64(dst, src) c89atomic_fetch_and_explicit_64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_explicit_i8( ptr, order) (c89atomic_int8 )c89atomic_test_and_set_explicit_8( (c89atomic_uint8* )ptr, order) -#define c89atomic_test_and_set_explicit_i16(ptr, order) (c89atomic_int16)c89atomic_test_and_set_explicit_16((c89atomic_uint16*)ptr, order) -#define c89atomic_test_and_set_explicit_i32(ptr, order) (c89atomic_int32)c89atomic_test_and_set_explicit_32((c89atomic_uint32*)ptr, order) -#define c89atomic_test_and_set_explicit_i64(ptr, order) (c89atomic_int64)c89atomic_test_and_set_explicit_64((c89atomic_uint64*)ptr, order) -#define c89atomic_clear_explicit_i8( ptr, order) c89atomic_clear_explicit_8( (c89atomic_uint8* )ptr, order) -#define c89atomic_clear_explicit_i16(ptr, order) c89atomic_clear_explicit_16((c89atomic_uint16*)ptr, order) -#define c89atomic_clear_explicit_i32(ptr, order) c89atomic_clear_explicit_32((c89atomic_uint32*)ptr, order) -#define c89atomic_clear_explicit_i64(ptr, order) c89atomic_clear_explicit_64((c89atomic_uint64*)ptr, order) -#define c89atomic_store_explicit_i8( dst, src, order) c89atomic_store_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_store_explicit_i16(dst, src, order) c89atomic_store_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_store_explicit_i32(dst, src, order) c89atomic_store_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_store_explicit_i64(dst, src, order) c89atomic_store_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) -#define c89atomic_load_explicit_i8( ptr, order) (c89atomic_int8 )c89atomic_load_explicit_8( (c89atomic_uint8* )ptr, order) -#define c89atomic_load_explicit_i16(ptr, order) (c89atomic_int16)c89atomic_load_explicit_16((c89atomic_uint16*)ptr, order) -#define c89atomic_load_explicit_i32(ptr, order) (c89atomic_int32)c89atomic_load_explicit_32((c89atomic_uint32*)ptr, order) -#define c89atomic_load_explicit_i64(ptr, order) (c89atomic_int64)c89atomic_load_explicit_64((c89atomic_uint64*)ptr, order) -#define c89atomic_exchange_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_exchange_explicit_8 ((c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_exchange_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_exchange_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_exchange_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_exchange_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_exchange_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_exchange_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) -#define c89atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8* )expected, (c89atomic_uint8 )desired, successOrder, failureOrder) -#define c89atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16*)expected, (c89atomic_uint16)desired, successOrder, failureOrder) -#define c89atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder) -#define c89atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_strong_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder) -#define c89atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8* )expected, (c89atomic_uint8 )desired, successOrder, failureOrder) -#define c89atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16*)expected, (c89atomic_uint16)desired, successOrder, failureOrder) -#define c89atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32*)expected, (c89atomic_uint32)desired, successOrder, failureOrder) -#define c89atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, successOrder, failureOrder) c89atomic_compare_exchange_weak_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64*)expected, (c89atomic_uint64)desired, successOrder, failureOrder) -#define c89atomic_fetch_add_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_add_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_fetch_add_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_add_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_fetch_add_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_add_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_fetch_add_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_add_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) -#define c89atomic_fetch_sub_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_sub_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_fetch_sub_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_sub_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_fetch_sub_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_sub_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_fetch_sub_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_sub_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) -#define c89atomic_fetch_or_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_or_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_fetch_or_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_or_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_fetch_or_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_or_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_fetch_or_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_or_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) -#define c89atomic_fetch_xor_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_xor_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_fetch_xor_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_xor_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_fetch_xor_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_xor_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_fetch_xor_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_xor_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) -#define c89atomic_fetch_and_explicit_i8( dst, src, order) (c89atomic_int8 )c89atomic_fetch_and_explicit_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )src, order) -#define c89atomic_fetch_and_explicit_i16(dst, src, order) (c89atomic_int16)c89atomic_fetch_and_explicit_16((c89atomic_uint16*)dst, (c89atomic_uint16)src, order) -#define c89atomic_fetch_and_explicit_i32(dst, src, order) (c89atomic_int32)c89atomic_fetch_and_explicit_32((c89atomic_uint32*)dst, (c89atomic_uint32)src, order) -#define c89atomic_fetch_and_explicit_i64(dst, src, order) (c89atomic_int64)c89atomic_fetch_and_explicit_64((c89atomic_uint64*)dst, (c89atomic_uint64)src, order) -#define c89atomic_test_and_set_i8( ptr) c89atomic_test_and_set_explicit_i8( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_i16(ptr) c89atomic_test_and_set_explicit_i16(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_i32(ptr) c89atomic_test_and_set_explicit_i32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_test_and_set_i64(ptr) c89atomic_test_and_set_explicit_i64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_i8( ptr) c89atomic_clear_explicit_i8( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_i16(ptr) c89atomic_clear_explicit_i16(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_i32(ptr) c89atomic_clear_explicit_i32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_i64(ptr) c89atomic_clear_explicit_i64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_store_i8( dst, src) c89atomic_store_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_i16(dst, src) c89atomic_store_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_i32(dst, src) c89atomic_store_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_i64(dst, src) c89atomic_store_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_load_i8( ptr) c89atomic_load_explicit_i8( ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_i16(ptr) c89atomic_load_explicit_i16(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_i32(ptr) c89atomic_load_explicit_i32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_i64(ptr) c89atomic_load_explicit_i64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_i8( dst, src) c89atomic_exchange_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_i16(dst, src) c89atomic_exchange_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_i32(dst, src) c89atomic_exchange_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_i64(dst, src) c89atomic_exchange_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_i8( dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_i16(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_i32(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_i64(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_i8( dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_i16(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_i32(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_i64(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_i8( dst, src) c89atomic_fetch_add_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_i16(dst, src) c89atomic_fetch_add_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_i32(dst, src) c89atomic_fetch_add_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_i64(dst, src) c89atomic_fetch_add_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_i8( dst, src) c89atomic_fetch_sub_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_i16(dst, src) c89atomic_fetch_sub_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_i32(dst, src) c89atomic_fetch_sub_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_i64(dst, src) c89atomic_fetch_sub_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_i8( dst, src) c89atomic_fetch_or_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_i16(dst, src) c89atomic_fetch_or_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_i32(dst, src) c89atomic_fetch_or_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_i64(dst, src) c89atomic_fetch_or_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_i8( dst, src) c89atomic_fetch_xor_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_i16(dst, src) c89atomic_fetch_xor_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_i32(dst, src) c89atomic_fetch_xor_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_i64(dst, src) c89atomic_fetch_xor_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_i8( dst, src) c89atomic_fetch_and_explicit_i8( dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_i16(dst, src) c89atomic_fetch_and_explicit_i16(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_i32(dst, src) c89atomic_fetch_and_explicit_i32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_i64(dst, src) c89atomic_fetch_and_explicit_i64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_and_swap_i8( dst, expected, dedsired) (c89atomic_int8 )c89atomic_compare_and_swap_8( (c89atomic_uint8* )dst, (c89atomic_uint8 )expected, (c89atomic_uint8 )dedsired) -#define c89atomic_compare_and_swap_i16(dst, expected, dedsired) (c89atomic_int16)c89atomic_compare_and_swap_16((c89atomic_uint16*)dst, (c89atomic_uint16)expected, (c89atomic_uint16)dedsired) -#define c89atomic_compare_and_swap_i32(dst, expected, dedsired) (c89atomic_int32)c89atomic_compare_and_swap_32((c89atomic_uint32*)dst, (c89atomic_uint32)expected, (c89atomic_uint32)dedsired) -#define c89atomic_compare_and_swap_i64(dst, expected, dedsired) (c89atomic_int64)c89atomic_compare_and_swap_64((c89atomic_uint64*)dst, (c89atomic_uint64)expected, (c89atomic_uint64)dedsired) +#define ma_atomic_flag_test_and_set(ptr) ma_atomic_flag_test_and_set_explicit(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_flag_clear(ptr) ma_atomic_flag_clear_explicit(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_ptr(dst, src) ma_atomic_store_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_ptr(ptr) ma_atomic_load_explicit_ptr((volatile void**)ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_ptr(dst, src) ma_atomic_exchange_explicit_ptr((volatile void**)dst, (void*)src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_ptr(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_ptr(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_ptr((volatile void**)dst, (void**)expected, (void*)desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_8( ptr) ma_atomic_test_and_set_explicit_8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_16(ptr) ma_atomic_test_and_set_explicit_16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_32(ptr) ma_atomic_test_and_set_explicit_32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_64(ptr) ma_atomic_test_and_set_explicit_64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_8( ptr) ma_atomic_clear_explicit_8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_16(ptr) ma_atomic_clear_explicit_16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_32(ptr) ma_atomic_clear_explicit_32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_64(ptr) ma_atomic_clear_explicit_64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_8( dst, src) ma_atomic_store_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_16(dst, src) ma_atomic_store_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_32(dst, src) ma_atomic_store_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_64(dst, src) ma_atomic_store_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_8( ptr) ma_atomic_load_explicit_8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_16(ptr) ma_atomic_load_explicit_16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_32(ptr) ma_atomic_load_explicit_32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_64(ptr) ma_atomic_load_explicit_64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_8( dst, src) ma_atomic_exchange_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_16(dst, src) ma_atomic_exchange_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_32(dst, src) ma_atomic_exchange_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_64(dst, src) ma_atomic_exchange_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_16( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_32( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_64( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_8( dst, src) ma_atomic_fetch_add_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_16(dst, src) ma_atomic_fetch_add_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_32(dst, src) ma_atomic_fetch_add_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_64(dst, src) ma_atomic_fetch_add_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_8( dst, src) ma_atomic_fetch_sub_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_16(dst, src) ma_atomic_fetch_sub_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_32(dst, src) ma_atomic_fetch_sub_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_64(dst, src) ma_atomic_fetch_sub_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_8( dst, src) ma_atomic_fetch_or_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_16(dst, src) ma_atomic_fetch_or_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_32(dst, src) ma_atomic_fetch_or_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_64(dst, src) ma_atomic_fetch_or_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_8( dst, src) ma_atomic_fetch_xor_explicit_8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_16(dst, src) ma_atomic_fetch_xor_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_32(dst, src) ma_atomic_fetch_xor_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_64(dst, src) ma_atomic_fetch_xor_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_8( dst, src) ma_atomic_fetch_and_explicit_8 (dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_16(dst, src) ma_atomic_fetch_and_explicit_16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_32(dst, src) ma_atomic_fetch_and_explicit_32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_64(dst, src) ma_atomic_fetch_and_explicit_64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_explicit_i8( ptr, order) (ma_int8 )ma_atomic_test_and_set_explicit_8( (ma_uint8* )ptr, order) +#define ma_atomic_test_and_set_explicit_i16(ptr, order) (ma_int16)ma_atomic_test_and_set_explicit_16((ma_uint16*)ptr, order) +#define ma_atomic_test_and_set_explicit_i32(ptr, order) (ma_int32)ma_atomic_test_and_set_explicit_32((ma_uint32*)ptr, order) +#define ma_atomic_test_and_set_explicit_i64(ptr, order) (ma_int64)ma_atomic_test_and_set_explicit_64((ma_uint64*)ptr, order) +#define ma_atomic_clear_explicit_i8( ptr, order) ma_atomic_clear_explicit_8( (ma_uint8* )ptr, order) +#define ma_atomic_clear_explicit_i16(ptr, order) ma_atomic_clear_explicit_16((ma_uint16*)ptr, order) +#define ma_atomic_clear_explicit_i32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) +#define ma_atomic_clear_explicit_i64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) +#define ma_atomic_store_explicit_i8( dst, src, order) ma_atomic_store_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_store_explicit_i16(dst, src, order) ma_atomic_store_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_store_explicit_i32(dst, src, order) ma_atomic_store_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_store_explicit_i64(dst, src, order) ma_atomic_store_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_load_explicit_i8( ptr, order) (ma_int8 )ma_atomic_load_explicit_8( (ma_uint8* )ptr, order) +#define ma_atomic_load_explicit_i16(ptr, order) (ma_int16)ma_atomic_load_explicit_16((ma_uint16*)ptr, order) +#define ma_atomic_load_explicit_i32(ptr, order) (ma_int32)ma_atomic_load_explicit_32((ma_uint32*)ptr, order) +#define ma_atomic_load_explicit_i64(ptr, order) (ma_int64)ma_atomic_load_explicit_64((ma_uint64*)ptr, order) +#define ma_atomic_exchange_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_exchange_explicit_8 ((ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_exchange_explicit_i16(dst, src, order) (ma_int16)ma_atomic_exchange_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_exchange_explicit_i32(dst, src, order) (ma_int32)ma_atomic_exchange_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_exchange_explicit_i64(dst, src, order) (ma_int64)ma_atomic_exchange_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_strong_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_8( (ma_uint8* )dst, (ma_uint8* )expected, (ma_uint8 )desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_16((ma_uint16*)dst, (ma_uint16*)expected, (ma_uint16)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_32((ma_uint32*)dst, (ma_uint32*)expected, (ma_uint32)desired, successOrder, failureOrder) +#define ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, successOrder, failureOrder) ma_atomic_compare_exchange_weak_explicit_64((ma_uint64*)dst, (ma_uint64*)expected, (ma_uint64)desired, successOrder, failureOrder) +#define ma_atomic_fetch_add_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_add_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_fetch_add_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_add_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_fetch_add_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_add_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_fetch_add_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_add_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_fetch_sub_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_sub_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_fetch_sub_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_sub_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_fetch_sub_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_sub_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_fetch_sub_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_sub_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_fetch_or_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_or_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_fetch_or_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_or_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_fetch_or_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_or_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_fetch_or_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_or_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_fetch_xor_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_xor_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_fetch_xor_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_xor_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_fetch_xor_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_xor_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_fetch_xor_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_xor_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_fetch_and_explicit_i8( dst, src, order) (ma_int8 )ma_atomic_fetch_and_explicit_8( (ma_uint8* )dst, (ma_uint8 )src, order) +#define ma_atomic_fetch_and_explicit_i16(dst, src, order) (ma_int16)ma_atomic_fetch_and_explicit_16((ma_uint16*)dst, (ma_uint16)src, order) +#define ma_atomic_fetch_and_explicit_i32(dst, src, order) (ma_int32)ma_atomic_fetch_and_explicit_32((ma_uint32*)dst, (ma_uint32)src, order) +#define ma_atomic_fetch_and_explicit_i64(dst, src, order) (ma_int64)ma_atomic_fetch_and_explicit_64((ma_uint64*)dst, (ma_uint64)src, order) +#define ma_atomic_test_and_set_i8( ptr) ma_atomic_test_and_set_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_i16(ptr) ma_atomic_test_and_set_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_i32(ptr) ma_atomic_test_and_set_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_test_and_set_i64(ptr) ma_atomic_test_and_set_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_i8( ptr) ma_atomic_clear_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_i16(ptr) ma_atomic_clear_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_i32(ptr) ma_atomic_clear_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_i64(ptr) ma_atomic_clear_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_i8( dst, src) ma_atomic_store_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_i16(dst, src) ma_atomic_store_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_i32(dst, src) ma_atomic_store_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_i64(dst, src) ma_atomic_store_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_i8( ptr) ma_atomic_load_explicit_i8( ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_i16(ptr) ma_atomic_load_explicit_i16(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_i32(ptr) ma_atomic_load_explicit_i32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_i64(ptr) ma_atomic_load_explicit_i64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_i8( dst, src) ma_atomic_exchange_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_i16(dst, src) ma_atomic_exchange_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_i32(dst, src) ma_atomic_exchange_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_i64(dst, src) ma_atomic_exchange_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i8( dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i16(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_i64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i8( dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i8( dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i16(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i16(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_i64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_i64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_i8( dst, src) ma_atomic_fetch_add_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_i16(dst, src) ma_atomic_fetch_add_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_i32(dst, src) ma_atomic_fetch_add_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_i64(dst, src) ma_atomic_fetch_add_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_i8( dst, src) ma_atomic_fetch_sub_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_i16(dst, src) ma_atomic_fetch_sub_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_i32(dst, src) ma_atomic_fetch_sub_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_i64(dst, src) ma_atomic_fetch_sub_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_i8( dst, src) ma_atomic_fetch_or_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_i16(dst, src) ma_atomic_fetch_or_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_i32(dst, src) ma_atomic_fetch_or_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_i64(dst, src) ma_atomic_fetch_or_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_i8( dst, src) ma_atomic_fetch_xor_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_i16(dst, src) ma_atomic_fetch_xor_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_i32(dst, src) ma_atomic_fetch_xor_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_i64(dst, src) ma_atomic_fetch_xor_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_i8( dst, src) ma_atomic_fetch_and_explicit_i8( dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_i16(dst, src) ma_atomic_fetch_and_explicit_i16(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_i32(dst, src) ma_atomic_fetch_and_explicit_i32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_i64(dst, src) ma_atomic_fetch_and_explicit_i64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_and_swap_i8( dst, expected, dedsired) (ma_int8 )ma_atomic_compare_and_swap_8( (ma_uint8* )dst, (ma_uint8 )expected, (ma_uint8 )dedsired) +#define ma_atomic_compare_and_swap_i16(dst, expected, dedsired) (ma_int16)ma_atomic_compare_and_swap_16((ma_uint16*)dst, (ma_uint16)expected, (ma_uint16)dedsired) +#define ma_atomic_compare_and_swap_i32(dst, expected, dedsired) (ma_int32)ma_atomic_compare_and_swap_32((ma_uint32*)dst, (ma_uint32)expected, (ma_uint32)dedsired) +#define ma_atomic_compare_and_swap_i64(dst, expected, dedsired) (ma_int64)ma_atomic_compare_and_swap_64((ma_uint64*)dst, (ma_uint64)expected, (ma_uint64)dedsired) typedef union { - c89atomic_uint32 i; + ma_uint32 i; float f; -} c89atomic_if32; +} ma_atomic_if32; typedef union { - c89atomic_uint64 i; + ma_uint64 i; double f; -} c89atomic_if64; -#define c89atomic_clear_explicit_f32(ptr, order) c89atomic_clear_explicit_32((c89atomic_uint32*)ptr, order) -#define c89atomic_clear_explicit_f64(ptr, order) c89atomic_clear_explicit_64((c89atomic_uint64*)ptr, order) -static C89ATOMIC_INLINE void c89atomic_store_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) +} ma_atomic_if64; +#define ma_atomic_clear_explicit_f32(ptr, order) ma_atomic_clear_explicit_32((ma_uint32*)ptr, order) +#define ma_atomic_clear_explicit_f64(ptr, order) ma_atomic_clear_explicit_64((ma_uint64*)ptr, order) +static MA_INLINE void ma_atomic_store_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) { - c89atomic_if32 x; + ma_atomic_if32 x; x.f = src; - c89atomic_store_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); + ma_atomic_store_explicit_32((volatile ma_uint32*)dst, x.i, order); } -static C89ATOMIC_INLINE void c89atomic_store_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) +static MA_INLINE void ma_atomic_store_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) { - c89atomic_if64 x; + ma_atomic_if64 x; x.f = src; - c89atomic_store_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); + ma_atomic_store_explicit_64((volatile ma_uint64*)dst, x.i, order); } -static C89ATOMIC_INLINE float c89atomic_load_explicit_f32(volatile const float* ptr, c89atomic_memory_order order) +static MA_INLINE float ma_atomic_load_explicit_f32(volatile const float* ptr, ma_atomic_memory_order order) { - c89atomic_if32 r; - r.i = c89atomic_load_explicit_32((volatile const c89atomic_uint32*)ptr, order); + ma_atomic_if32 r; + r.i = ma_atomic_load_explicit_32((volatile const ma_uint32*)ptr, order); return r.f; } -static C89ATOMIC_INLINE double c89atomic_load_explicit_f64(volatile const double* ptr, c89atomic_memory_order order) +static MA_INLINE double ma_atomic_load_explicit_f64(volatile const double* ptr, ma_atomic_memory_order order) { - c89atomic_if64 r; - r.i = c89atomic_load_explicit_64((volatile const c89atomic_uint64*)ptr, order); + ma_atomic_if64 r; + r.i = ma_atomic_load_explicit_64((volatile const ma_uint64*)ptr, order); return r.f; } -static C89ATOMIC_INLINE float c89atomic_exchange_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) +static MA_INLINE float ma_atomic_exchange_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) { - c89atomic_if32 r; - c89atomic_if32 x; + ma_atomic_if32 r; + ma_atomic_if32 x; x.f = src; - r.i = c89atomic_exchange_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); + r.i = ma_atomic_exchange_explicit_32((volatile ma_uint32*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE double c89atomic_exchange_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) +static MA_INLINE double ma_atomic_exchange_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) { - c89atomic_if64 r; - c89atomic_if64 x; + ma_atomic_if64 r; + ma_atomic_if64 x; x.f = src; - r.i = c89atomic_exchange_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); + r.i = ma_atomic_exchange_explicit_64((volatile ma_uint64*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - c89atomic_if32 d; + ma_atomic_if32 d; d.f = desired; - return c89atomic_compare_exchange_strong_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, d.i, successOrder, failureOrder); + return ma_atomic_compare_exchange_strong_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); } -static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_strong_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - c89atomic_if64 d; + ma_atomic_if64 d; d.f = desired; - return c89atomic_compare_exchange_strong_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, d.i, successOrder, failureOrder); + return ma_atomic_compare_exchange_strong_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); } -static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f32(volatile float* dst, float* expected, float desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - c89atomic_if32 d; + ma_atomic_if32 d; d.f = desired; - return c89atomic_compare_exchange_weak_explicit_32((volatile c89atomic_uint32*)dst, (c89atomic_uint32*)expected, d.i, successOrder, failureOrder); + return ma_atomic_compare_exchange_weak_explicit_32((volatile ma_uint32*)dst, (ma_uint32*)expected, d.i, successOrder, failureOrder); } -static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) +static MA_INLINE ma_bool32 ma_atomic_compare_exchange_weak_explicit_f64(volatile double* dst, double* expected, double desired, ma_atomic_memory_order successOrder, ma_atomic_memory_order failureOrder) { - c89atomic_if64 d; + ma_atomic_if64 d; d.f = desired; - return c89atomic_compare_exchange_weak_explicit_64((volatile c89atomic_uint64*)dst, (c89atomic_uint64*)expected, d.i, successOrder, failureOrder); + return ma_atomic_compare_exchange_weak_explicit_64((volatile ma_uint64*)dst, (ma_uint64*)expected, d.i, successOrder, failureOrder); } -static C89ATOMIC_INLINE float c89atomic_fetch_add_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) +static MA_INLINE float ma_atomic_fetch_add_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) { - c89atomic_if32 r; - c89atomic_if32 x; + ma_atomic_if32 r; + ma_atomic_if32 x; x.f = src; - r.i = c89atomic_fetch_add_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); + r.i = ma_atomic_fetch_add_explicit_32((volatile ma_uint32*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE double c89atomic_fetch_add_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) +static MA_INLINE double ma_atomic_fetch_add_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) { - c89atomic_if64 r; - c89atomic_if64 x; + ma_atomic_if64 r; + ma_atomic_if64 x; x.f = src; - r.i = c89atomic_fetch_add_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); + r.i = ma_atomic_fetch_add_explicit_64((volatile ma_uint64*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE float c89atomic_fetch_sub_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) +static MA_INLINE float ma_atomic_fetch_sub_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) { - c89atomic_if32 r; - c89atomic_if32 x; + ma_atomic_if32 r; + ma_atomic_if32 x; x.f = src; - r.i = c89atomic_fetch_sub_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); + r.i = ma_atomic_fetch_sub_explicit_32((volatile ma_uint32*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE double c89atomic_fetch_sub_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) +static MA_INLINE double ma_atomic_fetch_sub_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) { - c89atomic_if64 r; - c89atomic_if64 x; + ma_atomic_if64 r; + ma_atomic_if64 x; x.f = src; - r.i = c89atomic_fetch_sub_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); + r.i = ma_atomic_fetch_sub_explicit_64((volatile ma_uint64*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE float c89atomic_fetch_or_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) +static MA_INLINE float ma_atomic_fetch_or_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) { - c89atomic_if32 r; - c89atomic_if32 x; + ma_atomic_if32 r; + ma_atomic_if32 x; x.f = src; - r.i = c89atomic_fetch_or_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); + r.i = ma_atomic_fetch_or_explicit_32((volatile ma_uint32*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE double c89atomic_fetch_or_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) +static MA_INLINE double ma_atomic_fetch_or_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) { - c89atomic_if64 r; - c89atomic_if64 x; + ma_atomic_if64 r; + ma_atomic_if64 x; x.f = src; - r.i = c89atomic_fetch_or_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); + r.i = ma_atomic_fetch_or_explicit_64((volatile ma_uint64*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE float c89atomic_fetch_xor_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) +static MA_INLINE float ma_atomic_fetch_xor_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) { - c89atomic_if32 r; - c89atomic_if32 x; + ma_atomic_if32 r; + ma_atomic_if32 x; x.f = src; - r.i = c89atomic_fetch_xor_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); + r.i = ma_atomic_fetch_xor_explicit_32((volatile ma_uint32*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE double c89atomic_fetch_xor_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) +static MA_INLINE double ma_atomic_fetch_xor_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) { - c89atomic_if64 r; - c89atomic_if64 x; + ma_atomic_if64 r; + ma_atomic_if64 x; x.f = src; - r.i = c89atomic_fetch_xor_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); + r.i = ma_atomic_fetch_xor_explicit_64((volatile ma_uint64*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE float c89atomic_fetch_and_explicit_f32(volatile float* dst, float src, c89atomic_memory_order order) +static MA_INLINE float ma_atomic_fetch_and_explicit_f32(volatile float* dst, float src, ma_atomic_memory_order order) { - c89atomic_if32 r; - c89atomic_if32 x; + ma_atomic_if32 r; + ma_atomic_if32 x; x.f = src; - r.i = c89atomic_fetch_and_explicit_32((volatile c89atomic_uint32*)dst, x.i, order); + r.i = ma_atomic_fetch_and_explicit_32((volatile ma_uint32*)dst, x.i, order); return r.f; } -static C89ATOMIC_INLINE double c89atomic_fetch_and_explicit_f64(volatile double* dst, double src, c89atomic_memory_order order) +static MA_INLINE double ma_atomic_fetch_and_explicit_f64(volatile double* dst, double src, ma_atomic_memory_order order) { - c89atomic_if64 r; - c89atomic_if64 x; + ma_atomic_if64 r; + ma_atomic_if64 x; x.f = src; - r.i = c89atomic_fetch_and_explicit_64((volatile c89atomic_uint64*)dst, x.i, order); + r.i = ma_atomic_fetch_and_explicit_64((volatile ma_uint64*)dst, x.i, order); return r.f; } -#define c89atomic_clear_f32(ptr) (float )c89atomic_clear_explicit_f32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_clear_f64(ptr) (double)c89atomic_clear_explicit_f64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_store_f32(dst, src) c89atomic_store_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_store_f64(dst, src) c89atomic_store_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_load_f32(ptr) (float )c89atomic_load_explicit_f32(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_load_f64(ptr) (double)c89atomic_load_explicit_f64(ptr, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_f32(dst, src) (float )c89atomic_exchange_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_exchange_f64(dst, src) (double)c89atomic_exchange_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_f32(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_f32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_strong_f64(dst, expected, desired) c89atomic_compare_exchange_strong_explicit_f64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_f32(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_f32(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_compare_exchange_weak_f64(dst, expected, desired) c89atomic_compare_exchange_weak_explicit_f64(dst, expected, desired, c89atomic_memory_order_seq_cst, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_f32(dst, src) c89atomic_fetch_add_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_add_f64(dst, src) c89atomic_fetch_add_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_f32(dst, src) c89atomic_fetch_sub_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_sub_f64(dst, src) c89atomic_fetch_sub_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_f32(dst, src) c89atomic_fetch_or_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_or_f64(dst, src) c89atomic_fetch_or_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_f32(dst, src) c89atomic_fetch_xor_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_xor_f64(dst, src) c89atomic_fetch_xor_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_f32(dst, src) c89atomic_fetch_and_explicit_f32(dst, src, c89atomic_memory_order_seq_cst) -#define c89atomic_fetch_and_f64(dst, src) c89atomic_fetch_and_explicit_f64(dst, src, c89atomic_memory_order_seq_cst) -static C89ATOMIC_INLINE float c89atomic_compare_and_swap_f32(volatile float* dst, float expected, float desired) +#define ma_atomic_clear_f32(ptr) (float )ma_atomic_clear_explicit_f32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_clear_f64(ptr) (double)ma_atomic_clear_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_f32(dst, src) ma_atomic_store_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_store_f64(dst, src) ma_atomic_store_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_f32(ptr) (float )ma_atomic_load_explicit_f32(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_load_f64(ptr) (double)ma_atomic_load_explicit_f64(ptr, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_f32(dst, src) (float )ma_atomic_exchange_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_exchange_f64(dst, src) (double)ma_atomic_exchange_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_f32(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_strong_f64(dst, expected, desired) ma_atomic_compare_exchange_strong_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_f32(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f32(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_compare_exchange_weak_f64(dst, expected, desired) ma_atomic_compare_exchange_weak_explicit_f64(dst, expected, desired, ma_atomic_memory_order_seq_cst, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_f32(dst, src) ma_atomic_fetch_add_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_add_f64(dst, src) ma_atomic_fetch_add_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_f32(dst, src) ma_atomic_fetch_sub_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_sub_f64(dst, src) ma_atomic_fetch_sub_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_f32(dst, src) ma_atomic_fetch_or_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_or_f64(dst, src) ma_atomic_fetch_or_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_f32(dst, src) ma_atomic_fetch_xor_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_xor_f64(dst, src) ma_atomic_fetch_xor_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_f32(dst, src) ma_atomic_fetch_and_explicit_f32(dst, src, ma_atomic_memory_order_seq_cst) +#define ma_atomic_fetch_and_f64(dst, src) ma_atomic_fetch_and_explicit_f64(dst, src, ma_atomic_memory_order_seq_cst) +static MA_INLINE float ma_atomic_compare_and_swap_f32(volatile float* dst, float expected, float desired) { - c89atomic_if32 r; - c89atomic_if32 e, d; + ma_atomic_if32 r; + ma_atomic_if32 e, d; e.f = expected; d.f = desired; - r.i = c89atomic_compare_and_swap_32((volatile c89atomic_uint32*)dst, e.i, d.i); + r.i = ma_atomic_compare_and_swap_32((volatile ma_uint32*)dst, e.i, d.i); return r.f; } -static C89ATOMIC_INLINE double c89atomic_compare_and_swap_f64(volatile double* dst, double expected, double desired) +static MA_INLINE double ma_atomic_compare_and_swap_f64(volatile double* dst, double expected, double desired) { - c89atomic_if64 r; - c89atomic_if64 e, d; + ma_atomic_if64 r; + ma_atomic_if64 e, d; e.f = expected; d.f = desired; - r.i = c89atomic_compare_and_swap_64((volatile c89atomic_uint64*)dst, e.i, d.i); + r.i = ma_atomic_compare_and_swap_64((volatile ma_uint64*)dst, e.i, d.i); return r.f; } -typedef c89atomic_flag c89atomic_spinlock; -static C89ATOMIC_INLINE void c89atomic_spinlock_lock(volatile c89atomic_spinlock* pSpinlock) +typedef ma_atomic_flag ma_atomic_spinlock; +static MA_INLINE void ma_atomic_spinlock_lock(volatile ma_atomic_spinlock* pSpinlock) { for (;;) { - if (c89atomic_flag_test_and_set_explicit(pSpinlock, c89atomic_memory_order_acquire) == 0) { + if (ma_atomic_flag_test_and_set_explicit(pSpinlock, ma_atomic_memory_order_acquire) == 0) { break; } - while (c89atoimc_flag_load_explicit(pSpinlock, c89atomic_memory_order_relaxed) == 1) { + while (c89atoimc_flag_load_explicit(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { } } } -static C89ATOMIC_INLINE void c89atomic_spinlock_unlock(volatile c89atomic_spinlock* pSpinlock) +static MA_INLINE void ma_atomic_spinlock_unlock(volatile ma_atomic_spinlock* pSpinlock) { - c89atomic_flag_clear_explicit(pSpinlock, c89atomic_memory_order_release); + ma_atomic_flag_clear_explicit(pSpinlock, ma_atomic_memory_order_release); } #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic pop @@ -15885,70 +15892,70 @@ static C89ATOMIC_INLINE void c89atomic_spinlock_unlock(volatile c89atomic_spinlo } #endif #endif -/* c89atomic.h end */ +/* ma_atomic.h end */ #define MA_ATOMIC_SAFE_TYPE_IMPL(c89TypeExtension, type) \ static MA_INLINE ma_##type ma_atomic_##type##_get(ma_atomic_##type* x) \ { \ - return (ma_##type)c89atomic_load_##c89TypeExtension(&x->value); \ + return (ma_##type)ma_atomic_load_##c89TypeExtension(&x->value); \ } \ static MA_INLINE void ma_atomic_##type##_set(ma_atomic_##type* x, ma_##type value) \ { \ - c89atomic_store_##c89TypeExtension(&x->value, value); \ + ma_atomic_store_##c89TypeExtension(&x->value, value); \ } \ static MA_INLINE ma_##type ma_atomic_##type##_exchange(ma_atomic_##type* x, ma_##type value) \ { \ - return (ma_##type)c89atomic_exchange_##c89TypeExtension(&x->value, value); \ + return (ma_##type)ma_atomic_exchange_##c89TypeExtension(&x->value, value); \ } \ static MA_INLINE ma_bool32 ma_atomic_##type##_compare_exchange(ma_atomic_##type* x, ma_##type* expected, ma_##type desired) \ { \ - return c89atomic_compare_exchange_weak_##c89TypeExtension(&x->value, expected, desired); \ + return ma_atomic_compare_exchange_weak_##c89TypeExtension(&x->value, expected, desired); \ } \ static MA_INLINE ma_##type ma_atomic_##type##_fetch_add(ma_atomic_##type* x, ma_##type y) \ { \ - return (ma_##type)c89atomic_fetch_add_##c89TypeExtension(&x->value, y); \ + return (ma_##type)ma_atomic_fetch_add_##c89TypeExtension(&x->value, y); \ } \ static MA_INLINE ma_##type ma_atomic_##type##_fetch_sub(ma_atomic_##type* x, ma_##type y) \ { \ - return (ma_##type)c89atomic_fetch_sub_##c89TypeExtension(&x->value, y); \ + return (ma_##type)ma_atomic_fetch_sub_##c89TypeExtension(&x->value, y); \ } \ static MA_INLINE ma_##type ma_atomic_##type##_fetch_or(ma_atomic_##type* x, ma_##type y) \ { \ - return (ma_##type)c89atomic_fetch_or_##c89TypeExtension(&x->value, y); \ + return (ma_##type)ma_atomic_fetch_or_##c89TypeExtension(&x->value, y); \ } \ static MA_INLINE ma_##type ma_atomic_##type##_fetch_xor(ma_atomic_##type* x, ma_##type y) \ { \ - return (ma_##type)c89atomic_fetch_xor_##c89TypeExtension(&x->value, y); \ + return (ma_##type)ma_atomic_fetch_xor_##c89TypeExtension(&x->value, y); \ } \ static MA_INLINE ma_##type ma_atomic_##type##_fetch_and(ma_atomic_##type* x, ma_##type y) \ { \ - return (ma_##type)c89atomic_fetch_and_##c89TypeExtension(&x->value, y); \ + return (ma_##type)ma_atomic_fetch_and_##c89TypeExtension(&x->value, y); \ } \ static MA_INLINE ma_##type ma_atomic_##type##_compare_and_swap(ma_atomic_##type* x, ma_##type expected, ma_##type desired) \ { \ - return (ma_##type)c89atomic_compare_and_swap_##c89TypeExtension(&x->value, expected, desired); \ + return (ma_##type)ma_atomic_compare_and_swap_##c89TypeExtension(&x->value, expected, desired); \ } \ #define MA_ATOMIC_SAFE_TYPE_IMPL_PTR(type) \ static MA_INLINE ma_##type* ma_atomic_ptr_##type##_get(ma_atomic_ptr_##type* x) \ { \ - return c89atomic_load_ptr((void**)&x->value); \ + return ma_atomic_load_ptr((void**)&x->value); \ } \ static MA_INLINE void ma_atomic_ptr_##type##_set(ma_atomic_ptr_##type* x, ma_##type* value) \ { \ - c89atomic_store_ptr((void**)&x->value, (void*)value); \ + ma_atomic_store_ptr((void**)&x->value, (void*)value); \ } \ static MA_INLINE ma_##type* ma_atomic_ptr_##type##_exchange(ma_atomic_ptr_##type* x, ma_##type* value) \ { \ - return c89atomic_exchange_ptr((void**)&x->value, (void*)value); \ + return ma_atomic_exchange_ptr((void**)&x->value, (void*)value); \ } \ static MA_INLINE ma_bool32 ma_atomic_ptr_##type##_compare_exchange(ma_atomic_ptr_##type* x, ma_##type** expected, ma_##type* desired) \ { \ - return c89atomic_compare_exchange_weak_ptr((void**)&x->value, (void*)expected, (void*)desired); \ + return ma_atomic_compare_exchange_weak_ptr((void**)&x->value, (void*)expected, (void*)desired); \ } \ static MA_INLINE ma_##type* ma_atomic_ptr_##type##_compare_and_swap(ma_atomic_ptr_##type* x, ma_##type* expected, ma_##type* desired) \ { \ - return (ma_##type*)c89atomic_compare_and_swap_ptr((void**)&x->value, (void*)expected, (void*)desired); \ + return (ma_##type*)ma_atomic_compare_and_swap_ptr((void**)&x->value, (void*)expected, (void*)desired); \ } \ MA_ATOMIC_SAFE_TYPE_IMPL(32, uint32) @@ -16031,11 +16038,11 @@ static MA_INLINE ma_result ma_spinlock_lock_ex(volatile ma_spinlock* pSpinlock, } for (;;) { - if (c89atomic_exchange_explicit_32(pSpinlock, 1, c89atomic_memory_order_acquire) == 0) { + if (ma_atomic_exchange_explicit_32(pSpinlock, 1, ma_atomic_memory_order_acquire) == 0) { break; } - while (c89atomic_load_explicit_32(pSpinlock, c89atomic_memory_order_relaxed) == 1) { + while (ma_atomic_load_explicit_32(pSpinlock, ma_atomic_memory_order_relaxed) == 1) { if (yield) { ma_yield(); } @@ -16061,7 +16068,7 @@ MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock) return MA_INVALID_ARGS; } - c89atomic_store_explicit_32(pSpinlock, 0, c89atomic_memory_order_release); + ma_atomic_store_explicit_32(pSpinlock, 0, ma_atomic_memory_order_release); return MA_SUCCESS; } @@ -16808,7 +16815,7 @@ MA_API ma_result ma_fence_acquire(ma_fence* pFence) } for (;;) { - ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter); + ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter); ma_uint32 newCounter = oldCounter + 1; /* Make sure we're not about to exceed our maximum value. */ @@ -16817,7 +16824,7 @@ MA_API ma_result ma_fence_acquire(ma_fence* pFence) return MA_OUT_OF_RANGE; } - if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { + if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { return MA_SUCCESS; } else { if (oldCounter == MA_FENCE_COUNTER_MAX) { @@ -16838,7 +16845,7 @@ MA_API ma_result ma_fence_release(ma_fence* pFence) } for (;;) { - ma_uint32 oldCounter = c89atomic_load_32(&pFence->counter); + ma_uint32 oldCounter = ma_atomic_load_32(&pFence->counter); ma_uint32 newCounter = oldCounter - 1; if (oldCounter == 0) { @@ -16846,7 +16853,7 @@ MA_API ma_result ma_fence_release(ma_fence* pFence) return MA_INVALID_OPERATION; /* Acquire/release mismatch. */ } - if (c89atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { + if (ma_atomic_compare_exchange_weak_32(&pFence->counter, &oldCounter, newCounter)) { #ifndef MA_NO_THREADING { if (newCounter == 0) { @@ -16877,7 +16884,7 @@ MA_API ma_result ma_fence_wait(ma_fence* pFence) for (;;) { ma_uint32 counter; - counter = c89atomic_load_32(&pFence->counter); + counter = ma_atomic_load_32(&pFence->counter); if (counter == 0) { /* Counter has hit zero. By the time we get here some other thread may have acquired the @@ -17210,7 +17217,7 @@ MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint6 ma_uint32 newBitfield; ma_uint32 bitOffset; - oldBitfield = c89atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ + oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ /* Fast check to see if anything is available. */ if (oldBitfield == 0xFFFFFFFF) { @@ -17222,11 +17229,11 @@ MA_API ma_result ma_slot_allocator_alloc(ma_slot_allocator* pAllocator, ma_uint6 newBitfield = oldBitfield | (1 << bitOffset); - if (c89atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { + if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { ma_uint32 slotIndex; /* Increment the counter as soon as possible to have other threads report out-of-memory sooner than later. */ - c89atomic_fetch_add_32(&pAllocator->count, 1); + ma_atomic_fetch_add_32(&pAllocator->count, 1); /* The slot index is required for constructing the output value. */ slotIndex = (iGroup << 5) + bitOffset; /* iGroup << 5 = iGroup * 32 */ @@ -17275,12 +17282,12 @@ MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 MA_ASSERT(iBit < 32); /* This must be true due to the logic we used to actually calculate it. */ - while (c89atomic_load_32(&pAllocator->count) > 0) { + while (ma_atomic_load_32(&pAllocator->count) > 0) { /* CAS */ ma_uint32 oldBitfield; ma_uint32 newBitfield; - oldBitfield = c89atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ + oldBitfield = ma_atomic_load_32(&pAllocator->pGroups[iGroup].bitfield); /* <-- This copy must happen. The compiler must not optimize this away. */ newBitfield = oldBitfield & ~(1 << iBit); /* Debugging for checking for double-frees. */ @@ -17292,8 +17299,8 @@ MA_API ma_result ma_slot_allocator_free(ma_slot_allocator* pAllocator, ma_uint64 } #endif - if (c89atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { - c89atomic_fetch_sub_32(&pAllocator->count, 1); + if (ma_atomic_compare_and_swap_32(&pAllocator->pGroups[iGroup].bitfield, oldBitfield, newBitfield) == oldBitfield) { + ma_atomic_fetch_sub_32(&pAllocator->count, 1); return MA_SUCCESS; } } @@ -17624,7 +17631,7 @@ MA_API void ma_job_queue_uninit(ma_job_queue* pQueue, const ma_allocation_callba static ma_bool32 ma_job_queue_cas(volatile ma_uint64* dst, ma_uint64 expected, ma_uint64 desired) { /* The new counter is taken from the expected value. */ - return c89atomic_compare_and_swap_64(dst, expected, ma_job_set_refcount(desired, ma_job_extract_refcount(expected) + 1)) == expected; + return ma_atomic_compare_and_swap_64(dst, expected, ma_job_set_refcount(desired, ma_job_extract_refcount(expected) + 1)) == expected; } MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob) @@ -17662,10 +17669,10 @@ MA_API ma_result ma_job_queue_post(ma_job_queue* pQueue, const ma_job* pJob) { /* The job is stored in memory so now we need to add it to our linked list. We only ever add items to the end of the list. */ for (;;) { - tail = c89atomic_load_64(&pQueue->tail); - next = c89atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next); + tail = ma_atomic_load_64(&pQueue->tail); + next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(tail)].next); - if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(c89atomic_load_64(&pQueue->tail))) { + if (ma_job_toc_to_allocation(tail) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->tail))) { if (ma_job_extract_slot(next) == 0xFFFF) { if (ma_job_queue_cas(&pQueue->pJobs[ma_job_extract_slot(tail)].next, next, slot)) { break; @@ -17736,11 +17743,11 @@ MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob) /* Now we need to remove the root item from the list. */ for (;;) { - head = c89atomic_load_64(&pQueue->head); - tail = c89atomic_load_64(&pQueue->tail); - next = c89atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next); + head = ma_atomic_load_64(&pQueue->head); + tail = ma_atomic_load_64(&pQueue->tail); + next = ma_atomic_load_64(&pQueue->pJobs[ma_job_extract_slot(head)].next); - if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(c89atomic_load_64(&pQueue->head))) { + if (ma_job_toc_to_allocation(head) == ma_job_toc_to_allocation(ma_atomic_load_64(&pQueue->head))) { if (ma_job_extract_slot(head) == ma_job_extract_slot(tail)) { if (ma_job_extract_slot(next) == 0xFFFF) { #ifndef MA_USE_EXPERIMENTAL_LOCK_FREE_JOB_QUEUE @@ -17779,6 +17786,112 @@ MA_API ma_result ma_job_queue_next(ma_job_queue* pQueue, ma_job* pJob) +/******************************************************************************* + +Dynamic Linking + +*******************************************************************************/ +#ifdef MA_POSIX + /* No need for dlfcn.h if we're not using runtime linking. */ + #ifndef MA_NO_RUNTIME_LINKING + #include + #endif +#endif + +MA_API ma_handle ma_dlopen(ma_log* pLog, const char* filename) +{ +#ifndef MA_NO_RUNTIME_LINKING + ma_handle handle; + + ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); + + #ifdef MA_WIN32 + /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ + #if !defined(MA_WIN32_UWP) + handle = (ma_handle)LoadLibraryA(filename); + #else + /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ + WCHAR filenameW[4096]; + if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { + handle = NULL; + } else { + handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); + } + #endif + #else + handle = (ma_handle)dlopen(filename, RTLD_NOW); + #endif + + /* + I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority + backend is a deliberate design choice. Instead I'm logging it as an informational message. + */ + if (handle == NULL) { + ma_log_postf(pLog, MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename); + } + + return handle; +#else + /* Runtime linking is disabled. */ + (void)pLog; + (void)filename; + return NULL; +#endif +} + +MA_API void ma_dlclose(ma_log* pLog, ma_handle handle) +{ +#ifndef MA_NO_RUNTIME_LINKING + #ifdef MA_WIN32 + FreeLibrary((HMODULE)handle); + #else + dlclose((void*)handle); + #endif + + (void)pLog; +#else + /* Runtime linking is disabled. */ + (void)pLog; + (void)handle; +#endif +} + +MA_API ma_proc ma_dlsym(ma_log* pLog, ma_handle handle, const char* symbol) +{ +#ifndef MA_NO_RUNTIME_LINKING + ma_proc proc; + + ma_log_postf(pLog, MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol); + +#ifdef _WIN32 + proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); +#else +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" +#endif + proc = (ma_proc)dlsym((void*)handle, symbol); +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) + #pragma GCC diagnostic pop +#endif +#endif + + if (proc == NULL) { + ma_log_postf(pLog, MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol); + } + + (void)pLog; /* It's possible for pContext to be unused. */ + return proc; +#else + /* Runtime linking is disabled. */ + (void)pLog; + (void)handle; + (void)symbol; + return NULL; +#endif +} + + /************************************************************************************************************************************************************ ************************************************************************************************************************************************************* @@ -18443,105 +18556,6 @@ Timing #endif -/******************************************************************************* - -Dynamic Linking - -*******************************************************************************/ -MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename) -{ -#ifndef MA_NO_RUNTIME_LINKING - ma_handle handle; - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); - - #ifdef MA_WIN32 - /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ - #if !defined(MA_WIN32_UWP) - handle = (ma_handle)LoadLibraryA(filename); - #else - /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ - WCHAR filenameW[4096]; - if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { - handle = NULL; - } else { - handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); - } - #endif - #else - handle = (ma_handle)dlopen(filename, RTLD_NOW); - #endif - - /* - I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority - backend is a deliberate design choice. Instead I'm logging it as an informational message. - */ - if (handle == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "Failed to load library: %s\n", filename); - } - - (void)pContext; /* It's possible for pContext to be unused. */ - return handle; -#else - /* Runtime linking is disabled. */ - (void)pContext; - (void)filename; - return NULL; -#endif -} - -MA_API void ma_dlclose(ma_context* pContext, ma_handle handle) -{ -#ifndef MA_NO_RUNTIME_LINKING - #ifdef MA_WIN32 - FreeLibrary((HMODULE)handle); - #else - dlclose((void*)handle); - #endif - - (void)pContext; -#else - /* Runtime linking is disabled. */ - (void)pContext; - (void)handle; -#endif -} - -MA_API ma_proc ma_dlsym(ma_context* pContext, ma_handle handle, const char* symbol) -{ -#ifndef MA_NO_RUNTIME_LINKING - ma_proc proc; - - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading symbol: %s\n", symbol); - -#ifdef _WIN32 - proc = (ma_proc)GetProcAddress((HMODULE)handle, symbol); -#else -#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wpedantic" -#endif - proc = (ma_proc)dlsym((void*)handle, symbol); -#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #pragma GCC diagnostic pop -#endif -#endif - - if (proc == NULL) { - ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_WARNING, "Failed to load symbol: %s\n", symbol); - } - - (void)pContext; /* It's possible for pContext to be unused. */ - return proc; -#else - /* Runtime linking is disabled. */ - (void)pContext; - (void)handle; - (void)symbol; - return NULL; -#endif -} - #if 0 static ma_uint32 ma_get_closest_standard_sample_rate(ma_uint32 sampleRateIn) @@ -20676,12 +20690,12 @@ static HRESULT STDMETHODCALLTYPE ma_completion_handler_uwp_QueryInterface(ma_com static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_AddRef(ma_completion_handler_uwp* pThis) { - return (ULONG)c89atomic_fetch_add_32(&pThis->counter, 1) + 1; + return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1; } static ULONG STDMETHODCALLTYPE ma_completion_handler_uwp_Release(ma_completion_handler_uwp* pThis) { - ma_uint32 newRefCount = c89atomic_fetch_sub_32(&pThis->counter, 1) - 1; + ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1; if (newRefCount == 0) { return 0; /* We don't free anything here because we never allocate the object on the heap. */ } @@ -20753,12 +20767,12 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_QueryInterface(ma_IMMN static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_AddRef(ma_IMMNotificationClient* pThis) { - return (ULONG)c89atomic_fetch_add_32(&pThis->counter, 1) + 1; + return (ULONG)ma_atomic_fetch_add_32(&pThis->counter, 1) + 1; } static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificationClient* pThis) { - ma_uint32 newRefCount = c89atomic_fetch_sub_32(&pThis->counter, 1) - 1; + ma_uint32 newRefCount = ma_atomic_fetch_sub_32(&pThis->counter, 1) - 1; if (newRefCount == 0) { return 0; /* We don't free anything here because we never allocate the object on the heap. */ } @@ -22148,7 +22162,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device MA_COPY_MEMORY(&wf, pNativeFormat, cbSize); } - + result = MA_SUCCESS; } @@ -23404,14 +23418,14 @@ static ma_result ma_context_uninit__wasapi(ma_context* pContext) ma_thread_wait(&pContext->wasapi.commandThread); if (pContext->wasapi.hAvrt) { - ma_dlclose(pContext, pContext->wasapi.hAvrt); + ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); pContext->wasapi.hAvrt = NULL; } #if defined(MA_WIN32_UWP) { if (pContext->wasapi.hMMDevapi) { - ma_dlclose(pContext, pContext->wasapi.hMMDevapi); + ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); pContext->wasapi.hMMDevapi = NULL; } } @@ -23445,15 +23459,15 @@ static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_ ma_PFNVerifyVersionInfoW _VerifyVersionInfoW; ma_PFNVerSetConditionMask _VerSetConditionMask; - kernel32DLL = ma_dlopen(pContext, "kernel32.dll"); + kernel32DLL = ma_dlopen(ma_context_get_log(pContext), "kernel32.dll"); if (kernel32DLL == NULL) { return MA_NO_BACKEND; } - _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(pContext, kernel32DLL, "VerifyVersionInfoW"); - _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(pContext, kernel32DLL, "VerSetConditionMask"); + _VerifyVersionInfoW = (ma_PFNVerifyVersionInfoW )ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerifyVersionInfoW"); + _VerSetConditionMask = (ma_PFNVerSetConditionMask)ma_dlsym(ma_context_get_log(pContext), kernel32DLL, "VerSetConditionMask"); if (_VerifyVersionInfoW == NULL || _VerSetConditionMask == NULL) { - ma_dlclose(pContext, kernel32DLL); + ma_dlclose(ma_context_get_log(pContext), kernel32DLL); return MA_NO_BACKEND; } @@ -23468,7 +23482,7 @@ static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_ result = MA_NO_BACKEND; } - ma_dlclose(pContext, kernel32DLL); + ma_dlclose(ma_context_get_log(pContext), kernel32DLL); } #endif @@ -23525,13 +23539,13 @@ static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_ #if defined(MA_WIN32_UWP) { /* Link to mmdevapi so we can get access to ActivateAudioInterfaceAsync(). */ - pContext->wasapi.hMMDevapi = ma_dlopen(pContext, "mmdevapi.dll"); + pContext->wasapi.hMMDevapi = ma_dlopen(ma_context_get_log(pContext), "mmdevapi.dll"); if (pContext->wasapi.hMMDevapi) { - pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(pContext, pContext->wasapi.hMMDevapi, "ActivateAudioInterfaceAsync"); + pContext->wasapi.ActivateAudioInterfaceAsync = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi, "ActivateAudioInterfaceAsync"); if (pContext->wasapi.ActivateAudioInterfaceAsync == NULL) { ma_semaphore_uninit(&pContext->wasapi.commandSem); ma_mutex_uninit(&pContext->wasapi.commandLock); - ma_dlclose(pContext, pContext->wasapi.hMMDevapi); + ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hMMDevapi); return MA_NO_BACKEND; /* ActivateAudioInterfaceAsync() could not be loaded. */ } } else { @@ -23543,16 +23557,16 @@ static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_ #endif /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */ - pContext->wasapi.hAvrt = ma_dlopen(pContext, "avrt.dll"); + pContext->wasapi.hAvrt = ma_dlopen(ma_context_get_log(pContext), "avrt.dll"); if (pContext->wasapi.hAvrt) { - pContext->wasapi.AvSetMmThreadCharacteristicsA = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsA"); - pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics"); + pContext->wasapi.AvSetMmThreadCharacteristicsA = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsA"); + pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(ma_context_get_log(pContext), pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics"); /* If either function could not be found, disable use of avrt entirely. */ if (!pContext->wasapi.AvSetMmThreadCharacteristicsA || !pContext->wasapi.AvRevertMmThreadcharacteristics) { pContext->wasapi.AvSetMmThreadCharacteristicsA = NULL; pContext->wasapi.AvRevertMmThreadcharacteristics = NULL; - ma_dlclose(pContext, pContext->wasapi.hAvrt); + ma_dlclose(ma_context_get_log(pContext), pContext->wasapi.hAvrt); pContext->wasapi.hAvrt = NULL; } } @@ -24173,7 +24187,7 @@ static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(GUID* lpGuid, /* Call the callback function, but make sure we stop enumerating if the callee requested so. */ MA_ASSERT(pData != NULL); - pData->terminated = !pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData); + pData->terminated = (pData->callback(pData->pContext, pData->deviceType, &deviceInfo, pData->pUserData) == MA_FALSE); if (pData->terminated) { return FALSE; /* Stop enumeration. */ } else { @@ -25283,7 +25297,7 @@ static ma_result ma_context_uninit__dsound(ma_context* pContext) MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_dsound); - ma_dlclose(pContext, pContext->dsound.hDSoundDLL); + ma_dlclose(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL); return MA_SUCCESS; } @@ -25294,15 +25308,15 @@ static ma_result ma_context_init__dsound(ma_context* pContext, const ma_context_ (void)pConfig; - pContext->dsound.hDSoundDLL = ma_dlopen(pContext, "dsound.dll"); + pContext->dsound.hDSoundDLL = ma_dlopen(ma_context_get_log(pContext), "dsound.dll"); if (pContext->dsound.hDSoundDLL == NULL) { return MA_API_NOT_FOUND; } - pContext->dsound.DirectSoundCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCreate"); - pContext->dsound.DirectSoundEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA"); - pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); - pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(pContext, pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA"); + pContext->dsound.DirectSoundCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCreate"); + pContext->dsound.DirectSoundEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundEnumerateA"); + pContext->dsound.DirectSoundCaptureCreate = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureCreate"); + pContext->dsound.DirectSoundCaptureEnumerateA = ma_dlsym(ma_context_get_log(pContext), pContext->dsound.hDSoundDLL, "DirectSoundCaptureEnumerateA"); /* We need to support all functions or nothing. DirectSound with Windows 95 seems to not work too @@ -26382,7 +26396,7 @@ static ma_result ma_context_uninit__winmm(ma_context* pContext) MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_winmm); - ma_dlclose(pContext, pContext->winmm.hWinMM); + ma_dlclose(ma_context_get_log(pContext), pContext->winmm.hWinMM); return MA_SUCCESS; } @@ -26392,28 +26406,28 @@ static ma_result ma_context_init__winmm(ma_context* pContext, const ma_context_c (void)pConfig; - pContext->winmm.hWinMM = ma_dlopen(pContext, "winmm.dll"); + pContext->winmm.hWinMM = ma_dlopen(ma_context_get_log(pContext), "winmm.dll"); if (pContext->winmm.hWinMM == NULL) { return MA_NO_BACKEND; } - pContext->winmm.waveOutGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetNumDevs"); - pContext->winmm.waveOutGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutGetDevCapsA"); - pContext->winmm.waveOutOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutOpen"); - pContext->winmm.waveOutClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutClose"); - pContext->winmm.waveOutPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutPrepareHeader"); - pContext->winmm.waveOutUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutUnprepareHeader"); - pContext->winmm.waveOutWrite = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutWrite"); - pContext->winmm.waveOutReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveOutReset"); - pContext->winmm.waveInGetNumDevs = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetNumDevs"); - pContext->winmm.waveInGetDevCapsA = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInGetDevCapsA"); - pContext->winmm.waveInOpen = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInOpen"); - pContext->winmm.waveInClose = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInClose"); - pContext->winmm.waveInPrepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInPrepareHeader"); - pContext->winmm.waveInUnprepareHeader = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInUnprepareHeader"); - pContext->winmm.waveInAddBuffer = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInAddBuffer"); - pContext->winmm.waveInStart = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInStart"); - pContext->winmm.waveInReset = ma_dlsym(pContext, pContext->winmm.hWinMM, "waveInReset"); + pContext->winmm.waveOutGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetNumDevs"); + pContext->winmm.waveOutGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutGetDevCapsA"); + pContext->winmm.waveOutOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutOpen"); + pContext->winmm.waveOutClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutClose"); + pContext->winmm.waveOutPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutPrepareHeader"); + pContext->winmm.waveOutUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutUnprepareHeader"); + pContext->winmm.waveOutWrite = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutWrite"); + pContext->winmm.waveOutReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveOutReset"); + pContext->winmm.waveInGetNumDevs = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetNumDevs"); + pContext->winmm.waveInGetDevCapsA = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInGetDevCapsA"); + pContext->winmm.waveInOpen = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInOpen"); + pContext->winmm.waveInClose = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInClose"); + pContext->winmm.waveInPrepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInPrepareHeader"); + pContext->winmm.waveInUnprepareHeader = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInUnprepareHeader"); + pContext->winmm.waveInAddBuffer = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInAddBuffer"); + pContext->winmm.waveInStart = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInStart"); + pContext->winmm.waveInReset = ma_dlsym(ma_context_get_log(pContext), pContext->winmm.hWinMM, "waveInReset"); pCallbacks->onContextInit = ma_context_init__winmm; pCallbacks->onContextUninit = ma_context_uninit__winmm; @@ -28031,6 +28045,12 @@ static ma_result ma_device_start__alsa(ma_device* pDevice) static ma_result ma_device_stop__alsa(ma_device* pDevice) { + /* + The stop callback will get called on the worker thread after read/write__alsa() has returned. At this point there is + a small chance that our wakeupfd has not been cleared. We'll clear that out now if applicable. + */ + int resultPoll; + if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Dropping capture device...\n"); ((ma_snd_pcm_drop_proc)pDevice->pContext->alsa.snd_pcm_drop)((ma_snd_pcm_t*)pDevice->alsa.pPCMCapture); @@ -28043,6 +28063,13 @@ static ma_result ma_device_stop__alsa(ma_device* pDevice) } else { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing capture device successful.\n"); } + + /* Clear the wakeupfd. */ + resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture, 1, 0); + if (resultPoll > 0) { + ma_uint64 t; + read(((struct pollfd*)pDevice->alsa.pPollDescriptorsCapture)[0].fd, &t, sizeof(t)); + } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { @@ -28057,6 +28084,14 @@ static ma_result ma_device_stop__alsa(ma_device* pDevice) } else { ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "[ALSA] Preparing playback device successful.\n"); } + + /* Clear the wakeupfd. */ + resultPoll = poll((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback, 1, 0); + if (resultPoll > 0) { + ma_uint64 t; + read(((struct pollfd*)pDevice->alsa.pPollDescriptorsPlayback)[0].fd, &t, sizeof(t)); + } + } return MA_SUCCESS; @@ -28069,7 +28104,7 @@ static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, st int resultALSA; int resultPoll = poll(pPollDescriptors, pollDescriptorCount, -1); if (resultPoll < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] poll() failed."); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] poll() failed.\n"); return ma_result_from_errno(errno); } @@ -28082,7 +28117,7 @@ static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, st ma_uint64 t; int resultRead = read(pPollDescriptors[0].fd, &t, sizeof(t)); /* <-- Important that we read here so that the next write() does not block. */ if (resultRead < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] read() failed."); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] read() failed.\n"); return ma_result_from_errno(errno); } @@ -28096,13 +28131,17 @@ static ma_result ma_device_wait__alsa(ma_device* pDevice, ma_snd_pcm_t* pPCM, st */ resultALSA = ((ma_snd_pcm_poll_descriptors_revents_proc)pDevice->pContext->alsa.snd_pcm_poll_descriptors_revents)(pPCM, pPollDescriptors + 1, pollDescriptorCount - 1, &revents); /* +1, -1 to ignore the wakeup descriptor. */ if (resultALSA < 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed."); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] snd_pcm_poll_descriptors_revents() failed.\n"); return ma_result_from_errno(-resultALSA); } if ((revents & POLLERR) != 0) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[ALSA] POLLERR detected."); - return ma_result_from_errno(errno); + ma_snd_pcm_state_t state = ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM); + if (state == MA_SND_PCM_STATE_XRUN) { + /* The PCM is in a xrun state. This will be recovered from at a higher level. We can disregard this. */ + } else { + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[ALSA] POLLERR detected. status = %d\n", ((ma_snd_pcm_state_proc)pDevice->pContext->alsa.snd_pcm_state)(pPCM)); + } } if ((revents & requiredEvent) == requiredEvent) { @@ -28277,7 +28316,7 @@ static ma_result ma_context_uninit__alsa(ma_context* pContext) ((ma_snd_config_update_free_global_proc)pContext->alsa.snd_config_update_free_global)(); #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext, pContext->alsa.asoundSO); + ma_dlclose(ma_context_get_log(pContext), pContext->alsa.asoundSO); #endif ma_mutex_uninit(&pContext->alsa.internalDeviceEnumLock); @@ -28296,7 +28335,7 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co size_t i; for (i = 0; i < ma_countof(libasoundNames); ++i) { - pContext->alsa.asoundSO = ma_dlopen(pContext, libasoundNames[i]); + pContext->alsa.asoundSO = ma_dlopen(ma_context_get_log(pContext), libasoundNames[i]); if (pContext->alsa.asoundSO != NULL) { break; } @@ -28307,72 +28346,72 @@ static ma_result ma_context_init__alsa(ma_context* pContext, const ma_context_co return MA_NO_BACKEND; } - pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_open"); - pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_close"); - pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof"); - pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_any"); - pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format"); - pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first"); - pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask"); - pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels"); - pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near"); - pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax"); - pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample"); - pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate"); - pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near"); - pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); - pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near"); - pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access"); - pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format"); - pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels"); - pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min"); - pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max"); - pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate"); - pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min"); - pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max"); - pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size"); - pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods"); - pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access"); - pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format"); - pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels"); - pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate"); - pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_hw_params"); - pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof"); - pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_current"); - pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary"); - pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min"); - pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold"); - pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold"); - pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_sw_params"); - pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof"); - pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_format_mask_test"); - pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_get_chmap"); - pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_state"); - pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_prepare"); - pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_start"); - pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drop"); - pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_drain"); - pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_reset"); - pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_hint"); - pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_get_hint"); - pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_card_get_index"); - pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_device_name_free_hint"); - pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_begin"); - pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_mmap_commit"); - pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_recover"); - pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_readi"); - pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_writei"); - pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail"); - pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_avail_update"); - pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_wait"); - pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_nonblock"); - pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info"); - pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); - pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_info_get_name"); - pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors"); - pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count"); - pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents"); - pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(pContext, pContext->alsa.asoundSO, "snd_config_update_free_global"); + pContext->alsa.snd_pcm_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_open"); + pContext->alsa.snd_pcm_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_close"); + pContext->alsa.snd_pcm_hw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_sizeof"); + pContext->alsa.snd_pcm_hw_params_any = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_any"); + pContext->alsa.snd_pcm_hw_params_set_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format"); + pContext->alsa.snd_pcm_hw_params_set_format_first = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_format_first"); + pContext->alsa.snd_pcm_hw_params_get_format_mask = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format_mask"); + pContext->alsa.snd_pcm_hw_params_set_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels"); + pContext->alsa.snd_pcm_hw_params_set_channels_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_near"); + pContext->alsa.snd_pcm_hw_params_set_channels_minmax = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_channels_minmax"); + pContext->alsa.snd_pcm_hw_params_set_rate_resample = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_resample"); + pContext->alsa.snd_pcm_hw_params_set_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate"); + pContext->alsa.snd_pcm_hw_params_set_rate_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_rate_near"); + pContext->alsa.snd_pcm_hw_params_set_buffer_size_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_buffer_size_near"); + pContext->alsa.snd_pcm_hw_params_set_periods_near = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_periods_near"); + pContext->alsa.snd_pcm_hw_params_set_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_set_access"); + pContext->alsa.snd_pcm_hw_params_get_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_format"); + pContext->alsa.snd_pcm_hw_params_get_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels"); + pContext->alsa.snd_pcm_hw_params_get_channels_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_min"); + pContext->alsa.snd_pcm_hw_params_get_channels_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_channels_max"); + pContext->alsa.snd_pcm_hw_params_get_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate"); + pContext->alsa.snd_pcm_hw_params_get_rate_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_min"); + pContext->alsa.snd_pcm_hw_params_get_rate_max = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_rate_max"); + pContext->alsa.snd_pcm_hw_params_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_buffer_size"); + pContext->alsa.snd_pcm_hw_params_get_periods = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_periods"); + pContext->alsa.snd_pcm_hw_params_get_access = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_get_access"); + pContext->alsa.snd_pcm_hw_params_test_format = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_format"); + pContext->alsa.snd_pcm_hw_params_test_channels = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_channels"); + pContext->alsa.snd_pcm_hw_params_test_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params_test_rate"); + pContext->alsa.snd_pcm_hw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_hw_params"); + pContext->alsa.snd_pcm_sw_params_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_sizeof"); + pContext->alsa.snd_pcm_sw_params_current = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_current"); + pContext->alsa.snd_pcm_sw_params_get_boundary = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_get_boundary"); + pContext->alsa.snd_pcm_sw_params_set_avail_min = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_avail_min"); + pContext->alsa.snd_pcm_sw_params_set_start_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_start_threshold"); + pContext->alsa.snd_pcm_sw_params_set_stop_threshold = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params_set_stop_threshold"); + pContext->alsa.snd_pcm_sw_params = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_sw_params"); + pContext->alsa.snd_pcm_format_mask_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_sizeof"); + pContext->alsa.snd_pcm_format_mask_test = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_format_mask_test"); + pContext->alsa.snd_pcm_get_chmap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_get_chmap"); + pContext->alsa.snd_pcm_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_state"); + pContext->alsa.snd_pcm_prepare = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_prepare"); + pContext->alsa.snd_pcm_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_start"); + pContext->alsa.snd_pcm_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drop"); + pContext->alsa.snd_pcm_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_drain"); + pContext->alsa.snd_pcm_reset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_reset"); + pContext->alsa.snd_device_name_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_hint"); + pContext->alsa.snd_device_name_get_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_get_hint"); + pContext->alsa.snd_card_get_index = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_card_get_index"); + pContext->alsa.snd_device_name_free_hint = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_device_name_free_hint"); + pContext->alsa.snd_pcm_mmap_begin = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_begin"); + pContext->alsa.snd_pcm_mmap_commit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_mmap_commit"); + pContext->alsa.snd_pcm_recover = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_recover"); + pContext->alsa.snd_pcm_readi = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_readi"); + pContext->alsa.snd_pcm_writei = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_writei"); + pContext->alsa.snd_pcm_avail = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail"); + pContext->alsa.snd_pcm_avail_update = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_avail_update"); + pContext->alsa.snd_pcm_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_wait"); + pContext->alsa.snd_pcm_nonblock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_nonblock"); + pContext->alsa.snd_pcm_info = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info"); + pContext->alsa.snd_pcm_info_sizeof = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_sizeof"); + pContext->alsa.snd_pcm_info_get_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_info_get_name"); + pContext->alsa.snd_pcm_poll_descriptors = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors"); + pContext->alsa.snd_pcm_poll_descriptors_count = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_count"); + pContext->alsa.snd_pcm_poll_descriptors_revents = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_pcm_poll_descriptors_revents"); + pContext->alsa.snd_config_update_free_global = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->alsa.asoundSO, "snd_config_update_free_global"); #else /* The system below is just for type safety. */ ma_snd_pcm_open_proc _snd_pcm_open = snd_pcm_open; @@ -30228,11 +30267,6 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi /* Notes for PulseAudio: - - We're always using native format/channels/rate regardless of whether or not PulseAudio - supports the format directly through their own data conversion system. I'm doing this to - reduce as much variability from the PulseAudio side as possible because it's seems to be - extremely unreliable at everything it does. - - When both the period size in frames and milliseconds are 0, we default to miniaudio's default buffer sizes rather than leaving it up to PulseAudio because I don't trust PulseAudio to give us any kind of reasonable latency by default. @@ -30321,6 +30355,7 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi if (pDescriptorCapture->sampleRate != 0) { ss.rate = pDescriptorCapture->sampleRate; } + streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; if (ma_format_from_pulse(ss.format) == ma_format_unknown) { if (ma_is_little_endian()) { @@ -30328,14 +30363,17 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi } else { ss.format = MA_PA_SAMPLE_FLOAT32BE; } + streamFlags |= MA_PA_STREAM_FIX_FORMAT; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); } if (ss.rate == 0) { ss.rate = MA_DEFAULT_SAMPLE_RATE; + streamFlags |= MA_PA_STREAM_FIX_RATE; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); } if (ss.channels == 0) { ss.channels = MA_DEFAULT_CHANNELS; + streamFlags |= MA_PA_STREAM_FIX_CHANNELS; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); } @@ -30364,7 +30402,6 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi /* Connect after we've got all of our internal state set up. */ - streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS; if (devCapture != NULL) { streamFlags |= MA_PA_STREAM_DONT_MOVE; } @@ -30467,20 +30504,24 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ss.rate = pDescriptorPlayback->sampleRate; } + streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY; if (ma_format_from_pulse(ss.format) == ma_format_unknown) { if (ma_is_little_endian()) { ss.format = MA_PA_SAMPLE_FLOAT32LE; } else { ss.format = MA_PA_SAMPLE_FLOAT32BE; } + streamFlags |= MA_PA_STREAM_FIX_FORMAT; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.format not supported by miniaudio. Defaulting to PA_SAMPLE_FLOAT32.\n"); } if (ss.rate == 0) { ss.rate = MA_DEFAULT_SAMPLE_RATE; + streamFlags |= MA_PA_STREAM_FIX_RATE; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.rate = 0. Defaulting to %d.\n", ss.rate); } if (ss.channels == 0) { ss.channels = MA_DEFAULT_CHANNELS; + streamFlags |= MA_PA_STREAM_FIX_CHANNELS; ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_INFO, "[PulseAudio] sample_spec.channels = 0. Defaulting to %d.\n", ss.channels); } @@ -30513,7 +30554,6 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi /* Connect after we've got all of our internal state set up. */ - streamFlags = MA_PA_STREAM_START_CORKED | MA_PA_STREAM_ADJUST_LATENCY | MA_PA_STREAM_FIX_FORMAT | MA_PA_STREAM_FIX_RATE | MA_PA_STREAM_FIX_CHANNELS; if (devPlayback != NULL) { streamFlags |= MA_PA_STREAM_DONT_MOVE; } @@ -30792,7 +30832,7 @@ static ma_result ma_context_uninit__pulse(ma_context* pContext) ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext, pContext->pulse.pulseSO); + ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); #endif return MA_SUCCESS; @@ -30809,7 +30849,7 @@ static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_c size_t i; for (i = 0; i < ma_countof(libpulseNames); ++i) { - pContext->pulse.pulseSO = ma_dlopen(pContext, libpulseNames[i]); + pContext->pulse.pulseSO = ma_dlopen(ma_context_get_log(pContext), libpulseNames[i]); if (pContext->pulse.pulseSO != NULL) { break; } @@ -30819,67 +30859,67 @@ static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_c return MA_NO_BACKEND; } - pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_new"); - pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_free"); - pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_quit"); - pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_get_api"); - pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_iterate"); - pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_mainloop_wakeup"); - pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_new"); - pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_free"); - pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_start"); - pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_stop"); - pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_lock"); - pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock"); - pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_wait"); - pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_signal"); - pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_accept"); - pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval"); - pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api"); - pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread"); - pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name"); - pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_new"); - pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_unref"); - pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_connect"); - pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_disconnect"); - pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_set_state_callback"); - pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_state"); - pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_list"); - pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_list"); - pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name"); - pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_context_get_source_info_by_name"); - pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_unref"); - pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_operation_get_state"); - pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_init_extend"); - pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_valid"); - pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_channel_map_compatible"); - pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_new"); - pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_unref"); - pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_playback"); - pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_connect_record"); - pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_disconnect"); - pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_state"); - pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_sample_spec"); - pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_channel_map"); - pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_buffer_attr"); - pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_buffer_attr"); - pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_get_device_name"); - pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_write_callback"); - pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_read_callback"); - pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_suspended_callback"); - pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_set_moved_callback"); - pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_suspended"); - pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_flush"); - pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drain"); - pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_is_corked"); - pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_cork"); - pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_trigger"); - pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_begin_write"); - pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_write"); - pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_peek"); - pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_drop"); - pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_writable_size"); - pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(pContext, pContext->pulse.pulseSO, "pa_stream_readable_size"); + pContext->pulse.pa_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_new"); + pContext->pulse.pa_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_free"); + pContext->pulse.pa_mainloop_quit = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_quit"); + pContext->pulse.pa_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_get_api"); + pContext->pulse.pa_mainloop_iterate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_iterate"); + pContext->pulse.pa_mainloop_wakeup = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_mainloop_wakeup"); + pContext->pulse.pa_threaded_mainloop_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_new"); + pContext->pulse.pa_threaded_mainloop_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_free"); + pContext->pulse.pa_threaded_mainloop_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_start"); + pContext->pulse.pa_threaded_mainloop_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_stop"); + pContext->pulse.pa_threaded_mainloop_lock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_lock"); + pContext->pulse.pa_threaded_mainloop_unlock = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_unlock"); + pContext->pulse.pa_threaded_mainloop_wait = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_wait"); + pContext->pulse.pa_threaded_mainloop_signal = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_signal"); + pContext->pulse.pa_threaded_mainloop_accept = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_accept"); + pContext->pulse.pa_threaded_mainloop_get_retval = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_retval"); + pContext->pulse.pa_threaded_mainloop_get_api = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_get_api"); + pContext->pulse.pa_threaded_mainloop_in_thread = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_in_thread"); + pContext->pulse.pa_threaded_mainloop_set_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_threaded_mainloop_set_name"); + pContext->pulse.pa_context_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_new"); + pContext->pulse.pa_context_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_unref"); + pContext->pulse.pa_context_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_connect"); + pContext->pulse.pa_context_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_disconnect"); + pContext->pulse.pa_context_set_state_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_set_state_callback"); + pContext->pulse.pa_context_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_state"); + pContext->pulse.pa_context_get_sink_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_list"); + pContext->pulse.pa_context_get_source_info_list = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_list"); + pContext->pulse.pa_context_get_sink_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_sink_info_by_name"); + pContext->pulse.pa_context_get_source_info_by_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_context_get_source_info_by_name"); + pContext->pulse.pa_operation_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_unref"); + pContext->pulse.pa_operation_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_operation_get_state"); + pContext->pulse.pa_channel_map_init_extend = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_init_extend"); + pContext->pulse.pa_channel_map_valid = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_valid"); + pContext->pulse.pa_channel_map_compatible = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_channel_map_compatible"); + pContext->pulse.pa_stream_new = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_new"); + pContext->pulse.pa_stream_unref = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_unref"); + pContext->pulse.pa_stream_connect_playback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_playback"); + pContext->pulse.pa_stream_connect_record = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_connect_record"); + pContext->pulse.pa_stream_disconnect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_disconnect"); + pContext->pulse.pa_stream_get_state = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_state"); + pContext->pulse.pa_stream_get_sample_spec = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_sample_spec"); + pContext->pulse.pa_stream_get_channel_map = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_channel_map"); + pContext->pulse.pa_stream_get_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_buffer_attr"); + pContext->pulse.pa_stream_set_buffer_attr = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_buffer_attr"); + pContext->pulse.pa_stream_get_device_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_get_device_name"); + pContext->pulse.pa_stream_set_write_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_write_callback"); + pContext->pulse.pa_stream_set_read_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_read_callback"); + pContext->pulse.pa_stream_set_suspended_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_suspended_callback"); + pContext->pulse.pa_stream_set_moved_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_set_moved_callback"); + pContext->pulse.pa_stream_is_suspended = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_suspended"); + pContext->pulse.pa_stream_flush = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_flush"); + pContext->pulse.pa_stream_drain = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drain"); + pContext->pulse.pa_stream_is_corked = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_is_corked"); + pContext->pulse.pa_stream_cork = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_cork"); + pContext->pulse.pa_stream_trigger = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_trigger"); + pContext->pulse.pa_stream_begin_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_begin_write"); + pContext->pulse.pa_stream_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_write"); + pContext->pulse.pa_stream_peek = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_peek"); + pContext->pulse.pa_stream_drop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_drop"); + pContext->pulse.pa_stream_writable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_writable_size"); + pContext->pulse.pa_stream_readable_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->pulse.pulseSO, "pa_stream_readable_size"); #else /* This strange assignment system is just for type safety. */ ma_pa_mainloop_new_proc _pa_mainloop_new = pa_mainloop_new; @@ -31024,7 +31064,7 @@ static ma_result ma_context_init__pulse(ma_context* pContext, const ma_context_c ma_free(pContext->pulse.pServerName, &pContext->allocationCallbacks); ma_free(pContext->pulse.pApplicationName, &pContext->allocationCallbacks); #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext, pContext->pulse.pulseSO); + ma_dlclose(ma_context_get_log(pContext), pContext->pulse.pulseSO); #endif return result; } @@ -31588,7 +31628,7 @@ static ma_result ma_context_uninit__jack(ma_context* pContext) pContext->jack.pClientName = NULL; #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext, pContext->jack.jackSO); + ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); #endif return MA_SUCCESS; @@ -31610,7 +31650,7 @@ static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_co size_t i; for (i = 0; i < ma_countof(libjackNames); ++i) { - pContext->jack.jackSO = ma_dlopen(pContext, libjackNames[i]); + pContext->jack.jackSO = ma_dlopen(ma_context_get_log(pContext), libjackNames[i]); if (pContext->jack.jackSO != NULL) { break; } @@ -31620,22 +31660,22 @@ static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_co return MA_NO_BACKEND; } - pContext->jack.jack_client_open = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_open"); - pContext->jack.jack_client_close = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_close"); - pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_client_name_size"); - pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_process_callback"); - pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_set_buffer_size_callback"); - pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_on_shutdown"); - pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_sample_rate"); - pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_buffer_size"); - pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_get_ports"); - pContext->jack.jack_activate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_activate"); - pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_deactivate"); - pContext->jack.jack_connect = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_connect"); - pContext->jack.jack_port_register = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_register"); - pContext->jack.jack_port_name = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_name"); - pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_port_get_buffer"); - pContext->jack.jack_free = (ma_proc)ma_dlsym(pContext, pContext->jack.jackSO, "jack_free"); + pContext->jack.jack_client_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_open"); + pContext->jack.jack_client_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_close"); + pContext->jack.jack_client_name_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_client_name_size"); + pContext->jack.jack_set_process_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_process_callback"); + pContext->jack.jack_set_buffer_size_callback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_set_buffer_size_callback"); + pContext->jack.jack_on_shutdown = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_on_shutdown"); + pContext->jack.jack_get_sample_rate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_sample_rate"); + pContext->jack.jack_get_buffer_size = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_buffer_size"); + pContext->jack.jack_get_ports = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_get_ports"); + pContext->jack.jack_activate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_activate"); + pContext->jack.jack_deactivate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_deactivate"); + pContext->jack.jack_connect = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_connect"); + pContext->jack.jack_port_register = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_register"); + pContext->jack.jack_port_name = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_name"); + pContext->jack.jack_port_get_buffer = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_port_get_buffer"); + pContext->jack.jack_free = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->jack.jackSO, "jack_free"); #else /* This strange assignment system is here just to ensure type safety of miniaudio's function pointer @@ -31691,7 +31731,7 @@ static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_co if (result != MA_SUCCESS) { ma_free(pContext->jack.pClientName, &pContext->allocationCallbacks); #ifndef MA_NO_RUNTIME_LINKING - ma_dlclose(pContext, pContext->jack.jackSO); + ma_dlclose(ma_context_get_log(pContext), pContext->jack.jackSO); #endif return MA_NO_BACKEND; } @@ -34703,9 +34743,9 @@ static ma_result ma_context_uninit__coreaudio(ma_context* pContext) #endif #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); - ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); - ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); #endif #if !defined(MA_APPLE_MOBILE) @@ -34794,26 +34834,26 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte #endif #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - pContext->coreaudio.hCoreFoundation = ma_dlopen(pContext, "CoreFoundation.framework/CoreFoundation"); + pContext->coreaudio.hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), "CoreFoundation.framework/CoreFoundation"); if (pContext->coreaudio.hCoreFoundation == NULL) { return MA_API_NOT_FOUND; } - pContext->coreaudio.CFStringGetCString = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFStringGetCString"); - pContext->coreaudio.CFRelease = ma_dlsym(pContext, pContext->coreaudio.hCoreFoundation, "CFRelease"); + pContext->coreaudio.CFStringGetCString = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFStringGetCString"); + pContext->coreaudio.CFRelease = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFRelease"); - pContext->coreaudio.hCoreAudio = ma_dlopen(pContext, "CoreAudio.framework/CoreAudio"); + pContext->coreaudio.hCoreAudio = ma_dlopen(ma_context_get_log(pContext), "CoreAudio.framework/CoreAudio"); if (pContext->coreaudio.hCoreAudio == NULL) { - ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); return MA_API_NOT_FOUND; } - pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData"); - pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize"); - pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); - pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener"); - pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(pContext, pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener"); + pContext->coreaudio.AudioObjectGetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyData"); + pContext->coreaudio.AudioObjectGetPropertyDataSize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectGetPropertyDataSize"); + pContext->coreaudio.AudioObjectSetPropertyData = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectSetPropertyData"); + pContext->coreaudio.AudioObjectAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectAddPropertyListener"); + pContext->coreaudio.AudioObjectRemovePropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio, "AudioObjectRemovePropertyListener"); /* It looks like Apple has moved some APIs from AudioUnit into AudioToolbox on more recent versions of macOS. They are still @@ -34821,35 +34861,35 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to AudioToolbox. */ - pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioUnit.framework/AudioUnit"); + pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "AudioUnit.framework/AudioUnit"); if (pContext->coreaudio.hAudioUnit == NULL) { - ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); - ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); return MA_API_NOT_FOUND; } - if (ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { + if (ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */ - ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); - pContext->coreaudio.hAudioUnit = ma_dlopen(pContext, "AudioToolbox.framework/AudioToolbox"); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); + pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "AudioToolbox.framework/AudioToolbox"); if (pContext->coreaudio.hAudioUnit == NULL) { - ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); - ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); return MA_API_NOT_FOUND; } } - pContext->coreaudio.AudioComponentFindNext = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentFindNext"); - pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose"); - pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew"); - pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart"); - pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop"); - pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener"); - pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo"); - pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty"); - pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty"); - pContext->coreaudio.AudioUnitInitialize = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitInitialize"); - pContext->coreaudio.AudioUnitRender = ma_dlsym(pContext, pContext->coreaudio.hAudioUnit, "AudioUnitRender"); + pContext->coreaudio.AudioComponentFindNext = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext"); + pContext->coreaudio.AudioComponentInstanceDispose = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceDispose"); + pContext->coreaudio.AudioComponentInstanceNew = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentInstanceNew"); + pContext->coreaudio.AudioOutputUnitStart = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStart"); + pContext->coreaudio.AudioOutputUnitStop = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioOutputUnitStop"); + pContext->coreaudio.AudioUnitAddPropertyListener = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitAddPropertyListener"); + pContext->coreaudio.AudioUnitGetPropertyInfo = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetPropertyInfo"); + pContext->coreaudio.AudioUnitGetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitGetProperty"); + pContext->coreaudio.AudioUnitSetProperty = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitSetProperty"); + pContext->coreaudio.AudioUnitInitialize = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitInitialize"); + pContext->coreaudio.AudioUnitRender = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioUnitRender"); #else pContext->coreaudio.CFStringGetCString = (ma_proc)CFStringGetCString; pContext->coreaudio.CFRelease = (ma_proc)CFRelease; @@ -34891,9 +34931,9 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte pContext->coreaudio.component = ((ma_AudioComponentFindNext_proc)pContext->coreaudio.AudioComponentFindNext)(NULL, &desc); if (pContext->coreaudio.component == NULL) { #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); - ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); - ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); #endif return MA_FAILED_TO_INIT_BACKEND; } @@ -34903,9 +34943,9 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte result = ma_context__init_device_tracking__coreaudio(pContext); if (result != MA_SUCCESS) { #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - ma_dlclose(pContext, pContext->coreaudio.hAudioUnit); - ma_dlclose(pContext, pContext->coreaudio.hCoreAudio); - ma_dlclose(pContext, pContext->coreaudio.hCoreFoundation); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); #endif return result; } @@ -35726,7 +35766,7 @@ static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_c size_t i; for (i = 0; i < ma_countof(libsndioNames); ++i) { - pContext->sndio.sndioSO = ma_dlopen(pContext, libsndioNames[i]); + pContext->sndio.sndioSO = ma_dlopen(ma_context_get_log(pContext), libsndioNames[i]); if (pContext->sndio.sndioSO != NULL) { break; } @@ -35736,16 +35776,16 @@ static ma_result ma_context_init__sndio(ma_context* pContext, const ma_context_c return MA_NO_BACKEND; } - pContext->sndio.sio_open = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_open"); - pContext->sndio.sio_close = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_close"); - pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_setpar"); - pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getpar"); - pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_getcap"); - pContext->sndio.sio_write = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_write"); - pContext->sndio.sio_read = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_read"); - pContext->sndio.sio_start = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_start"); - pContext->sndio.sio_stop = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_stop"); - pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(pContext, pContext->sndio.sndioSO, "sio_initpar"); + pContext->sndio.sio_open = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_open"); + pContext->sndio.sio_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_close"); + pContext->sndio.sio_setpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_setpar"); + pContext->sndio.sio_getpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getpar"); + pContext->sndio.sio_getcap = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_getcap"); + pContext->sndio.sio_write = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_write"); + pContext->sndio.sio_read = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_read"); + pContext->sndio.sio_start = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_start"); + pContext->sndio.sio_stop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_stop"); + pContext->sndio.sio_initpar = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->sndio.sndioSO, "sio_initpar"); #else pContext->sndio.sio_open = sio_open; pContext->sndio.sio_close = sio_close; @@ -37614,7 +37654,7 @@ static ma_result ma_create_and_configure_AAudioStreamBuilder__aaudio(ma_context* anything from Android 11 and earlier. Suggestions welcome on how we might be able to make this more targetted. */ - if (pConfig->aaudio.enableCompatibilityWorkarounds && ma_android_sdk_version() > 30) { + if (!pConfig->aaudio.enableCompatibilityWorkarounds || ma_android_sdk_version() > 30) { /* AAudio is annoying when it comes to it's buffer calculation stuff because it doesn't let you retrieve the actual sample rate until after you've opened the stream. But you need to configure @@ -38187,7 +38227,7 @@ static ma_result ma_context_uninit__aaudio(ma_context* pContext) ma_device_job_thread_uninit(&pContext->aaudio.jobThread, &pContext->allocationCallbacks); - ma_dlclose(pContext, pContext->aaudio.hAAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); pContext->aaudio.hAAudio = NULL; return MA_SUCCESS; @@ -38201,7 +38241,7 @@ static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_ }; for (i = 0; i < ma_countof(libNames); ++i) { - pContext->aaudio.hAAudio = ma_dlopen(pContext, libNames[i]); + pContext->aaudio.hAAudio = ma_dlopen(ma_context_get_log(pContext), libNames[i]); if (pContext->aaudio.hAAudio != NULL) { break; } @@ -38211,35 +38251,35 @@ static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_ return MA_FAILED_TO_INIT_BACKEND; } - pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudio_createStreamBuilder"); - pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete"); - pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId"); - pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection"); - pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode"); - pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat"); - pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount"); - pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate"); - pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames"); - pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback"); - pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback"); - pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback"); - pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode"); - pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage"); - pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType"); - pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset"); - pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_setAllowedCapturePolicy"); - pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream"); - pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_close"); - pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getState"); - pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange"); - pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFormat"); - pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getChannelCount"); - pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getSampleRate"); - pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames"); - pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback"); - pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst"); - pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStart"); - pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(pContext, pContext->aaudio.hAAudio, "AAudioStream_requestStop"); + pContext->aaudio.AAudio_createStreamBuilder = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudio_createStreamBuilder"); + pContext->aaudio.AAudioStreamBuilder_delete = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_delete"); + pContext->aaudio.AAudioStreamBuilder_setDeviceId = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDeviceId"); + pContext->aaudio.AAudioStreamBuilder_setDirection = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDirection"); + pContext->aaudio.AAudioStreamBuilder_setSharingMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSharingMode"); + pContext->aaudio.AAudioStreamBuilder_setFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFormat"); + pContext->aaudio.AAudioStreamBuilder_setChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setChannelCount"); + pContext->aaudio.AAudioStreamBuilder_setSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setSampleRate"); + pContext->aaudio.AAudioStreamBuilder_setBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setBufferCapacityInFrames"); + pContext->aaudio.AAudioStreamBuilder_setFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setFramesPerDataCallback"); + pContext->aaudio.AAudioStreamBuilder_setDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setDataCallback"); + pContext->aaudio.AAudioStreamBuilder_setErrorCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setErrorCallback"); + pContext->aaudio.AAudioStreamBuilder_setPerformanceMode = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setPerformanceMode"); + pContext->aaudio.AAudioStreamBuilder_setUsage = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setUsage"); + pContext->aaudio.AAudioStreamBuilder_setContentType = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setContentType"); + pContext->aaudio.AAudioStreamBuilder_setInputPreset = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setInputPreset"); + pContext->aaudio.AAudioStreamBuilder_setAllowedCapturePolicy = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_setAllowedCapturePolicy"); + pContext->aaudio.AAudioStreamBuilder_openStream = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStreamBuilder_openStream"); + pContext->aaudio.AAudioStream_close = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_close"); + pContext->aaudio.AAudioStream_getState = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getState"); + pContext->aaudio.AAudioStream_waitForStateChange = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_waitForStateChange"); + pContext->aaudio.AAudioStream_getFormat = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFormat"); + pContext->aaudio.AAudioStream_getChannelCount = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getChannelCount"); + pContext->aaudio.AAudioStream_getSampleRate = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getSampleRate"); + pContext->aaudio.AAudioStream_getBufferCapacityInFrames = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getBufferCapacityInFrames"); + pContext->aaudio.AAudioStream_getFramesPerDataCallback = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerDataCallback"); + pContext->aaudio.AAudioStream_getFramesPerBurst = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_getFramesPerBurst"); + pContext->aaudio.AAudioStream_requestStart = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStart"); + pContext->aaudio.AAudioStream_requestStop = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->aaudio.hAAudio, "AAudioStream_requestStop"); pCallbacks->onContextInit = ma_context_init__aaudio; @@ -38265,7 +38305,7 @@ static ma_result ma_context_init__aaudio(ma_context* pContext, const ma_context_ result = ma_device_job_thread_init(&jobThreadConfig, &pContext->allocationCallbacks, &pContext->aaudio.jobThread); if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->aaudio.hAAudio); + ma_dlclose(ma_context_get_log(pContext), pContext->aaudio.hAAudio); pContext->aaudio.hAAudio = NULL; return result; } @@ -39281,7 +39321,7 @@ static ma_result ma_device_start__opensl(ma_device* pDevice) return ma_result_from_OpenSL(resultSL); } - /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueu silent buffers. */ + /* In playback mode (no duplex) we need to load some initial buffers. In duplex mode we need to enqueue silent buffers. */ if (pDevice->type == ma_device_type_duplex) { MA_ZERO_MEMORY(pDevice->opensl.pBufferPlayback, pDevice->playback.internalPeriodSizeInFrames * pDevice->playback.internalPeriods * ma_get_bytes_per_frame(pDevice->playback.internalFormat, pDevice->playback.internalChannels)); } else { @@ -39402,7 +39442,7 @@ static ma_result ma_context_uninit__opensl(ma_context* pContext) static ma_result ma_dlsym_SLInterfaceID__opensl(ma_context* pContext, const char* pName, ma_handle* pHandle) { /* We need to return an error if the symbol cannot be found. This is important because there have been reports that some symbols do not exist. */ - ma_handle* p = (ma_handle*)ma_dlsym(pContext, pContext->opensl.libOpenSLES, pName); + ma_handle* p = (ma_handle*)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, pName); if (p == NULL) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol %s", pName); return MA_NO_BACKEND; @@ -39460,7 +39500,7 @@ static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_ references to the symbols and will hopefully skip the checks. */ for (i = 0; i < ma_countof(libOpenSLESNames); i += 1) { - pContext->opensl.libOpenSLES = ma_dlopen(pContext, libOpenSLESNames[i]); + pContext->opensl.libOpenSLES = ma_dlopen(ma_context_get_log(pContext), libOpenSLESNames[i]); if (pContext->opensl.libOpenSLES != NULL) { break; } @@ -39473,49 +39513,49 @@ static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_ result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ENGINE", &pContext->opensl.SL_IID_ENGINE); if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_AUDIOIODEVICECAPABILITIES", &pContext->opensl.SL_IID_AUDIOIODEVICECAPABILITIES); if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDSIMPLEBUFFERQUEUE", &pContext->opensl.SL_IID_ANDROIDSIMPLEBUFFERQUEUE); if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_RECORD", &pContext->opensl.SL_IID_RECORD); if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_PLAY", &pContext->opensl.SL_IID_PLAY); if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_OUTPUTMIX", &pContext->opensl.SL_IID_OUTPUTMIX); if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); return result; } result = ma_dlsym_SLInterfaceID__opensl(pContext, "SL_IID_ANDROIDCONFIGURATION", &pContext->opensl.SL_IID_ANDROIDCONFIGURATION); if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); return result; } - pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(pContext, pContext->opensl.libOpenSLES, "slCreateEngine"); + pContext->opensl.slCreateEngine = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->opensl.libOpenSLES, "slCreateEngine"); if (pContext->opensl.slCreateEngine == NULL) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Cannot find symbol slCreateEngine."); return MA_NO_BACKEND; } @@ -39539,7 +39579,7 @@ static ma_result ma_context_init__opensl(ma_context* pContext, const ma_context_ ma_spinlock_unlock(&g_maOpenSLSpinlock); if (result != MA_SUCCESS) { - ma_dlclose(pContext, pContext->opensl.libOpenSLES); + ma_dlclose(ma_context_get_log(pContext), pContext->opensl.libOpenSLES); ma_log_post(ma_context_get_log(pContext), MA_LOG_LEVEL_INFO, "[OpenSL] Failed to initialize OpenSL engine."); return result; } @@ -39702,95 +39742,65 @@ static ma_result ma_context_get_device_info__webaudio(ma_context* pContext, ma_d return MA_SUCCESS; } -#if !defined(MA_USE_AUDIO_WORKLETS) -static void ma_device_uninit_by_index__webaudio(ma_device* pDevice, ma_device_type deviceType, int deviceIndex) -{ - MA_ASSERT(pDevice != NULL); - - EM_ASM({ - var device = miniaudio.get_device_by_index($0); - var pAllocationCallbacks = $3; - - /* Make sure all nodes are disconnected and marked for collection. */ - if (device.scriptNode !== undefined) { - device.scriptNode.onaudioprocess = function(e) {}; /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */ - device.scriptNode.disconnect(); - device.scriptNode = undefined; - } - if (device.streamNode !== undefined) { - device.streamNode.disconnect(); - device.streamNode = undefined; - } - - /* - Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want - to clear the callback before closing. - */ - device.webaudio.close(); - device.webaudio = undefined; - - /* Can't forget to free the intermediary buffer. This is the buffer that's shared between JavaScript and C. */ - if (device.intermediaryBuffer !== undefined) { - _ma_free_emscripten(device.intermediaryBuffer, pAllocationCallbacks); - device.intermediaryBuffer = undefined; - device.intermediaryBufferView = undefined; - device.intermediaryBufferSizeInBytes = undefined; - } - - /* Make sure the device is untracked so the slot can be reused later. */ - miniaudio.untrack_device_by_index($0); - }, deviceIndex, deviceType, &pDevice->pContext->allocationCallbacks); -} -#endif - -static void ma_device_uninit_by_type__webaudio(ma_device* pDevice, ma_device_type deviceType) -{ - MA_ASSERT(pDevice != NULL); - MA_ASSERT(deviceType == ma_device_type_capture || deviceType == ma_device_type_playback); - -#if defined(MA_USE_AUDIO_WORKLETS) - if (deviceType == ma_device_type_capture) { - ma_free(pDevice->webaudio.pIntermediaryBufferCapture, &pDevice->pContext->allocationCallbacks); - ma_free(pDevice->webaudio.pStackBufferCapture, &pDevice->pContext->allocationCallbacks); - emscripten_destroy_audio_context(pDevice->webaudio.audioContextCapture); - } else { - ma_free(pDevice->webaudio.pIntermediaryBufferPlayback, &pDevice->pContext->allocationCallbacks); - ma_free(pDevice->webaudio.pStackBufferPlayback, &pDevice->pContext->allocationCallbacks); - emscripten_destroy_audio_context(pDevice->webaudio.audioContextPlayback); - } -#else - if (deviceType == ma_device_type_capture) { - ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_capture, pDevice->webaudio.indexCapture); - } else { - ma_device_uninit_by_index__webaudio(pDevice, ma_device_type_playback, pDevice->webaudio.indexPlayback); - } -#endif -} - static ma_result ma_device_uninit__webaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ma_device_uninit_by_type__webaudio(pDevice, ma_device_type_capture); - } + #if defined(MA_USE_AUDIO_WORKLETS) + { + EM_ASM({ + var device = miniaudio.get_device_by_index($0); - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ma_device_uninit_by_type__webaudio(pDevice, ma_device_type_playback); + if (device.streamNode !== undefined) { + device.streamNode.disconnect(); + device.streamNode = undefined; + } + }, pDevice->webaudio.deviceIndex); + + emscripten_destroy_web_audio_node(pDevice->webaudio.audioWorklet); + emscripten_destroy_audio_context(pDevice->webaudio.audioContext); + ma_free(pDevice->webaudio.pStackBuffer, &pDevice->pContext->allocationCallbacks); } + #else + { + EM_ASM({ + var device = miniaudio.get_device_by_index($0); + + /* Make sure all nodes are disconnected and marked for collection. */ + if (device.scriptNode !== undefined) { + device.scriptNode.onaudioprocess = function(e) {}; /* We want to reset the callback to ensure it doesn't get called after AudioContext.close() has returned. Shouldn't happen since we're disconnecting, but just to be safe... */ + device.scriptNode.disconnect(); + device.scriptNode = undefined; + } + + if (device.streamNode !== undefined) { + device.streamNode.disconnect(); + device.streamNode = undefined; + } + + /* + Stop the device. I think there is a chance the callback could get fired after calling this, hence why we want + to clear the callback before closing. + */ + device.webaudio.close(); + device.webaudio = undefined; + }, pDevice->webaudio.deviceIndex); + } + #endif + + /* Clean up the device on the JS side. */ + EM_ASM({ + miniaudio.untrack_device_by_index($0); + }, pDevice->webaudio.deviceIndex); + + ma_free(pDevice->webaudio.pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); return MA_SUCCESS; } +#if !defined(MA_USE_AUDIO_WORKLETS) static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(const ma_device_descriptor* pDescriptor, ma_uint32 nativeSampleRate, ma_performance_profile performanceProfile) { -#if defined(MA_USE_AUDIO_WORKLETS) - (void)pDescriptor; - (void)nativeSampleRate; - (void)performanceProfile; - - return 256; -#else /* There have been reports of the default buffer size being too small on some browsers. If we're using the default buffer size, we'll make sure the period size is bigger than our standard defaults. @@ -39821,8 +39831,8 @@ static ma_uint32 ma_calculate_period_size_in_frames_from_descriptor__webaudio(co } return periodSizeInFrames; -#endif } +#endif #if defined(MA_USE_AUDIO_WORKLETS) @@ -39830,20 +39840,22 @@ typedef struct { ma_device* pDevice; const ma_device_config* pConfig; - ma_device_descriptor* pDescriptor; - ma_device_type deviceType; - ma_uint32 channels; + ma_device_descriptor* pDescriptorPlayback; + ma_device_descriptor* pDescriptorCapture; } ma_audio_worklet_thread_initialized_data; static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const AudioSampleFrame* pInputs, int outputCount, AudioSampleFrame* pOutputs, int paramCount, const AudioParamFrame* pParams, void* pUserData) { ma_device* pDevice = (ma_device*)pUserData; ma_uint32 frameCount; - ma_uint32 framesProcessed; (void)paramCount; (void)pParams; + if (ma_device_get_state(pDevice) != ma_device_state_started) { + return EM_TRUE; + } + /* The Emscripten documentation says that it'll always be 128 frames being passed in. Hard coding it like that feels like a very bad idea to me. Even if it's hard coded in the backend, the API and documentation should always refer @@ -39854,38 +39866,31 @@ static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const */ frameCount = 128; - /* Run the conversion logic in a loop for robustness. */ - framesProcessed = 0; - while (framesProcessed < frameCount) { - ma_uint32 framesToProcessThisIteration = frameCount - framesProcessed; - - if (inputCount > 0) { - if (framesToProcessThisIteration > pDevice->webaudio.intermediaryBufferSizeInFramesPlayback) { - framesToProcessThisIteration = pDevice->webaudio.intermediaryBufferSizeInFramesPlayback; + if (inputCount > 0) { + /* Input data needs to be interleaved before we hand it to the client. */ + for (ma_uint32 iChannel = 0; iChannel < pDevice->capture.internalChannels; iChannel += 1) { + for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { + pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->capture.internalChannels + iChannel] = pInputs[0].data[frameCount*iChannel + iFrame]; } - - /* Input data needs to be interleaved before we hand it to the client. */ - for (ma_uint32 iFrame = 0; iFrame < framesToProcessThisIteration; iFrame += 1) { - for (ma_uint32 iChannel = 0; iChannel < pDevice->capture.internalChannels; iChannel += 1) { - pDevice->webaudio.pIntermediaryBufferCapture[iFrame*pDevice->capture.internalChannels + iChannel] = pInputs[0].data[frameCount*iChannel + framesProcessed + iFrame]; - } - } - - ma_device_process_pcm_frames_capture__webaudio(pDevice, framesToProcessThisIteration, pDevice->webaudio.pIntermediaryBufferCapture); } - if (outputCount > 0) { - ma_device_process_pcm_frames_playback__webaudio(pDevice, framesToProcessThisIteration, pDevice->webaudio.pIntermediaryBufferPlayback); + ma_device_process_pcm_frames_capture__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); + } + + if (outputCount > 0) { + /* If it's a capture-only device, we'll need to output silence. */ + if (pDevice->type == ma_device_type_capture) { + MA_ZERO_MEMORY(pOutputs[0].data, frameCount * pDevice->playback.internalChannels * sizeof(float)); + } else { + ma_device_process_pcm_frames_playback__webaudio(pDevice, frameCount, pDevice->webaudio.pIntermediaryBuffer); /* We've read the data from the client. Now we need to deinterleave the buffer and output to the output buffer. */ - for (ma_uint32 iFrame = 0; iFrame < framesToProcessThisIteration; iFrame += 1) { - for (ma_uint32 iChannel = 0; iChannel < pDevice->playback.internalChannels; iChannel += 1) { - pOutputs[0].data[frameCount*iChannel + framesProcessed + iFrame] = pDevice->webaudio.pIntermediaryBufferPlayback[iFrame*pDevice->playback.internalChannels + iChannel]; + for (ma_uint32 iChannel = 0; iChannel < pDevice->playback.internalChannels; iChannel += 1) { + for (ma_uint32 iFrame = 0; iFrame < frameCount; iFrame += 1) { + pOutputs[0].data[frameCount*iChannel + iFrame] = pDevice->webaudio.pIntermediaryBuffer[iFrame*pDevice->playback.internalChannels + iChannel]; } } } - - framesProcessed += framesToProcessThisIteration; } return EM_TRUE; @@ -39895,76 +39900,135 @@ static EM_BOOL ma_audio_worklet_process_callback__webaudio(int inputCount, const static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) { ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; - EmscriptenAudioWorkletNodeCreateOptions workletNodeOptions; - EMSCRIPTEN_AUDIO_WORKLET_NODE_T workletNode; - int outputChannelCount = 0; + EmscriptenAudioWorkletNodeCreateOptions audioWorkletOptions; + int channels = 0; + size_t intermediaryBufferSizeInFrames; + int sampleRate; if (success == EM_FALSE) { - pParameters->pDevice->webaudio.isInitialized = MA_TRUE; + pParameters->pDevice->webaudio.initResult = MA_ERROR; + ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); return; } - MA_ZERO_OBJECT(&workletNodeOptions); - - if (pParameters->deviceType == ma_device_type_capture) { - workletNodeOptions.numberOfInputs = 1; - } else { - outputChannelCount = (int)pParameters->channels; /* Safe cast. */ - - workletNodeOptions.numberOfOutputs = 1; - workletNodeOptions.outputChannelCounts = &outputChannelCount; - } - - /* Here is where we create the node that will do our processing. */ - workletNode = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &workletNodeOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice); - - if (pParameters->deviceType == ma_device_type_capture) { - pParameters->pDevice->webaudio.workletNodeCapture = workletNode; - } else { - pParameters->pDevice->webaudio.workletNodePlayback = workletNode; - } + /* The next step is to initialize the audio worklet node. */ + MA_ZERO_OBJECT(&audioWorkletOptions); /* - With the worklet node created we can now attach it to the graph. This is done differently depending on whether or not - it's capture or playback mode. + The way channel counts work with Web Audio is confusing. As far as I can tell, there's no way to know the channel + count from MediaStreamAudioSourceNode (what we use for capture)? The only way to have control is to configure an + output channel count on the capture side. This is slightly confusing for capture mode because intuitively you + wouldn't actually connect an output to an input-only node, but this is what we'll have to do in order to have + proper control over the channel count. In the capture case, we'll have to output silence to it's output node. */ - if (pParameters->deviceType == ma_device_type_capture) { - EM_ASM({ - var workletNode = emscriptenGetAudioObject($0); + if (pParameters->pConfig->deviceType == ma_device_type_capture) { + channels = (int)((pParameters->pDescriptorCapture->channels > 0) ? pParameters->pDescriptorCapture->channels : MA_DEFAULT_CHANNELS); + audioWorkletOptions.numberOfInputs = 1; + } else { + channels = (int)((pParameters->pDescriptorPlayback->channels > 0) ? pParameters->pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS); + + if (pParameters->pConfig->deviceType == ma_device_type_duplex) { + audioWorkletOptions.numberOfInputs = 1; + } else { + audioWorkletOptions.numberOfInputs = 0; + } + } + + audioWorkletOptions.numberOfOutputs = 1; + audioWorkletOptions.outputChannelCounts = &channels; + + + /* + Now that we know the channel count to use we can allocate the intermediary buffer. The + intermediary buffer is used for interleaving and deinterleaving. + */ + intermediaryBufferSizeInFrames = 128; + + pParameters->pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(intermediaryBufferSizeInFrames * (ma_uint32)channels * sizeof(float), &pParameters->pDevice->pContext->allocationCallbacks); + if (pParameters->pDevice->webaudio.pIntermediaryBuffer == NULL) { + pParameters->pDevice->webaudio.initResult = MA_OUT_OF_MEMORY; + ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); + return; + } + + + pParameters->pDevice->webaudio.audioWorklet = emscripten_create_wasm_audio_worklet_node(audioContext, "miniaudio", &audioWorkletOptions, &ma_audio_worklet_process_callback__webaudio, pParameters->pDevice); + + /* With the audio worklet initialized we can now attach it to the graph. */ + if (pParameters->pConfig->deviceType == ma_device_type_capture || pParameters->pConfig->deviceType == ma_device_type_duplex) { + ma_result attachmentResult = EM_ASM_INT({ + var getUserMediaResult = 0; + var audioWorklet = emscriptenGetAudioObject($0); var audioContext = emscriptenGetAudioObject($1); navigator.mediaDevices.getUserMedia({audio:true, video:false}) .then(function(stream) { audioContext.streamNode = audioContext.createMediaStreamSource(stream); - audioContext.streamNode.connect(workletNode); - - /* - Now that the worklet node has been connected, do we need to inspect workletNode.channelCount - to check the actual channel count, or is it safe to assume it's always 2? - */ + audioContext.streamNode.connect(audioWorklet); + audioWorklet.connect(audioContext.destination); + getUserMediaResult = 0; /* 0 = MA_SUCCESS */ }) .catch(function(error) { - + console.log("navigator.mediaDevices.getUserMedia Failed: " + error); + getUserMediaResult = -1; /* -1 = MA_ERROR */ }); - }, workletNode, audioContext); - } else { - EM_ASM({ - var workletNode = emscriptenGetAudioObject($0); - var audioContext = emscriptenGetAudioObject($1); - workletNode.connect(audioContext.destination); - }, workletNode, audioContext); + + return getUserMediaResult; + }, pParameters->pDevice->webaudio.audioWorklet, audioContext); + + if (attachmentResult != MA_SUCCESS) { + ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect capture node."); + emscripten_destroy_web_audio_node(pParameters->pDevice->webaudio.audioWorklet); + pParameters->pDevice->webaudio.initResult = attachmentResult; + ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); + return; + } } - pParameters->pDevice->webaudio.isInitialized = MA_TRUE; + /* If it's playback only we can now attach the worklet node to the graph. This has already been done for the duplex case. */ + if (pParameters->pConfig->deviceType == ma_device_type_playback) { + ma_result attachmentResult = EM_ASM_INT({ + var audioWorklet = emscriptenGetAudioObject($0); + var audioContext = emscriptenGetAudioObject($1); + audioWorklet.connect(audioContext.destination); + return 0; /* 0 = MA_SUCCESS */ + }, pParameters->pDevice->webaudio.audioWorklet, audioContext); - ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_DEBUG, "AudioWorklets: Created worklet node: %d\n", workletNode); + if (attachmentResult != MA_SUCCESS) { + ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_ERROR, "Web Audio: Failed to connect playback node."); + pParameters->pDevice->webaudio.initResult = attachmentResult; + ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); + return; + } + } - /* Our parameter data is no longer needed. */ + /* We need to update the descriptors so that they reflect the internal data format. Both capture and playback should be the same. */ + sampleRate = EM_ASM_INT({ return emscriptenGetAudioObject($0).sampleRate; }, audioContext); + + if (pParameters->pDescriptorCapture != NULL) { + pParameters->pDescriptorCapture->format = ma_format_f32; + pParameters->pDescriptorCapture->channels = (ma_uint32)channels; + pParameters->pDescriptorCapture->sampleRate = (ma_uint32)sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorCapture->channelMap, ma_countof(pParameters->pDescriptorCapture->channelMap), pParameters->pDescriptorCapture->channels); + pParameters->pDescriptorCapture->periodSizeInFrames = intermediaryBufferSizeInFrames; + pParameters->pDescriptorCapture->periodCount = 1; + } + + if (pParameters->pDescriptorPlayback != NULL) { + pParameters->pDescriptorPlayback->format = ma_format_f32; + pParameters->pDescriptorPlayback->channels = (ma_uint32)channels; + pParameters->pDescriptorPlayback->sampleRate = (ma_uint32)sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pParameters->pDescriptorPlayback->channelMap, ma_countof(pParameters->pDescriptorPlayback->channelMap), pParameters->pDescriptorPlayback->channels); + pParameters->pDescriptorPlayback->periodSizeInFrames = intermediaryBufferSizeInFrames; + pParameters->pDescriptorPlayback->periodCount = 1; + } + + /* At this point we're done and we can return. */ + ma_log_postf(ma_device_get_log(pParameters->pDevice), MA_LOG_LEVEL_DEBUG, "AudioWorklets: Created worklet node: %d\n", pParameters->pDevice->webaudio.audioWorklet); + pParameters->pDevice->webaudio.initResult = MA_SUCCESS; ma_free(pParameters, &pParameters->pDevice->pContext->allocationCallbacks); } - - static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T audioContext, EM_BOOL success, void* pUserData) { ma_audio_worklet_thread_initialized_data* pParameters = (ma_audio_worklet_thread_initialized_data*)pUserData; @@ -39973,7 +40037,7 @@ static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T MA_ASSERT(pParameters != NULL); if (success == EM_FALSE) { - pParameters->pDevice->webaudio.isInitialized = MA_TRUE; + pParameters->pDevice->webaudio.initResult = MA_ERROR; return; } @@ -39984,327 +40048,8 @@ static void ma_audio_worklet_thread_initialized__webaudio(EMSCRIPTEN_WEBAUDIO_T } #endif -static ma_result ma_device_init_by_type__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptor, ma_device_type deviceType) -{ -#if defined(MA_USE_AUDIO_WORKLETS) - EMSCRIPTEN_WEBAUDIO_T audioContext; - void* pStackBuffer; - size_t intermediaryBufferSizeInFrames; - float* pIntermediaryBuffer; -#endif - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint32 periodSizeInFrames; - - MA_ASSERT(pDevice != NULL); - MA_ASSERT(pConfig != NULL); - MA_ASSERT(deviceType != ma_device_type_duplex); - - if (deviceType == ma_device_type_capture && !ma_is_capture_supported__webaudio()) { - return MA_NO_DEVICE; - } - - /* We're going to calculate some stuff in C just to simplify the JS code. */ - channels = (pDescriptor->channels > 0) ? pDescriptor->channels : MA_DEFAULT_CHANNELS; - sampleRate = (pDescriptor->sampleRate > 0) ? pDescriptor->sampleRate : MA_DEFAULT_SAMPLE_RATE; - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptor, sampleRate, pConfig->performanceProfile); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "periodSizeInFrames = %d\n", (int)periodSizeInFrames); - -#if defined(MA_USE_AUDIO_WORKLETS) - { - ma_audio_worklet_thread_initialized_data* pInitParameters; - EmscriptenWebAudioCreateAttributes audioContextAttributes; - - audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE; - audioContextAttributes.sampleRate = sampleRate; - - /* It's not clear if this can return an error. None of the tests in the Emscripten repository check for this, so neither am I for now. */ - audioContext = emscripten_create_audio_context(&audioContextAttributes); - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: AUDIO CONTEXT CREATED\n"); - - /* - We now need to create a worker thread. This is a bit weird because we need to allocate our - own buffer for the thread's stack. The stack needs to be aligned to 16 bytes. I'm going to - allocate this on the heap to keep it simple. - */ - pStackBuffer = ma_aligned_malloc(MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, 16, &pDevice->pContext->allocationCallbacks); - if (pStackBuffer == NULL) { - emscripten_destroy_audio_context(audioContext); - return MA_OUT_OF_MEMORY; - } - - /* - We need an intermediary buffer for data conversion. WebAudio reports data in uninterleaved - format whereas we require it to be interleaved. We'll do this in chunks of 128 frames. - */ - intermediaryBufferSizeInFrames = 128; - pIntermediaryBuffer = ma_malloc(intermediaryBufferSizeInFrames * channels * sizeof(float), &pDevice->pContext->allocationCallbacks); - if (pIntermediaryBuffer == NULL) { - ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); - emscripten_destroy_audio_context(audioContext); - return MA_OUT_OF_MEMORY; - } - - pInitParameters = ma_malloc(sizeof(*pInitParameters), &pDevice->pContext->allocationCallbacks); - if (pInitParameters == NULL) { - ma_free(pIntermediaryBuffer, &pDevice->pContext->allocationCallbacks); - ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); - emscripten_destroy_audio_context(audioContext); - return MA_OUT_OF_MEMORY; - } - - pInitParameters->pDevice = pDevice; - pInitParameters->pConfig = pConfig; - pInitParameters->pDescriptor = pDescriptor; - pInitParameters->deviceType = deviceType; - pInitParameters->channels = channels; - - /* - We need to flag the device as not yet initialized so we can wait on it later. Unfortunately all of - the Emscripten WebAudio stuff is asynchronous. - */ - pDevice->webaudio.isInitialized = MA_FALSE; - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: CREATING WORKLET\n"); - - emscripten_start_wasm_audio_worklet_thread_async(audioContext, pStackBuffer, MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, ma_audio_worklet_thread_initialized__webaudio, pInitParameters); - - /* We must wait for initialization to complete. We're just spinning here. The emscripten_sleep() call is why we need to build with `-sASYNCIFY`. */ - while (pDevice->webaudio.isInitialized == MA_FALSE) { - emscripten_sleep(1); - } - - /* - Now that initialization is finished we can go ahead and extract our channel count so that - miniaudio can set up a data converter at a higher level. - */ - if (deviceType == ma_device_type_capture) { - /* - For capture we won't actually know what the channel count is. Everything I've seen seems - to indicate that the default channel count is 2, so I'm sticking with that. - */ - channels = 2; - } else { - /* Get the channel count from the audio context. */ - channels = (ma_uint32)EM_ASM_INT({ - return emscriptenGetAudioObject($0).destination.channelCount; - }, audioContext); - } - - ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_DEBUG, "TRACE: INITIALIZED. channels = %u\n", channels); - } -#else - /* We create the device on the JavaScript side and reference it using an index. We use this to make it possible to reference the device between JavaScript and C. */ - int deviceIndex = EM_ASM_INT({ - var channels = $0; - var sampleRate = $1; - var bufferSize = $2; /* In PCM frames. */ - var isCapture = $3; - var pDevice = $4; - var pAllocationCallbacks = $5; - - if (typeof(window.miniaudio) === 'undefined') { - return -1; /* Context not initialized. */ - } - - var device = {}; - - /* The AudioContext must be created in a suspended state. */ - device.webaudio = new (window.AudioContext || window.webkitAudioContext)({sampleRate:sampleRate}); - device.webaudio.suspend(); - device.state = 1; /* ma_device_state_stopped */ - - /* We need an intermediary buffer which we use for JavaScript and C interop. This buffer stores interleaved f32 PCM data. */ - device.intermediaryBufferSizeInBytes = channels * bufferSize * 4; - device.intermediaryBuffer = _ma_malloc_emscripten(device.intermediaryBufferSizeInBytes, pAllocationCallbacks); - device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes); - - /* - Both playback and capture devices use a ScriptProcessorNode for performing per-sample operations. - - ScriptProcessorNode is actually deprecated so this is likely to be temporary. The way this works for playback is very simple. You just set a callback - that's periodically fired, just like a normal audio callback function. But apparently this design is "flawed" and is now deprecated in favour of - something called AudioWorklets which _forces_ you to load a _separate_ .js file at run time... nice... Hopefully ScriptProcessorNode will continue to - work for years to come, but this may need to change to use AudioSourceBufferNode instead, which I think is what Emscripten uses for it's built-in SDL - implementation. I'll be avoiding that insane AudioWorklet API like the plague... - - For capture it is a bit unintuitive. We use the ScriptProccessorNode _only_ to get the raw PCM data. It is connected to an AudioContext just like the - playback case, however we just output silence to the AudioContext instead of passing any real data. It would make more sense to me to use the - MediaRecorder API, but unfortunately you need to specify a MIME time (Opus, Vorbis, etc.) for the binary blob that's returned to the client, but I've - been unable to figure out how to get this as raw PCM. The closest I can think is to use the MIME type for WAV files and just parse it, but I don't know - how well this would work. Although ScriptProccessorNode is deprecated, in practice it seems to have pretty good browser support so I'm leaving it like - this for now. If anyone knows how I could get raw PCM data using the MediaRecorder API please let me know! - */ - device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, (isCapture) ? channels : 0, (isCapture) ? 0 : channels); - - if (isCapture) { - device.scriptNode.onaudioprocess = function(e) { - if (device.intermediaryBuffer === undefined) { - return; /* This means the device has been uninitialized. */ - } - - if (device.intermediaryBufferView.length == 0) { - /* Recreate intermediaryBufferView when losing reference to the underlying buffer, probably due to emscripten resizing heap. */ - device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes); - } - - /* Make sure silence it output to the AudioContext destination. Not doing this will cause sound to come out of the speakers! */ - for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { - e.outputBuffer.getChannelData(iChannel).fill(0.0); - } - - /* There are some situations where we may want to send silence to the client. */ - var sendSilence = false; - if (device.streamNode === undefined) { - sendSilence = true; - } - - /* Sanity check. This will never happen, right? */ - if (e.inputBuffer.numberOfChannels != channels) { - console.log("Capture: Channel count mismatch. " + e.inputBufer.numberOfChannels + " != " + channels + ". Sending silence."); - sendSilence = true; - } - - /* This looped design guards against the situation where e.inputBuffer is a different size to the original buffer size. Should never happen in practice. */ - var totalFramesProcessed = 0; - while (totalFramesProcessed < e.inputBuffer.length) { - var framesRemaining = e.inputBuffer.length - totalFramesProcessed; - var framesToProcess = framesRemaining; - if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) { - framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4); - } - - /* We need to do the reverse of the playback case. We need to interleave the input data and copy it into the intermediary buffer. Then we send it to the client. */ - if (sendSilence) { - device.intermediaryBufferView.fill(0.0); - } else { - for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) { - for (var iChannel = 0; iChannel < e.inputBuffer.numberOfChannels; ++iChannel) { - device.intermediaryBufferView[iFrame*channels + iChannel] = e.inputBuffer.getChannelData(iChannel)[totalFramesProcessed + iFrame]; - } - } - } - - /* Send data to the client from our intermediary buffer. */ - _ma_device_process_pcm_frames_capture__webaudio(pDevice, framesToProcess, device.intermediaryBuffer); - - totalFramesProcessed += framesToProcess; - } - }; - - navigator.mediaDevices.getUserMedia({audio:true, video:false}) - .then(function(stream) { - device.streamNode = device.webaudio.createMediaStreamSource(stream); - device.streamNode.connect(device.scriptNode); - device.scriptNode.connect(device.webaudio.destination); - }) - .catch(function(error) { - /* I think this should output silence... */ - device.scriptNode.connect(device.webaudio.destination); - }); - } else { - device.scriptNode.onaudioprocess = function(e) { - if (device.intermediaryBuffer === undefined) { - return; /* This means the device has been uninitialized. */ - } - - if(device.intermediaryBufferView.length == 0) { - /* Recreate intermediaryBufferView when losing reference to the underlying buffer, probably due to emscripten resizing heap. */ - device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, device.intermediaryBuffer, device.intermediaryBufferSizeInBytes); - } - - var outputSilence = false; - - /* Sanity check. This will never happen, right? */ - if (e.outputBuffer.numberOfChannels != channels) { - console.log("Playback: Channel count mismatch. " + e.outputBufer.numberOfChannels + " != " + channels + ". Outputting silence."); - outputSilence = true; - return; - } - - /* This looped design guards against the situation where e.outputBuffer is a different size to the original buffer size. Should never happen in practice. */ - var totalFramesProcessed = 0; - while (totalFramesProcessed < e.outputBuffer.length) { - var framesRemaining = e.outputBuffer.length - totalFramesProcessed; - var framesToProcess = framesRemaining; - if (framesToProcess > (device.intermediaryBufferSizeInBytes/channels/4)) { - framesToProcess = (device.intermediaryBufferSizeInBytes/channels/4); - } - - /* Read data from the client into our intermediary buffer. */ - _ma_device_process_pcm_frames_playback__webaudio(pDevice, framesToProcess, device.intermediaryBuffer); - - /* At this point we'll have data in our intermediary buffer which we now need to deinterleave and copy over to the output buffers. */ - if (outputSilence) { - for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { - e.outputBuffer.getChannelData(iChannel).fill(0.0); - } - } else { - for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { - var outputBuffer = e.outputBuffer.getChannelData(iChannel); - var intermediaryBuffer = device.intermediaryBufferView; - for (var iFrame = 0; iFrame < framesToProcess; ++iFrame) { - outputBuffer[totalFramesProcessed + iFrame] = intermediaryBuffer[iFrame*channels + iChannel]; - } - } - } - - totalFramesProcessed += framesToProcess; - } - }; - - device.scriptNode.connect(device.webaudio.destination); - } - - return miniaudio.track_device(device); - }, channels, sampleRate, periodSizeInFrames, deviceType == ma_device_type_capture, pDevice, &pDevice->pContext->allocationCallbacks); - - if (deviceIndex < 0) { - return MA_FAILED_TO_OPEN_BACKEND_DEVICE; - } -#endif - -#if defined(MA_USE_AUDIO_WORKLETS) - if (deviceType == ma_device_type_capture) { - pDevice->webaudio.audioContextCapture = audioContext; - pDevice->webaudio.pStackBufferCapture = pStackBuffer; - pDevice->webaudio.intermediaryBufferSizeInFramesCapture = intermediaryBufferSizeInFrames; - pDevice->webaudio.pIntermediaryBufferCapture = pIntermediaryBuffer; - } else { - pDevice->webaudio.audioContextPlayback = audioContext; - pDevice->webaudio.pStackBufferPlayback = pStackBuffer; - pDevice->webaudio.intermediaryBufferSizeInFramesPlayback = intermediaryBufferSizeInFrames; - pDevice->webaudio.pIntermediaryBufferPlayback = pIntermediaryBuffer; - } -#else - if (deviceType == ma_device_type_capture) { - pDevice->webaudio.indexCapture = deviceIndex; - } else { - pDevice->webaudio.indexPlayback = deviceIndex; - } -#endif - - pDescriptor->format = ma_format_f32; - pDescriptor->channels = channels; - ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptor->channelMap, ma_countof(pDescriptor->channelMap), pDescriptor->channels); - pDescriptor->periodSizeInFrames = periodSizeInFrames; - pDescriptor->periodCount = 1; - -#if defined(MA_USE_AUDIO_WORKLETS) - pDescriptor->sampleRate = sampleRate; /* Is this good enough to be used in the general case? */ -#else - pDescriptor->sampleRate = EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); -#endif - - return MA_SUCCESS; -} - static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_config* pConfig, ma_device_descriptor* pDescriptorPlayback, ma_device_descriptor* pDescriptorCapture) { - ma_result result; - if (pConfig->deviceType == ma_device_type_loopback) { return MA_DEVICE_TYPE_NOT_SUPPORTED; } @@ -40315,55 +40060,269 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co return MA_SHARE_MODE_NOT_SUPPORTED; } - if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_by_type__webaudio(pDevice, pConfig, pDescriptorCapture, ma_device_type_capture); - if (result != MA_SUCCESS) { - return result; - } - } + /* + With AudioWorklets we'll have just a single AudioContext. I'm not sure why I'm not doing this for ScriptProcessorNode so + it might be worthwhile to look into that as well. + */ + #if defined(MA_USE_AUDIO_WORKLETS) + { + EmscriptenWebAudioCreateAttributes audioContextAttributes; + ma_audio_worklet_thread_initialized_data* pInitParameters; + void* pStackBuffer; - if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - result = ma_device_init_by_type__webaudio(pDevice, pConfig, pDescriptorPlayback, ma_device_type_playback); - if (result != MA_SUCCESS) { - if (pConfig->deviceType == ma_device_type_duplex) { - ma_device_uninit_by_type__webaudio(pDevice, ma_device_type_capture); + if (pConfig->performanceProfile == ma_performance_profile_conservative) { + audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_PLAYBACK; + } else { + audioContextAttributes.latencyHint = MA_WEBAUDIO_LATENCY_HINT_INTERACTIVE; + } + + /* + In my testing, Firefox does not seem to capture audio data properly if the sample rate is set + to anything other than 48K. This does not seem to be the case for other browsers. For this reason, + if the device type is anything other than playback, we'll leave the sample rate as-is and let the + browser pick the appropriate rate for us. + */ + if (pConfig->deviceType == ma_device_type_playback) { + audioContextAttributes.sampleRate = pDescriptorPlayback->sampleRate; + } else { + audioContextAttributes.sampleRate = 0; + } + + /* It's not clear if this can return an error. None of the tests in the Emscripten repository check for this, so neither am I for now. */ + pDevice->webaudio.audioContext = emscripten_create_audio_context(&audioContextAttributes); + + + /* + With the context created we can now create the worklet. We can only have a single worklet per audio + context which means we'll need to craft this appropriately to handle duplex devices correctly. + */ + + /* + We now need to create a worker thread. This is a bit weird because we need to allocate our + own buffer for the thread's stack. The stack needs to be aligned to 16 bytes. I'm going to + allocate this on the heap to keep it simple. + */ + pStackBuffer = ma_aligned_malloc(MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, 16, &pDevice->pContext->allocationCallbacks); + if (pStackBuffer == NULL) { + emscripten_destroy_audio_context(pDevice->webaudio.audioContext); + return MA_OUT_OF_MEMORY; + } + + /* Our thread initialization parameters need to be allocated on the heap so they don't go out of scope. */ + pInitParameters = (ma_audio_worklet_thread_initialized_data*)ma_malloc(sizeof(*pInitParameters), &pDevice->pContext->allocationCallbacks); + if (pInitParameters == NULL) { + ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); + emscripten_destroy_audio_context(pDevice->webaudio.audioContext); + return MA_OUT_OF_MEMORY; + } + + pInitParameters->pDevice = pDevice; + pInitParameters->pConfig = pConfig; + pInitParameters->pDescriptorPlayback = pDescriptorPlayback; + pInitParameters->pDescriptorCapture = pDescriptorCapture; + + /* + We need to flag the device as not yet initialized so we can wait on it later. Unfortunately all of + the Emscripten WebAudio stuff is asynchronous. + */ + pDevice->webaudio.initResult = MA_BUSY; + { + emscripten_start_wasm_audio_worklet_thread_async(pDevice->webaudio.audioContext, pStackBuffer, MA_AUDIO_WORKLETS_THREAD_STACK_SIZE, ma_audio_worklet_thread_initialized__webaudio, pInitParameters); + } + while (pDevice->webaudio.initResult == MA_BUSY) { emscripten_sleep(1); } /* We must wait for initialization to complete. We're just spinning here. The emscripten_sleep() call is why we need to build with `-sASYNCIFY`. */ + + /* Initialization is now complete. Descriptors were updated when the worklet was initialized. */ + if (pDevice->webaudio.initResult != MA_SUCCESS) { + ma_free(pStackBuffer, &pDevice->pContext->allocationCallbacks); + emscripten_destroy_audio_context(pDevice->webaudio.audioContext); + return pDevice->webaudio.initResult; + } + + /* We need to add an entry to the miniaudio.devices list on the JS side so we can do some JS/C interop. */ + pDevice->webaudio.deviceIndex = EM_ASM_INT({ + return miniaudio.track_device({ + webaudio: emscriptenGetAudioObject($0), + state: 1 /* 1 = ma_device_state_stopped */ + }); + }, pDevice->webaudio.audioContext); + + return MA_SUCCESS; + } + #else + { + /* ScriptProcessorNode. This path requires us to do almost everything in JS, but we'll do as much as we can in C. */ + ma_uint32 deviceIndex; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint32 periodSizeInFrames; + + /* The channel count will depend on the device type. If it's a capture, use it's, otherwise use the playback side. */ + if (pConfig->deviceType == ma_device_type_capture) { + channels = (pDescriptorCapture->channels > 0) ? pDescriptorCapture->channels : MA_DEFAULT_CHANNELS; + } else { + channels = (pDescriptorPlayback->channels > 0) ? pDescriptorPlayback->channels : MA_DEFAULT_CHANNELS; + } + + /* + When testing in Firefox, I've seen it where capture mode fails if the sample rate is changed to anything other than it's + native rate. For this reason we're leaving the sample rate untouched for capture devices. + */ + if (pConfig->deviceType == ma_device_type_playback) { + sampleRate = pDescriptorPlayback->sampleRate; + } else { + sampleRate = 0; /* Let the browser decide when capturing. */ + } + + /* The period size needs to be a power of 2. */ + if (pConfig->deviceType == ma_device_type_capture) { + periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorCapture, sampleRate, pConfig->performanceProfile); + } else { + periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__webaudio(pDescriptorPlayback, sampleRate, pConfig->performanceProfile); + } + + /* We need an intermediary buffer for doing interleaving and deinterleaving. */ + pDevice->webaudio.pIntermediaryBuffer = (float*)ma_malloc(periodSizeInFrames * channels * sizeof(float), &pDevice->pContext->allocationCallbacks); + if (pDevice->webaudio.pIntermediaryBuffer == NULL) { + return MA_OUT_OF_MEMORY; + } + + deviceIndex = EM_ASM_INT({ + var deviceType = $0; + var channels = $1; + var sampleRate = $2; + var bufferSize = $3; + var pIntermediaryBuffer = $4; + var pDevice = $5; + + if (typeof(window.miniaudio) === 'undefined') { + return -1; /* Context not initialized. */ } - return result; - } - } - return MA_SUCCESS; + var device = {}; + + /* First thing we need is an AudioContext. */ + var audioContextOptions = {}; + if (deviceType == window.miniaudio.device_type.playback) { + audioContextOptions.sampleRate = sampleRate; + } + + device.webaudio = new (window.AudioContext || window.webkitAudioContext)(audioContextOptions); + device.webaudio.suspend(); /* The AudioContext must be created in a suspended state. */ + device.state = window.miniaudio.device_state.stopped; + + /* + We need to create a ScriptProcessorNode. The channel situation is the same as the AudioWorklet path in that we + need to specify an output and configure the channel count there. + */ + var channelCountIn = 0; + var channelCountOut = channels; + if (deviceType != window.miniaudio.device_type.playback) { + channelCountIn = channels; + } + + device.scriptNode = device.webaudio.createScriptProcessor(bufferSize, channelCountIn, channelCountOut); + + /* The node processing callback. */ + device.scriptNode.onaudioprocess = function(e) { + if (device.intermediaryBufferView == null || device.intermediaryBufferView.length == 0) { + device.intermediaryBufferView = new Float32Array(Module.HEAPF32.buffer, pIntermediaryBuffer, bufferSize * channels); + } + + /* Do the capture side first. */ + if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) { + /* The data must be interleaved before being processed miniaudio. */ + for (var iChannel = 0; iChannel < channels; iChannel += 1) { + var inputBuffer = e.inputBuffer.getChannelData(iChannel); + var intermediaryBuffer = device.intermediaryBufferView; + + for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) { + intermediaryBuffer[iFrame*channels + iChannel] = inputBuffer[iFrame]; + } + } + + _ma_device_process_pcm_frames_capture__webaudio(pDevice, bufferSize, pIntermediaryBuffer); + } + + if (deviceType == miniaudio.device_type.playback || deviceType == miniaudio.device_type.duplex) { + _ma_device_process_pcm_frames_playback__webaudio(pDevice, bufferSize, pIntermediaryBuffer); + + for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { + var outputBuffer = e.outputBuffer.getChannelData(iChannel); + var intermediaryBuffer = device.intermediaryBufferView; + + for (var iFrame = 0; iFrame < bufferSize; iFrame += 1) { + outputBuffer[iFrame] = intermediaryBuffer[iFrame*channels + iChannel]; + } + } + } else { + /* It's a capture-only device. Make sure the output is silenced. */ + for (var iChannel = 0; iChannel < e.outputBuffer.numberOfChannels; ++iChannel) { + e.outputBuffer.getChannelData(iChannel).fill(0.0); + } + } + }; + + /* Now we need to connect our node to the graph. */ + if (deviceType == miniaudio.device_type.capture || deviceType == miniaudio.device_type.duplex) { + navigator.mediaDevices.getUserMedia({audio:true, video:false}) + .then(function(stream) { + device.streamNode = device.webaudio.createMediaStreamSource(stream); + device.streamNode.connect(device.scriptNode); + device.scriptNode.connect(device.webaudio.destination); + }) + .catch(function(error) { + console.log("Failed to get user media: " + error); + }); + } + + if (deviceType == miniaudio.device_type.playback) { + device.scriptNode.connect(device.webaudio.destination); + } + + return miniaudio.track_device(device); + }, pConfig->deviceType, channels, sampleRate, periodSizeInFrames, pDevice->webaudio.pIntermediaryBuffer, pDevice); + + if (deviceIndex < 0) { + return MA_FAILED_TO_OPEN_BACKEND_DEVICE; + } + + pDevice->webaudio.deviceIndex = deviceIndex; + + /* Grab the sample rate from the audio context directly. */ + sampleRate = (ma_uint32)EM_ASM_INT({ return miniaudio.get_device_by_index($0).webaudio.sampleRate; }, deviceIndex); + + if (pDescriptorCapture != NULL) { + pDescriptorCapture->format = ma_format_f32; + pDescriptorCapture->channels = channels; + pDescriptorCapture->sampleRate = sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorCapture->channelMap, ma_countof(pDescriptorCapture->channelMap), pDescriptorCapture->channels); + pDescriptorCapture->periodSizeInFrames = periodSizeInFrames; + pDescriptorCapture->periodCount = 1; + } + + if (pDescriptorPlayback != NULL) { + pDescriptorPlayback->format = ma_format_f32; + pDescriptorPlayback->channels = channels; + pDescriptorPlayback->sampleRate = sampleRate; + ma_channel_map_init_standard(ma_standard_channel_map_webaudio, pDescriptorPlayback->channelMap, ma_countof(pDescriptorPlayback->channelMap), pDescriptorPlayback->channels); + pDescriptorPlayback->periodSizeInFrames = periodSizeInFrames; + pDescriptorPlayback->periodCount = 1; + } + + return MA_SUCCESS; + } + #endif } static ma_result ma_device_start__webaudio(ma_device* pDevice) { MA_ASSERT(pDevice != NULL); -#if defined(MA_USE_AUDIO_WORKLETS) - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - emscripten_resume_audio_context_sync(pDevice->webaudio.audioContextCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - emscripten_resume_audio_context_sync(pDevice->webaudio.audioContextPlayback); - } -#else - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - EM_ASM({ - var device = miniaudio.get_device_by_index($0); - device.webaudio.resume(); - device.state = 2; /* ma_device_state_started */ - }, pDevice->webaudio.indexCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - EM_ASM({ - var device = miniaudio.get_device_by_index($0); - device.webaudio.resume(); - device.state = 2; /* ma_device_state_started */ - }, pDevice->webaudio.indexPlayback); - } -#endif + EM_ASM({ + var device = miniaudio.get_device_by_index($0); + device.webaudio.resume(); + device.state = miniaudio.device_state.started; + }, pDevice->webaudio.deviceIndex); return MA_SUCCESS; } @@ -40381,37 +40340,11 @@ static ma_result ma_device_stop__webaudio(ma_device* pDevice) I read this to mean that "any current context processing blocks" are processed by suspend() - i.e. They they are drained. We therefore shouldn't need to do any kind of explicit draining. */ - -#if defined(MA_USE_AUDIO_WORKLETS) - /* I can't seem to find a way to suspend an AudioContext via the C Emscripten API. Is this an oversight? */ - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - EM_ASM({ - emscriptenGetAudioObject($0).suspend(); - }, pDevice->webaudio.audioContextCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - EM_ASM({ - emscriptenGetAudioObject($0).suspend(); - }, pDevice->webaudio.audioContextPlayback); - } -#else - if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - EM_ASM({ - var device = miniaudio.get_device_by_index($0); - device.webaudio.suspend(); - device.state = 1; /* ma_device_state_stopped */ - }, pDevice->webaudio.indexCapture); - } - - if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - EM_ASM({ - var device = miniaudio.get_device_by_index($0); - device.webaudio.suspend(); - device.state = 1; /* ma_device_state_stopped */ - }, pDevice->webaudio.indexPlayback); - } -#endif + EM_ASM({ + var device = miniaudio.get_device_by_index($0); + device.webaudio.suspend(); + device.state = miniaudio.device_state.stopped; + }, pDevice->webaudio.deviceIndex); ma_device__on_notification_stopped(pDevice); @@ -40428,7 +40361,7 @@ static ma_result ma_context_uninit__webaudio(ma_context* pContext) /* Remove the global miniaudio object from window if there are no more references to it. */ EM_ASM({ if (typeof(window.miniaudio) !== 'undefined') { - window.miniaudio.referenceCount--; + window.miniaudio.referenceCount -= 1; if (window.miniaudio.referenceCount === 0) { delete window.miniaudio; } @@ -40456,7 +40389,20 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex window.miniaudio = { referenceCount: 0 }; - miniaudio.devices = []; /* Device cache for mapping devices to indexes for JavaScript/C interop. */ + + /* Device types. */ + window.miniaudio.device_type = {}; + window.miniaudio.device_type.playback = $0; + window.miniaudio.device_type.capture = $1; + window.miniaudio.device_type.duplex = $2; + + /* Device states. */ + window.miniaudio.device_state = {}; + window.miniaudio.device_state.stopped = $3; + window.miniaudio.device_state.started = $4; + + /* Device cache for mapping devices to indexes for JavaScript/C interop. */ + miniaudio.devices = []; miniaudio.track_device = function(device) { /* Try inserting into a free slot first. */ @@ -40519,10 +40465,10 @@ static ma_result ma_context_init__webaudio(ma_context* pContext, const ma_contex }); } - window.miniaudio.referenceCount++; + window.miniaudio.referenceCount += 1; return 1; - }, 0); /* Must pass in a dummy argument for C99 compatibility. */ + }, ma_device_type_playback, ma_device_type_capture, ma_device_type_duplex, ma_device_state_stopped, ma_device_state_started); if (resultFromJS != 1) { return MA_FAILED_TO_INIT_BACKEND; @@ -40857,10 +40803,14 @@ MA_API ma_result ma_device_post_init(ma_device* pDevice, ma_device_type deviceTy static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) { ma_device* pDevice = (ma_device*)pData; +#ifdef MA_WIN32 + HRESULT CoInitializeResult; +#endif + MA_ASSERT(pDevice != NULL); #ifdef MA_WIN32 - ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE); + CoInitializeResult = ma_CoInitializeEx(pDevice->pContext, NULL, MA_COINIT_VALUE); #endif /* @@ -40946,7 +40896,9 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) } #ifdef MA_WIN32 - ma_CoUninitialize(pDevice->pContext); + if (CoInitializeResult == S_OK) { + ma_CoUninitialize(pDevice->pContext); + } #endif return (ma_thread_result)0; @@ -40969,14 +40921,16 @@ static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) { /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */ #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) - ma_CoUninitialize(pContext); + if (pContext->win32.CoInitializeResult == S_OK) { + ma_CoUninitialize(pContext); + } #if defined(MA_WIN32_DESKTOP) - ma_dlclose(pContext, pContext->win32.hUser32DLL); - ma_dlclose(pContext, pContext->win32.hAdvapi32DLL); + ma_dlclose(ma_context_get_log(pContext), pContext->win32.hUser32DLL); + ma_dlclose(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL); #endif - ma_dlclose(pContext, pContext->win32.hOle32DLL); + ma_dlclose(ma_context_get_log(pContext), pContext->win32.hOle32DLL); #else (void)pContext; #endif @@ -40989,44 +40943,44 @@ static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) #if defined(MA_WIN32_DESKTOP) /* User32.dll */ - pContext->win32.hUser32DLL = ma_dlopen(pContext, "user32.dll"); + pContext->win32.hUser32DLL = ma_dlopen(ma_context_get_log(pContext), "user32.dll"); if (pContext->win32.hUser32DLL == NULL) { return MA_FAILED_TO_INIT_BACKEND; } - pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetForegroundWindow"); - pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetDesktopWindow"); + pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetForegroundWindow"); + pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hUser32DLL, "GetDesktopWindow"); /* Advapi32.dll */ - pContext->win32.hAdvapi32DLL = ma_dlopen(pContext, "advapi32.dll"); + pContext->win32.hAdvapi32DLL = ma_dlopen(ma_context_get_log(pContext), "advapi32.dll"); if (pContext->win32.hAdvapi32DLL == NULL) { return MA_FAILED_TO_INIT_BACKEND; } - pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); - pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegCloseKey"); - pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); + pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); + pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegCloseKey"); + pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); #endif /* Ole32.dll */ - pContext->win32.hOle32DLL = ma_dlopen(pContext, "ole32.dll"); + pContext->win32.hOle32DLL = ma_dlopen(ma_context_get_log(pContext), "ole32.dll"); if (pContext->win32.hOle32DLL == NULL) { return MA_FAILED_TO_INIT_BACKEND; } - pContext->win32.CoInitialize = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoInitialize"); - pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoInitializeEx"); - pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoUninitialize"); - pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoCreateInstance"); - pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoTaskMemFree"); - pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "PropVariantClear"); - pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "StringFromGUID2"); + pContext->win32.CoInitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitialize"); + pContext->win32.CoInitializeEx = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoInitializeEx"); + pContext->win32.CoUninitialize = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoUninitialize"); + pContext->win32.CoCreateInstance = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoCreateInstance"); + pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "CoTaskMemFree"); + pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "PropVariantClear"); + pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(ma_context_get_log(pContext), pContext->win32.hOle32DLL, "StringFromGUID2"); #else (void)pContext; /* Unused. */ #endif - ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); + pContext->win32.CoInitializeResult = ma_CoInitializeEx(pContext, NULL, MA_COINIT_VALUE); return MA_SUCCESS; } #else @@ -41901,7 +41855,6 @@ MA_API ma_result ma_device_init(ma_context* pContext, const ma_device_config* pC } - /* If we're using fixed sized callbacks we'll need to make use of an intermediary buffer. Needs to be done after post_init_setup() because we'll need access to the sample rate. @@ -44682,13 +44635,14 @@ static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uin d1 = vmovq_n_f32(0); } else if (ditherMode == ma_dither_mode_rectangle) { float d0v[4]; + float d1v[4]; + d0v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); d0v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); d0v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); d0v[3] = ma_dither_f32_rectangle(ditherMin, ditherMax); d0 = vld1q_f32(d0v); - float d1v[4]; d1v[0] = ma_dither_f32_rectangle(ditherMin, ditherMax); d1v[1] = ma_dither_f32_rectangle(ditherMin, ditherMax); d1v[2] = ma_dither_f32_rectangle(ditherMin, ditherMax); @@ -44696,13 +44650,14 @@ static MA_INLINE void ma_pcm_f32_to_s16__neon(void* dst, const void* src, ma_uin d1 = vld1q_f32(d1v); } else { float d0v[4]; + float d1v[4]; + d0v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); d0v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); d0v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); d0v[3] = ma_dither_f32_triangle(ditherMin, ditherMax); d0 = vld1q_f32(d0v); - float d1v[4]; d1v[0] = ma_dither_f32_triangle(ditherMin, ditherMax); d1v[1] = ma_dither_f32_triangle(ditherMin, ditherMax); d1v[2] = ma_dither_f32_triangle(ditherMin, ditherMax); @@ -49315,48 +49270,65 @@ MA_API ma_result ma_fader_process_pcm_frames(ma_fader* pFader, void* pFramesOut, return MA_INVALID_ARGS; } - /* - For now we need to clamp frameCount so that the cursor never overflows 32-bits. This is required for - the conversion to a float which we use for the linear interpolation. This might be changed later. - */ - if (frameCount + pFader->cursorInFrames > UINT_MAX) { - frameCount = UINT_MAX - pFader->cursorInFrames; + /* If the cursor is still negative we need to just copy the absolute number of those frames, but no more than frameCount. */ + if (pFader->cursorInFrames < 0) { + ma_uint64 absCursorInFrames = (ma_uint64)0 - pFader->cursorInFrames; + if (absCursorInFrames > frameCount) { + absCursorInFrames = frameCount; + } + + ma_copy_pcm_frames(pFramesOut, pFramesIn, absCursorInFrames, pFader->config.format, pFader->config.channels); + + pFader->cursorInFrames += absCursorInFrames; + frameCount -= absCursorInFrames; + pFramesOut = ma_offset_ptr(pFramesOut, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames); + pFramesIn = ma_offset_ptr(pFramesIn, ma_get_bytes_per_frame(pFader->config.format, pFader->config.channels)*absCursorInFrames); } - /* Optimized path if volumeBeg and volumeEnd are equal. */ - if (pFader->volumeBeg == pFader->volumeEnd) { - if (pFader->volumeBeg == 1) { - /* Straight copy. */ - ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels); - } else { - /* Copy with volume. */ - ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); + if (pFader->cursorInFrames >= 0) { + /* + For now we need to clamp frameCount so that the cursor never overflows 32-bits. This is required for + the conversion to a float which we use for the linear interpolation. This might be changed later. + */ + if (frameCount + pFader->cursorInFrames > UINT_MAX) { + frameCount = UINT_MAX - pFader->cursorInFrames; } - } else { - /* Slower path. Volumes are different, so may need to do an interpolation. */ - if (pFader->cursorInFrames >= pFader->lengthInFrames) { - /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */ - ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); - } else { - /* Slow path. This is where we do the actual fading. */ - ma_uint64 iFrame; - ma_uint32 iChannel; - /* For now we only support f32. Support for other formats will be added later. */ - if (pFader->config.format == ma_format_f32) { - const float* pFramesInF32 = (const float*)pFramesIn; - /* */ float* pFramesOutF32 = ( float*)pFramesOut; - - for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float a = (ma_uint32)ma_min(pFader->cursorInFrames + iFrame, pFader->lengthInFrames) / (float)((ma_uint32)pFader->lengthInFrames); /* Safe cast due to the frameCount clamp at the top of this function. */ - float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a); - - for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) { - pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume; - } - } + /* Optimized path if volumeBeg and volumeEnd are equal. */ + if (pFader->volumeBeg == pFader->volumeEnd) { + if (pFader->volumeBeg == 1) { + /* Straight copy. */ + ma_copy_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels); } else { - return MA_NOT_IMPLEMENTED; + /* Copy with volume. */ + ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeBeg); + } + } else { + /* Slower path. Volumes are different, so may need to do an interpolation. */ + if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { + /* Fast path. We've gone past the end of the fade period so just apply the end volume to all samples. */ + ma_copy_and_apply_volume_and_clip_pcm_frames(pFramesOut, pFramesIn, frameCount, pFader->config.format, pFader->config.channels, pFader->volumeEnd); + } else { + /* Slow path. This is where we do the actual fading. */ + ma_uint64 iFrame; + ma_uint32 iChannel; + + /* For now we only support f32. Support for other formats might be added later. */ + if (pFader->config.format == ma_format_f32) { + const float* pFramesInF32 = (const float*)pFramesIn; + /* */ float* pFramesOutF32 = ( float*)pFramesOut; + + for (iFrame = 0; iFrame < frameCount; iFrame += 1) { + float a = (ma_uint32)ma_min(pFader->cursorInFrames + iFrame, pFader->lengthInFrames) / (float)((ma_uint32)pFader->lengthInFrames); /* Safe cast due to the frameCount clamp at the top of this function. */ + float volume = ma_mix_f32_fast(pFader->volumeBeg, pFader->volumeEnd, a); + + for (iChannel = 0; iChannel < pFader->config.channels; iChannel += 1) { + pFramesOutF32[iFrame*pFader->config.channels + iChannel] = pFramesInF32[iFrame*pFader->config.channels + iChannel] * volume; + } + } + } else { + return MA_NOT_IMPLEMENTED; + } } } } @@ -49386,6 +49358,11 @@ MA_API void ma_fader_get_data_format(const ma_fader* pFader, ma_format* pFormat, } MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames) +{ + ma_fader_set_fade_ex(pFader, volumeBeg, volumeEnd, lengthInFrames, 0); +} + +MA_API void ma_fader_set_fade_ex(ma_fader* pFader, float volumeBeg, float volumeEnd, ma_uint64 lengthInFrames, ma_int64 startOffsetInFrames) { if (pFader == NULL) { return; @@ -49404,10 +49381,15 @@ MA_API void ma_fader_set_fade(ma_fader* pFader, float volumeBeg, float volumeEnd lengthInFrames = UINT_MAX; } + /* The start offset needs to be clamped to ensure it doesn't overflow a signed number. */ + if (startOffsetInFrames > INT_MAX) { + startOffsetInFrames = INT_MAX; + } + pFader->volumeBeg = volumeBeg; pFader->volumeEnd = volumeEnd; pFader->lengthInFrames = lengthInFrames; - pFader->cursorInFrames = 0; /* Reset cursor. */ + pFader->cursorInFrames = -startOffsetInFrames; } MA_API float ma_fader_get_current_volume(const ma_fader* pFader) @@ -49416,10 +49398,15 @@ MA_API float ma_fader_get_current_volume(const ma_fader* pFader) return 0.0f; } + /* Any frames prior to the start of the fade period will be at unfaded volume. */ + if (pFader->cursorInFrames < 0) { + return 1.0f; + } + /* The current volume depends on the position of the cursor. */ if (pFader->cursorInFrames == 0) { return pFader->volumeBeg; - } else if (pFader->cursorInFrames >= pFader->lengthInFrames) { + } else if ((ma_uint64)pFader->cursorInFrames >= pFader->lengthInFrames) { /* Safe case because the < 0 case was checked above. */ return pFader->volumeEnd; } else { /* The cursor is somewhere inside the fading period. We can figure this out with a simple linear interpoluation between volumeBeg and volumeEnd based on our cursor position. */ @@ -50297,7 +50284,7 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, } /* If we're not spatializing we need to run an optimized path. */ - if (c89atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) { + if (ma_atomic_load_i32(&pSpatializer->attenuationModel) == ma_attenuation_model_none) { if (ma_spatializer_listener_is_enabled(pListener)) { /* No attenuation is required, but we'll need to do some channel conversion. */ if (pSpatializer->channelsIn == pSpatializer->channelsOut) { @@ -50638,7 +50625,7 @@ MA_API void ma_spatializer_set_attenuation_model(ma_spatializer* pSpatializer, m return; } - c89atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel); + ma_atomic_exchange_i32(&pSpatializer->attenuationModel, attenuationModel); } MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatializer* pSpatializer) @@ -50647,7 +50634,7 @@ MA_API ma_attenuation_model ma_spatializer_get_attenuation_model(const ma_spatia return ma_attenuation_model_none; } - return (ma_attenuation_model)c89atomic_load_i32(&pSpatializer->attenuationModel); + return (ma_attenuation_model)ma_atomic_load_i32(&pSpatializer->attenuationModel); } MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_positioning positioning) @@ -50656,7 +50643,7 @@ MA_API void ma_spatializer_set_positioning(ma_spatializer* pSpatializer, ma_posi return; } - c89atomic_exchange_i32(&pSpatializer->positioning, positioning); + ma_atomic_exchange_i32(&pSpatializer->positioning, positioning); } MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpatializer) @@ -50665,7 +50652,7 @@ MA_API ma_positioning ma_spatializer_get_positioning(const ma_spatializer* pSpat return ma_positioning_absolute; } - return (ma_positioning)c89atomic_load_i32(&pSpatializer->positioning); + return (ma_positioning)ma_atomic_load_i32(&pSpatializer->positioning); } MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rolloff) @@ -50674,7 +50661,7 @@ MA_API void ma_spatializer_set_rolloff(ma_spatializer* pSpatializer, float rollo return; } - c89atomic_exchange_f32(&pSpatializer->rolloff, rolloff); + ma_atomic_exchange_f32(&pSpatializer->rolloff, rolloff); } MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer) @@ -50683,7 +50670,7 @@ MA_API float ma_spatializer_get_rolloff(const ma_spatializer* pSpatializer) return 0; } - return c89atomic_load_f32(&pSpatializer->rolloff); + return ma_atomic_load_f32(&pSpatializer->rolloff); } MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minGain) @@ -50692,7 +50679,7 @@ MA_API void ma_spatializer_set_min_gain(ma_spatializer* pSpatializer, float minG return; } - c89atomic_exchange_f32(&pSpatializer->minGain, minGain); + ma_atomic_exchange_f32(&pSpatializer->minGain, minGain); } MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer) @@ -50701,7 +50688,7 @@ MA_API float ma_spatializer_get_min_gain(const ma_spatializer* pSpatializer) return 0; } - return c89atomic_load_f32(&pSpatializer->minGain); + return ma_atomic_load_f32(&pSpatializer->minGain); } MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxGain) @@ -50710,7 +50697,7 @@ MA_API void ma_spatializer_set_max_gain(ma_spatializer* pSpatializer, float maxG return; } - c89atomic_exchange_f32(&pSpatializer->maxGain, maxGain); + ma_atomic_exchange_f32(&pSpatializer->maxGain, maxGain); } MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer) @@ -50719,7 +50706,7 @@ MA_API float ma_spatializer_get_max_gain(const ma_spatializer* pSpatializer) return 0; } - return c89atomic_load_f32(&pSpatializer->maxGain); + return ma_atomic_load_f32(&pSpatializer->maxGain); } MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float minDistance) @@ -50728,7 +50715,7 @@ MA_API void ma_spatializer_set_min_distance(ma_spatializer* pSpatializer, float return; } - c89atomic_exchange_f32(&pSpatializer->minDistance, minDistance); + ma_atomic_exchange_f32(&pSpatializer->minDistance, minDistance); } MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer) @@ -50737,7 +50724,7 @@ MA_API float ma_spatializer_get_min_distance(const ma_spatializer* pSpatializer) return 0; } - return c89atomic_load_f32(&pSpatializer->minDistance); + return ma_atomic_load_f32(&pSpatializer->minDistance); } MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float maxDistance) @@ -50746,7 +50733,7 @@ MA_API void ma_spatializer_set_max_distance(ma_spatializer* pSpatializer, float return; } - c89atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance); + ma_atomic_exchange_f32(&pSpatializer->maxDistance, maxDistance); } MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer) @@ -50755,7 +50742,7 @@ MA_API float ma_spatializer_get_max_distance(const ma_spatializer* pSpatializer) return 0; } - return c89atomic_load_f32(&pSpatializer->maxDistance); + return ma_atomic_load_f32(&pSpatializer->maxDistance); } MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAngleInRadians, float outerAngleInRadians, float outerGain) @@ -50764,9 +50751,9 @@ MA_API void ma_spatializer_set_cone(ma_spatializer* pSpatializer, float innerAng return; } - c89atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians); - c89atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians); - c89atomic_exchange_f32(&pSpatializer->coneOuterGain, outerGain); + ma_atomic_exchange_f32(&pSpatializer->coneInnerAngleInRadians, innerAngleInRadians); + ma_atomic_exchange_f32(&pSpatializer->coneOuterAngleInRadians, outerAngleInRadians); + ma_atomic_exchange_f32(&pSpatializer->coneOuterGain, outerGain); } MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* pInnerAngleInRadians, float* pOuterAngleInRadians, float* pOuterGain) @@ -50776,15 +50763,15 @@ MA_API void ma_spatializer_get_cone(const ma_spatializer* pSpatializer, float* p } if (pInnerAngleInRadians != NULL) { - *pInnerAngleInRadians = c89atomic_load_f32(&pSpatializer->coneInnerAngleInRadians); + *pInnerAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneInnerAngleInRadians); } if (pOuterAngleInRadians != NULL) { - *pOuterAngleInRadians = c89atomic_load_f32(&pSpatializer->coneOuterAngleInRadians); + *pOuterAngleInRadians = ma_atomic_load_f32(&pSpatializer->coneOuterAngleInRadians); } if (pOuterGain != NULL) { - *pOuterGain = c89atomic_load_f32(&pSpatializer->coneOuterGain); + *pOuterGain = ma_atomic_load_f32(&pSpatializer->coneOuterGain); } } @@ -50794,7 +50781,7 @@ MA_API void ma_spatializer_set_doppler_factor(ma_spatializer* pSpatializer, floa return; } - c89atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor); + ma_atomic_exchange_f32(&pSpatializer->dopplerFactor, dopplerFactor); } MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatializer) @@ -50803,7 +50790,7 @@ MA_API float ma_spatializer_get_doppler_factor(const ma_spatializer* pSpatialize return 1; } - return c89atomic_load_f32(&pSpatializer->dopplerFactor); + return ma_atomic_load_f32(&pSpatializer->dopplerFactor); } MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pSpatializer, float directionalAttenuationFactor) @@ -50812,7 +50799,7 @@ MA_API void ma_spatializer_set_directional_attenuation_factor(ma_spatializer* pS return; } - c89atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor); + ma_atomic_exchange_f32(&pSpatializer->directionalAttenuationFactor, directionalAttenuationFactor); } MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatializer* pSpatializer) @@ -50821,7 +50808,7 @@ MA_API float ma_spatializer_get_directional_attenuation_factor(const ma_spatiali return 1; } - return c89atomic_load_f32(&pSpatializer->directionalAttenuationFactor); + return ma_atomic_load_f32(&pSpatializer->directionalAttenuationFactor); } MA_API void ma_spatializer_set_position(ma_spatializer* pSpatializer, float x, float y, float z) @@ -51343,8 +51330,10 @@ static ma_result ma_linear_resampler_process_pcm_frames_s16_downsample(ma_linear } } - /* Filter. */ - ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16); + /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ + if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { + ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pResampler->x1.s16, pResampler->x1.s16); + } framesProcessedIn += 1; pResampler->inTimeInt -= 1; @@ -51430,8 +51419,10 @@ static ma_result ma_linear_resampler_process_pcm_frames_s16_upsample(ma_linear_r MA_ASSERT(pResampler->inTimeInt == 0); ma_linear_resampler_interpolate_frame_s16(pResampler, pFramesOutS16); - /* Filter. */ - ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16); + /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ + if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { + ma_lpf_process_pcm_frame_s16(&pResampler->lpf, pFramesOutS16, pFramesOutS16); + } pFramesOutS16 += pResampler->config.channels; } @@ -51503,8 +51494,10 @@ static ma_result ma_linear_resampler_process_pcm_frames_f32_downsample(ma_linear } } - /* Filter. */ - ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32); + /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ + if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { + ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pResampler->x1.f32, pResampler->x1.f32); + } framesProcessedIn += 1; pResampler->inTimeInt -= 1; @@ -51590,8 +51583,10 @@ static ma_result ma_linear_resampler_process_pcm_frames_f32_upsample(ma_linear_r MA_ASSERT(pResampler->inTimeInt == 0); ma_linear_resampler_interpolate_frame_f32(pResampler, pFramesOutF32); - /* Filter. */ - ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32); + /* Filter. Do not apply filtering if sample rates are the same or else you'll get dangerous glitching. */ + if (pResampler->config.sampleRateIn != pResampler->config.sampleRateOut) { + ma_lpf_process_pcm_frame_f32(&pResampler->lpf, pFramesOutF32, pFramesOutF32); + } pFramesOutF32 += pResampler->config.channels; } @@ -51661,7 +51656,7 @@ MA_API ma_result ma_linear_resampler_set_rate_ratio(ma_linear_resampler* pResamp return MA_INVALID_ARGS; } - d = 1000; + d = 1000000; n = (ma_uint32)(ratioInOut * d); if (n == 0) { @@ -56061,13 +56056,13 @@ static MA_INLINE ma_uint32 ma_rb__extract_offset_loop_flag(ma_uint32 encodedOffs static MA_INLINE void* ma_rb__get_read_ptr(ma_rb* pRB) { MA_ASSERT(pRB != NULL); - return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(c89atomic_load_32(&pRB->encodedReadOffset))); + return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedReadOffset))); } static MA_INLINE void* ma_rb__get_write_ptr(ma_rb* pRB) { MA_ASSERT(pRB != NULL); - return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(c89atomic_load_32(&pRB->encodedWriteOffset))); + return ma_offset_ptr(pRB->pBuffer, ma_rb__extract_offset_in_bytes(ma_atomic_load_32(&pRB->encodedWriteOffset))); } static MA_INLINE ma_uint32 ma_rb__construct_offset(ma_uint32 offsetInBytes, ma_uint32 offsetLoopFlag) @@ -56160,8 +56155,8 @@ MA_API void ma_rb_reset(ma_rb* pRB) return; } - c89atomic_exchange_32(&pRB->encodedReadOffset, 0); - c89atomic_exchange_32(&pRB->encodedWriteOffset, 0); + ma_atomic_exchange_32(&pRB->encodedReadOffset, 0); + ma_atomic_exchange_32(&pRB->encodedWriteOffset, 0); } MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppBufferOut) @@ -56180,10 +56175,10 @@ MA_API ma_result ma_rb_acquire_read(ma_rb* pRB, size_t* pSizeInBytes, void** ppB } /* The returned buffer should never move ahead of the write pointer. */ - writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); - readOffset = c89atomic_load_32(&pRB->encodedReadOffset); + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); /* @@ -56219,7 +56214,7 @@ MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes) return MA_INVALID_ARGS; } - readOffset = c89atomic_load_32(&pRB->encodedReadOffset); + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ @@ -56235,7 +56230,7 @@ MA_API ma_result ma_rb_commit_read(ma_rb* pRB, size_t sizeInBytes) newReadOffsetLoopFlag ^= 0x80000000; } - c89atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes)); + ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetLoopFlag, newReadOffsetInBytes)); if (ma_rb_pointer_distance(pRB) == 0) { return MA_AT_END; @@ -56260,10 +56255,10 @@ MA_API ma_result ma_rb_acquire_write(ma_rb* pRB, size_t* pSizeInBytes, void** pp } /* The returned buffer should never overtake the read buffer. */ - readOffset = c89atomic_load_32(&pRB->encodedReadOffset); + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); /* @@ -56305,7 +56300,7 @@ MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes) return MA_INVALID_ARGS; } - writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); /* Check that sizeInBytes is correct. It should never go beyond the end of the buffer. */ @@ -56321,7 +56316,7 @@ MA_API ma_result ma_rb_commit_write(ma_rb* pRB, size_t sizeInBytes) newWriteOffsetLoopFlag ^= 0x80000000; } - c89atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes)); + ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetLoopFlag, newWriteOffsetInBytes)); if (ma_rb_pointer_distance(pRB) == 0) { return MA_AT_END; @@ -56345,10 +56340,10 @@ MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes) return MA_INVALID_ARGS; } - readOffset = c89atomic_load_32(&pRB->encodedReadOffset); + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); newReadOffsetLoopFlag = readOffsetLoopFlag; @@ -56370,7 +56365,7 @@ MA_API ma_result ma_rb_seek_read(ma_rb* pRB, size_t offsetInBytes) } } - c89atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag)); + ma_atomic_exchange_32(&pRB->encodedReadOffset, ma_rb__construct_offset(newReadOffsetInBytes, newReadOffsetLoopFlag)); return MA_SUCCESS; } @@ -56389,10 +56384,10 @@ MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes) return MA_INVALID_ARGS; } - readOffset = c89atomic_load_32(&pRB->encodedReadOffset); + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); newWriteOffsetLoopFlag = writeOffsetLoopFlag; @@ -56414,7 +56409,7 @@ MA_API ma_result ma_rb_seek_write(ma_rb* pRB, size_t offsetInBytes) } } - c89atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag)); + ma_atomic_exchange_32(&pRB->encodedWriteOffset, ma_rb__construct_offset(newWriteOffsetInBytes, newWriteOffsetLoopFlag)); return MA_SUCCESS; } @@ -56431,10 +56426,10 @@ MA_API ma_int32 ma_rb_pointer_distance(ma_rb* pRB) return 0; } - readOffset = c89atomic_load_32(&pRB->encodedReadOffset); + readOffset = ma_atomic_load_32(&pRB->encodedReadOffset); ma_rb__deconstruct_offset(readOffset, &readOffsetInBytes, &readOffsetLoopFlag); - writeOffset = c89atomic_load_32(&pRB->encodedWriteOffset); + writeOffset = ma_atomic_load_32(&pRB->encodedWriteOffset); ma_rb__deconstruct_offset(writeOffset, &writeOffsetInBytes, &writeOffsetLoopFlag); if (readOffsetLoopFlag == writeOffsetLoopFlag) { @@ -56529,7 +56524,7 @@ static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, voi if (framesToRead > 0xFFFFFFFF) { framesToRead = 0xFFFFFFFF; } - + mappedFrameCount = (ma_uint32)framesToRead; result = ma_pcm_rb_acquire_read(pRB, &mappedFrameCount, &pMappedBuffer); if (result != MA_SUCCESS) { @@ -56579,7 +56574,7 @@ static ma_result ma_pcm_rb_data_source__on_get_data_format(ma_data_source* pData return MA_SUCCESS; } -static ma_data_source_vtable ma_gRBDataSourceVTable = +static ma_data_source_vtable ma_gRBDataSourceVTable = { ma_pcm_rb_data_source__on_read, NULL, /* onSeek */ @@ -57573,7 +57568,7 @@ MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool return MA_INVALID_ARGS; } - c89atomic_exchange_32(&pDataSourceBase->isLooping, isLooping); + ma_atomic_exchange_32(&pDataSourceBase->isLooping, isLooping); /* If there's no callback for this just treat it as a successful no-op. */ if (pDataSourceBase->vtable->onSetLooping == NULL) { @@ -57591,7 +57586,7 @@ MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource) return MA_FALSE; } - return c89atomic_load_32(&pDataSourceBase->isLooping); + return ma_atomic_load_32(&pDataSourceBase->isLooping); } MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames) @@ -57643,7 +57638,7 @@ MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSou pDataSourceBase->loopBegInFrames = 0; pDataSourceBase->loopEndInFrames = ~((ma_uint64)0); - + /* Seek to within range. Note that our seek positions here are relative to the new range. We don't want do do this if we failed to retrieve the cursor earlier on because it probably means the data source @@ -58357,9 +58352,9 @@ MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, } /* All pages need to be freed. */ - pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->head.pNext); + pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); while (pPage != NULL) { - ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext); + ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext); ma_free(pPage, pAllocationCallbacks); pPage = pNext; @@ -58399,7 +58394,7 @@ MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_au } /* Calculate the length from the linked list. */ - for (pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext)) { + for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->head.pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) { *pLength += pPage->sizeInFrames; } @@ -58465,12 +58460,12 @@ MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_da /* First thing to do is update the tail. */ for (;;) { - ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pData->pTail); + ma_paged_audio_buffer_page* pOldTail = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pData->pTail); ma_paged_audio_buffer_page* pNewTail = pPage; - if (c89atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) { + if (ma_atomic_compare_exchange_weak_ptr((volatile void**)&pData->pTail, (void**)&pOldTail, pNewTail)) { /* Here is where we append the page to the list. After this, the page is attached to the list and ready to be read from. */ - c89atomic_exchange_ptr(&pOldTail->pNext, pPage); + ma_atomic_exchange_ptr(&pOldTail->pNext, pPage); break; /* Done. */ } } @@ -58627,7 +58622,7 @@ MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pP if (pPagedAudioBuffer->relativeCursor == pPagedAudioBuffer->pCurrent->sizeInFrames) { /* We reached the end of the page. Need to move to the next. If there's no more pages, we're done. */ - ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext); + ma_paged_audio_buffer_page* pNext = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPagedAudioBuffer->pCurrent->pNext); if (pNext == NULL) { result = MA_AT_END; break; /* We've reached the end. */ @@ -58669,12 +58664,12 @@ MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* ma_paged_audio_buffer_page* pPage; ma_uint64 runningCursor = 0; - for (pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)c89atomic_load_ptr(&pPage->pNext)) { + for (pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&ma_paged_audio_buffer_data_get_head(pPagedAudioBuffer->pData)->pNext); pPage != NULL; pPage = (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(&pPage->pNext)) { ma_uint64 pageRangeBeg = runningCursor; ma_uint64 pageRangeEnd = pageRangeBeg + pPage->sizeInFrames; if (frameIndex >= pageRangeBeg) { - if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)c89atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) { /* A small edge case - allow seeking to the very end of the buffer. */ + if (frameIndex < pageRangeEnd || (frameIndex == pageRangeEnd && pPage == (ma_paged_audio_buffer_page*)ma_atomic_load_ptr(ma_paged_audio_buffer_data_get_tail(pPagedAudioBuffer->pData)))) { /* A small edge case - allow seeking to the very end of the buffer. */ /* We found the page. */ pPagedAudioBuffer->pCurrent = pPage; pPagedAudioBuffer->absoluteCursor = frameIndex; @@ -58887,85 +58882,36 @@ MA_API ma_result ma_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_info* pInfo } -static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePath, const wchar_t* pFilePathW, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - ma_result result; - ma_vfs_file file; - ma_file_info info; - void* pData; - size_t bytesRead; - - if (ppData != NULL) { - *ppData = NULL; - } - if (pSize != NULL) { - *pSize = 0; - } - - if (ppData == NULL) { - return MA_INVALID_ARGS; - } - - if (pFilePath != NULL) { - result = ma_vfs_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); - } else { - result = ma_vfs_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file); - } - if (result != MA_SUCCESS) { - return result; - } - - result = ma_vfs_info(pVFS, file, &info); - if (result != MA_SUCCESS) { - ma_vfs_close(pVFS, file); - return result; - } - - if (info.sizeInBytes > MA_SIZE_MAX) { - ma_vfs_close(pVFS, file); - return MA_TOO_BIG; - } - - pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */ - if (pData == NULL) { - ma_vfs_close(pVFS, file); - return result; - } - - result = ma_vfs_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead); /* Safe cast. */ - ma_vfs_close(pVFS, file); - - if (result != MA_SUCCESS) { - ma_free(pData, pAllocationCallbacks); - return result; - } - - if (pSize != NULL) { - *pSize = bytesRead; - } - - MA_ASSERT(ppData != NULL); - *ppData = pData; - - return MA_SUCCESS; -} - -MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks); -} - -MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) -{ - return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks); -} - - #if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX)) #define MA_USE_WIN32_FILEIO #endif #if defined(MA_USE_WIN32_FILEIO) +/* +We need to dynamically load SetFilePointer or SetFilePointerEx because older versions of Windows do +not have the Ex version. We therefore need to do some dynamic branching depending on what's available. + +We load these when we load our first file from the default VFS. It's left open for the life of the +program and is left to the OS to uninitialize when the program terminates. +*/ +typedef DWORD (__stdcall * ma_SetFilePointer_proc)(HANDLE hFile, LONG lDistanceToMove, LONG* lpDistanceToMoveHigh, DWORD dwMoveMethod); +typedef BOOL (__stdcall * ma_SetFilePointerEx_proc)(HANDLE hFile, LARGE_INTEGER liDistanceToMove, LARGE_INTEGER* lpNewFilePointer, DWORD dwMoveMethod); + +static ma_handle hKernel32DLL = NULL; +static ma_SetFilePointer_proc ma_SetFilePointer = NULL; +static ma_SetFilePointerEx_proc ma_SetFilePointerEx = NULL; + +static void ma_win32_fileio_init(void) +{ + if (hKernel32DLL == NULL) { + hKernel32DLL = ma_dlopen(NULL, "kernel32.dll"); + if (hKernel32DLL != NULL) { + ma_SetFilePointer = (ma_SetFilePointer_proc) ma_dlsym(NULL, hKernel32DLL, "SetFilePointer"); + ma_SetFilePointerEx = (ma_SetFilePointerEx_proc)ma_dlsym(NULL, hKernel32DLL, "SetFilePointerEx"); + } + } +} + static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition) { *pDesiredAccess = 0; @@ -58997,6 +58943,9 @@ static ma_result ma_default_vfs_open__win32(ma_vfs* pVFS, const char* pFilePath, (void)pVFS; + /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ + ma_win32_fileio_init(); + ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); hFile = CreateFileA(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); @@ -59017,6 +58966,9 @@ static ma_result ma_default_vfs_open_w__win32(ma_vfs* pVFS, const wchar_t* pFile (void)pVFS; + /* Load some Win32 symbols dynamically so we can dynamically check for the existence of SetFilePointerEx. */ + ma_win32_fileio_init(); + ma_default_vfs__get_open_settings_win32(openMode, &dwDesiredAccess, &dwShareMode, &dwCreationDisposition); hFile = CreateFileW(pFilePath, dwDesiredAccess, dwShareMode, NULL, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); @@ -59142,16 +59094,19 @@ static ma_result ma_default_vfs_seek__win32(ma_vfs* pVFS, ma_vfs_file file, ma_i dwMoveMethod = FILE_BEGIN; } -#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__) - /* No SetFilePointerEx() so restrict to 31 bits. */ - if (origin > 0x7FFFFFFF) { - return MA_OUT_OF_RANGE; + if (ma_SetFilePointerEx != NULL) { + result = ma_SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod); + } else if (ma_SetFilePointer != NULL) { + /* No SetFilePointerEx() so restrict to 31 bits. */ + if (origin > 0x7FFFFFFF) { + return MA_OUT_OF_RANGE; + } + + result = ma_SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod); + } else { + return MA_NOT_IMPLEMENTED; } - result = SetFilePointer((HANDLE)file, (LONG)liDistanceToMove.QuadPart, NULL, dwMoveMethod); -#else - result = SetFilePointerEx((HANDLE)file, liDistanceToMove, NULL, dwMoveMethod); -#endif if (result == 0) { return ma_result_from_GetLastError(GetLastError()); } @@ -59164,20 +59119,22 @@ static ma_result ma_default_vfs_tell__win32(ma_vfs* pVFS, ma_vfs_file file, ma_i LARGE_INTEGER liZero; LARGE_INTEGER liTell; BOOL result; -#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__) - LONG tell; -#endif (void)pVFS; liZero.QuadPart = 0; -#if (defined(_MSC_VER) && _MSC_VER <= 1200) || defined(__DMC__) - result = SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT); - liTell.QuadPart = tell; -#else - result = SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT); -#endif + if (ma_SetFilePointerEx != NULL) { + result = ma_SetFilePointerEx((HANDLE)file, liZero, &liTell, FILE_CURRENT); + } else if (ma_SetFilePointer != NULL) { + LONG tell; + + result = ma_SetFilePointer((HANDLE)file, (LONG)liZero.QuadPart, &tell, FILE_CURRENT); + liTell.QuadPart = tell; + } else { + return MA_NOT_IMPLEMENTED; + } + if (result == 0) { return ma_result_from_GetLastError(GetLastError()); } @@ -59653,6 +59610,81 @@ MA_API ma_result ma_vfs_or_default_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_ +static ma_result ma_vfs_open_and_read_file_ex(ma_vfs* pVFS, const char* pFilePath, const wchar_t* pFilePathW, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_result result; + ma_vfs_file file; + ma_file_info info; + void* pData; + size_t bytesRead; + + if (ppData != NULL) { + *ppData = NULL; + } + if (pSize != NULL) { + *pSize = 0; + } + + if (ppData == NULL) { + return MA_INVALID_ARGS; + } + + if (pFilePath != NULL) { + result = ma_vfs_or_default_open(pVFS, pFilePath, MA_OPEN_MODE_READ, &file); + } else { + result = ma_vfs_or_default_open_w(pVFS, pFilePathW, MA_OPEN_MODE_READ, &file); + } + if (result != MA_SUCCESS) { + return result; + } + + result = ma_vfs_or_default_info(pVFS, file, &info); + if (result != MA_SUCCESS) { + ma_vfs_or_default_close(pVFS, file); + return result; + } + + if (info.sizeInBytes > MA_SIZE_MAX) { + ma_vfs_or_default_close(pVFS, file); + return MA_TOO_BIG; + } + + pData = ma_malloc((size_t)info.sizeInBytes, pAllocationCallbacks); /* Safe cast. */ + if (pData == NULL) { + ma_vfs_or_default_close(pVFS, file); + return result; + } + + result = ma_vfs_or_default_read(pVFS, file, pData, (size_t)info.sizeInBytes, &bytesRead); /* Safe cast. */ + ma_vfs_or_default_close(pVFS, file); + + if (result != MA_SUCCESS) { + ma_free(pData, pAllocationCallbacks); + return result; + } + + if (pSize != NULL) { + *pSize = bytesRead; + } + + MA_ASSERT(ppData != NULL); + *ppData = pData; + + return MA_SUCCESS; +} + +MA_API ma_result ma_vfs_open_and_read_file(ma_vfs* pVFS, const char* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_vfs_open_and_read_file_ex(pVFS, pFilePath, NULL, ppData, pSize, pAllocationCallbacks); +} + +MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFilePath, void** ppData, size_t* pSize, const ma_allocation_callbacks* pAllocationCallbacks) +{ + return ma_vfs_open_and_read_file_ex(pVFS, NULL, pFilePath, ppData, pSize, pAllocationCallbacks); +} + + + /************************************************************************************************************************************************************** Decoding and Encoding Headers. These are auto-generated from a tool. @@ -59660,195 +59692,76 @@ Decoding and Encoding Headers. These are auto-generated from a tool. **************************************************************************************************************************************************************/ #if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) /* dr_wav_h begin */ -#ifndef dr_wav_h -#define dr_wav_h +#ifndef ma_dr_wav_h +#define ma_dr_wav_h #ifdef __cplusplus extern "C" { #endif -#define DRWAV_STRINGIFY(x) #x -#define DRWAV_XSTRINGIFY(x) DRWAV_STRINGIFY(x) -#define DRWAV_VERSION_MAJOR 0 -#define DRWAV_VERSION_MINOR 13 -#define DRWAV_VERSION_REVISION 8 -#define DRWAV_VERSION_STRING DRWAV_XSTRINGIFY(DRWAV_VERSION_MAJOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_MINOR) "." DRWAV_XSTRINGIFY(DRWAV_VERSION_REVISION) +#define MA_DR_WAV_STRINGIFY(x) #x +#define MA_DR_WAV_XSTRINGIFY(x) MA_DR_WAV_STRINGIFY(x) +#define MA_DR_WAV_VERSION_MAJOR 0 +#define MA_DR_WAV_VERSION_MINOR 13 +#define MA_DR_WAV_VERSION_REVISION 12 +#define MA_DR_WAV_VERSION_STRING MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MAJOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MINOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_REVISION) #include -typedef signed char drwav_int8; -typedef unsigned char drwav_uint8; -typedef signed short drwav_int16; -typedef unsigned short drwav_uint16; -typedef signed int drwav_int32; -typedef unsigned int drwav_uint32; -#if defined(_MSC_VER) && !defined(__clang__) - typedef signed __int64 drwav_int64; - typedef unsigned __int64 drwav_uint64; -#else - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif - #endif - typedef signed long long drwav_int64; - typedef unsigned long long drwav_uint64; - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop - #endif -#endif -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) - typedef drwav_uint64 drwav_uintptr; -#else - typedef drwav_uint32 drwav_uintptr; -#endif -typedef drwav_uint8 drwav_bool8; -typedef drwav_uint32 drwav_bool32; -#define DRWAV_TRUE 1 -#define DRWAV_FALSE 0 -#if !defined(DRWAV_API) - #if defined(DRWAV_DLL) - #if defined(_WIN32) - #define DRWAV_DLL_IMPORT __declspec(dllimport) - #define DRWAV_DLL_EXPORT __declspec(dllexport) - #define DRWAV_DLL_PRIVATE static - #else - #if defined(__GNUC__) && __GNUC__ >= 4 - #define DRWAV_DLL_IMPORT __attribute__((visibility("default"))) - #define DRWAV_DLL_EXPORT __attribute__((visibility("default"))) - #define DRWAV_DLL_PRIVATE __attribute__((visibility("hidden"))) - #else - #define DRWAV_DLL_IMPORT - #define DRWAV_DLL_EXPORT - #define DRWAV_DLL_PRIVATE static - #endif - #endif - #if defined(DR_WAV_IMPLEMENTATION) || defined(DRWAV_IMPLEMENTATION) - #define DRWAV_API DRWAV_DLL_EXPORT - #else - #define DRWAV_API DRWAV_DLL_IMPORT - #endif - #define DRWAV_PRIVATE DRWAV_DLL_PRIVATE - #else - #define DRWAV_API extern - #define DRWAV_PRIVATE static - #endif -#endif -typedef drwav_int32 drwav_result; -#define DRWAV_SUCCESS 0 -#define DRWAV_ERROR -1 -#define DRWAV_INVALID_ARGS -2 -#define DRWAV_INVALID_OPERATION -3 -#define DRWAV_OUT_OF_MEMORY -4 -#define DRWAV_OUT_OF_RANGE -5 -#define DRWAV_ACCESS_DENIED -6 -#define DRWAV_DOES_NOT_EXIST -7 -#define DRWAV_ALREADY_EXISTS -8 -#define DRWAV_TOO_MANY_OPEN_FILES -9 -#define DRWAV_INVALID_FILE -10 -#define DRWAV_TOO_BIG -11 -#define DRWAV_PATH_TOO_LONG -12 -#define DRWAV_NAME_TOO_LONG -13 -#define DRWAV_NOT_DIRECTORY -14 -#define DRWAV_IS_DIRECTORY -15 -#define DRWAV_DIRECTORY_NOT_EMPTY -16 -#define DRWAV_END_OF_FILE -17 -#define DRWAV_NO_SPACE -18 -#define DRWAV_BUSY -19 -#define DRWAV_IO_ERROR -20 -#define DRWAV_INTERRUPT -21 -#define DRWAV_UNAVAILABLE -22 -#define DRWAV_ALREADY_IN_USE -23 -#define DRWAV_BAD_ADDRESS -24 -#define DRWAV_BAD_SEEK -25 -#define DRWAV_BAD_PIPE -26 -#define DRWAV_DEADLOCK -27 -#define DRWAV_TOO_MANY_LINKS -28 -#define DRWAV_NOT_IMPLEMENTED -29 -#define DRWAV_NO_MESSAGE -30 -#define DRWAV_BAD_MESSAGE -31 -#define DRWAV_NO_DATA_AVAILABLE -32 -#define DRWAV_INVALID_DATA -33 -#define DRWAV_TIMEOUT -34 -#define DRWAV_NO_NETWORK -35 -#define DRWAV_NOT_UNIQUE -36 -#define DRWAV_NOT_SOCKET -37 -#define DRWAV_NO_ADDRESS -38 -#define DRWAV_BAD_PROTOCOL -39 -#define DRWAV_PROTOCOL_UNAVAILABLE -40 -#define DRWAV_PROTOCOL_NOT_SUPPORTED -41 -#define DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED -42 -#define DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED -43 -#define DRWAV_SOCKET_NOT_SUPPORTED -44 -#define DRWAV_CONNECTION_RESET -45 -#define DRWAV_ALREADY_CONNECTED -46 -#define DRWAV_NOT_CONNECTED -47 -#define DRWAV_CONNECTION_REFUSED -48 -#define DRWAV_NO_HOST -49 -#define DRWAV_IN_PROGRESS -50 -#define DRWAV_CANCELLED -51 -#define DRWAV_MEMORY_ALREADY_MAPPED -52 -#define DRWAV_AT_END -53 -#define DR_WAVE_FORMAT_PCM 0x1 -#define DR_WAVE_FORMAT_ADPCM 0x2 -#define DR_WAVE_FORMAT_IEEE_FLOAT 0x3 -#define DR_WAVE_FORMAT_ALAW 0x6 -#define DR_WAVE_FORMAT_MULAW 0x7 -#define DR_WAVE_FORMAT_DVI_ADPCM 0x11 -#define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE -#define DRWAV_SEQUENTIAL 0x00000001 -DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision); -DRWAV_API const char* drwav_version_string(void); +#define MA_DR_WAVE_FORMAT_PCM 0x1 +#define MA_DR_WAVE_FORMAT_ADPCM 0x2 +#define MA_DR_WAVE_FORMAT_IEEE_FLOAT 0x3 +#define MA_DR_WAVE_FORMAT_ALAW 0x6 +#define MA_DR_WAVE_FORMAT_MULAW 0x7 +#define MA_DR_WAVE_FORMAT_DVI_ADPCM 0x11 +#define MA_DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE +#define MA_DR_WAV_SEQUENTIAL 0x00000001 +#define MA_DR_WAV_WITH_METADATA 0x00000002 +MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); +MA_API const char* ma_dr_wav_version_string(void); typedef enum { - drwav_seek_origin_start, - drwav_seek_origin_current -} drwav_seek_origin; + ma_dr_wav_seek_origin_start, + ma_dr_wav_seek_origin_current +} ma_dr_wav_seek_origin; typedef enum { - drwav_container_riff, - drwav_container_w64, - drwav_container_rf64 -} drwav_container; + ma_dr_wav_container_riff, + ma_dr_wav_container_rifx, + ma_dr_wav_container_w64, + ma_dr_wav_container_rf64, + ma_dr_wav_container_aiff +} ma_dr_wav_container; typedef struct { union { - drwav_uint8 fourcc[4]; - drwav_uint8 guid[16]; + ma_uint8 fourcc[4]; + ma_uint8 guid[16]; } id; - drwav_uint64 sizeInBytes; + ma_uint64 sizeInBytes; unsigned int paddingSize; -} drwav_chunk_header; +} ma_dr_wav_chunk_header; typedef struct { - drwav_uint16 formatTag; - drwav_uint16 channels; - drwav_uint32 sampleRate; - drwav_uint32 avgBytesPerSec; - drwav_uint16 blockAlign; - drwav_uint16 bitsPerSample; - drwav_uint16 extendedSize; - drwav_uint16 validBitsPerSample; - drwav_uint32 channelMask; - drwav_uint8 subFormat[16]; -} drwav_fmt; -DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT); -typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef size_t (* drwav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); -typedef drwav_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin); -typedef drwav_uint64 (* drwav_chunk_proc)(void* pChunkUserData, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_chunk_header* pChunkHeader, drwav_container container, const drwav_fmt* pFMT); + ma_uint16 formatTag; + ma_uint16 channels; + ma_uint32 sampleRate; + ma_uint32 avgBytesPerSec; + ma_uint16 blockAlign; + ma_uint16 bitsPerSample; + ma_uint16 extendedSize; + ma_uint16 validBitsPerSample; + ma_uint32 channelMask; + ma_uint8 subFormat[16]; +} ma_dr_wav_fmt; +MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT); +typedef size_t (* ma_dr_wav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); +typedef size_t (* ma_dr_wav_write_proc)(void* pUserData, const void* pData, size_t bytesToWrite); +typedef ma_bool32 (* ma_dr_wav_seek_proc)(void* pUserData, int offset, ma_dr_wav_seek_origin origin); +typedef ma_uint64 (* ma_dr_wav_chunk_proc)(void* pChunkUserData, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_container container, const ma_dr_wav_fmt* pFMT); typedef struct { - void* pUserData; - void* (* onMalloc)(size_t sz, void* pUserData); - void* (* onRealloc)(void* p, size_t sz, void* pUserData); - void (* onFree)(void* p, void* pUserData); -} drwav_allocation_callbacks; -typedef struct -{ - const drwav_uint8* data; + const ma_uint8* data; size_t dataSize; size_t currentReadPos; -} drwav__memory_stream; +} ma_dr_wav__memory_stream; typedef struct { void** ppData; @@ -59856,129 +59769,129 @@ typedef struct size_t dataSize; size_t dataCapacity; size_t currentWritePos; -} drwav__memory_stream_write; +} ma_dr_wav__memory_stream_write; typedef struct { - drwav_container container; - drwav_uint32 format; - drwav_uint32 channels; - drwav_uint32 sampleRate; - drwav_uint32 bitsPerSample; -} drwav_data_format; + ma_dr_wav_container container; + ma_uint32 format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint32 bitsPerSample; +} ma_dr_wav_data_format; typedef enum { - drwav_metadata_type_none = 0, - drwav_metadata_type_unknown = 1 << 0, - drwav_metadata_type_smpl = 1 << 1, - drwav_metadata_type_inst = 1 << 2, - drwav_metadata_type_cue = 1 << 3, - drwav_metadata_type_acid = 1 << 4, - drwav_metadata_type_bext = 1 << 5, - drwav_metadata_type_list_label = 1 << 6, - drwav_metadata_type_list_note = 1 << 7, - drwav_metadata_type_list_labelled_cue_region = 1 << 8, - drwav_metadata_type_list_info_software = 1 << 9, - drwav_metadata_type_list_info_copyright = 1 << 10, - drwav_metadata_type_list_info_title = 1 << 11, - drwav_metadata_type_list_info_artist = 1 << 12, - drwav_metadata_type_list_info_comment = 1 << 13, - drwav_metadata_type_list_info_date = 1 << 14, - drwav_metadata_type_list_info_genre = 1 << 15, - drwav_metadata_type_list_info_album = 1 << 16, - drwav_metadata_type_list_info_tracknumber = 1 << 17, - drwav_metadata_type_list_all_info_strings = drwav_metadata_type_list_info_software - | drwav_metadata_type_list_info_copyright - | drwav_metadata_type_list_info_title - | drwav_metadata_type_list_info_artist - | drwav_metadata_type_list_info_comment - | drwav_metadata_type_list_info_date - | drwav_metadata_type_list_info_genre - | drwav_metadata_type_list_info_album - | drwav_metadata_type_list_info_tracknumber, - drwav_metadata_type_list_all_adtl = drwav_metadata_type_list_label - | drwav_metadata_type_list_note - | drwav_metadata_type_list_labelled_cue_region, - drwav_metadata_type_all = -2, - drwav_metadata_type_all_including_unknown = -1 -} drwav_metadata_type; + ma_dr_wav_metadata_type_none = 0, + ma_dr_wav_metadata_type_unknown = 1 << 0, + ma_dr_wav_metadata_type_smpl = 1 << 1, + ma_dr_wav_metadata_type_inst = 1 << 2, + ma_dr_wav_metadata_type_cue = 1 << 3, + ma_dr_wav_metadata_type_acid = 1 << 4, + ma_dr_wav_metadata_type_bext = 1 << 5, + ma_dr_wav_metadata_type_list_label = 1 << 6, + ma_dr_wav_metadata_type_list_note = 1 << 7, + ma_dr_wav_metadata_type_list_labelled_cue_region = 1 << 8, + ma_dr_wav_metadata_type_list_info_software = 1 << 9, + ma_dr_wav_metadata_type_list_info_copyright = 1 << 10, + ma_dr_wav_metadata_type_list_info_title = 1 << 11, + ma_dr_wav_metadata_type_list_info_artist = 1 << 12, + ma_dr_wav_metadata_type_list_info_comment = 1 << 13, + ma_dr_wav_metadata_type_list_info_date = 1 << 14, + ma_dr_wav_metadata_type_list_info_genre = 1 << 15, + ma_dr_wav_metadata_type_list_info_album = 1 << 16, + ma_dr_wav_metadata_type_list_info_tracknumber = 1 << 17, + ma_dr_wav_metadata_type_list_all_info_strings = ma_dr_wav_metadata_type_list_info_software + | ma_dr_wav_metadata_type_list_info_copyright + | ma_dr_wav_metadata_type_list_info_title + | ma_dr_wav_metadata_type_list_info_artist + | ma_dr_wav_metadata_type_list_info_comment + | ma_dr_wav_metadata_type_list_info_date + | ma_dr_wav_metadata_type_list_info_genre + | ma_dr_wav_metadata_type_list_info_album + | ma_dr_wav_metadata_type_list_info_tracknumber, + ma_dr_wav_metadata_type_list_all_adtl = ma_dr_wav_metadata_type_list_label + | ma_dr_wav_metadata_type_list_note + | ma_dr_wav_metadata_type_list_labelled_cue_region, + ma_dr_wav_metadata_type_all = -2, + ma_dr_wav_metadata_type_all_including_unknown = -1 +} ma_dr_wav_metadata_type; typedef enum { - drwav_smpl_loop_type_forward = 0, - drwav_smpl_loop_type_pingpong = 1, - drwav_smpl_loop_type_backward = 2 -} drwav_smpl_loop_type; + ma_dr_wav_smpl_loop_type_forward = 0, + ma_dr_wav_smpl_loop_type_pingpong = 1, + ma_dr_wav_smpl_loop_type_backward = 2 +} ma_dr_wav_smpl_loop_type; typedef struct { - drwav_uint32 cuePointId; - drwav_uint32 type; - drwav_uint32 firstSampleByteOffset; - drwav_uint32 lastSampleByteOffset; - drwav_uint32 sampleFraction; - drwav_uint32 playCount; -} drwav_smpl_loop; + ma_uint32 cuePointId; + ma_uint32 type; + ma_uint32 firstSampleByteOffset; + ma_uint32 lastSampleByteOffset; + ma_uint32 sampleFraction; + ma_uint32 playCount; +} ma_dr_wav_smpl_loop; typedef struct { - drwav_uint32 manufacturerId; - drwav_uint32 productId; - drwav_uint32 samplePeriodNanoseconds; - drwav_uint32 midiUnityNote; - drwav_uint32 midiPitchFraction; - drwav_uint32 smpteFormat; - drwav_uint32 smpteOffset; - drwav_uint32 sampleLoopCount; - drwav_uint32 samplerSpecificDataSizeInBytes; - drwav_smpl_loop* pLoops; - drwav_uint8* pSamplerSpecificData; -} drwav_smpl; + ma_uint32 manufacturerId; + ma_uint32 productId; + ma_uint32 samplePeriodNanoseconds; + ma_uint32 midiUnityNote; + ma_uint32 midiPitchFraction; + ma_uint32 smpteFormat; + ma_uint32 smpteOffset; + ma_uint32 sampleLoopCount; + ma_uint32 samplerSpecificDataSizeInBytes; + ma_dr_wav_smpl_loop* pLoops; + ma_uint8* pSamplerSpecificData; +} ma_dr_wav_smpl; typedef struct { - drwav_int8 midiUnityNote; - drwav_int8 fineTuneCents; - drwav_int8 gainDecibels; - drwav_int8 lowNote; - drwav_int8 highNote; - drwav_int8 lowVelocity; - drwav_int8 highVelocity; -} drwav_inst; + ma_int8 midiUnityNote; + ma_int8 fineTuneCents; + ma_int8 gainDecibels; + ma_int8 lowNote; + ma_int8 highNote; + ma_int8 lowVelocity; + ma_int8 highVelocity; +} ma_dr_wav_inst; typedef struct { - drwav_uint32 id; - drwav_uint32 playOrderPosition; - drwav_uint8 dataChunkId[4]; - drwav_uint32 chunkStart; - drwav_uint32 blockStart; - drwav_uint32 sampleByteOffset; -} drwav_cue_point; + ma_uint32 id; + ma_uint32 playOrderPosition; + ma_uint8 dataChunkId[4]; + ma_uint32 chunkStart; + ma_uint32 blockStart; + ma_uint32 sampleByteOffset; +} ma_dr_wav_cue_point; typedef struct { - drwav_uint32 cuePointCount; - drwav_cue_point *pCuePoints; -} drwav_cue; + ma_uint32 cuePointCount; + ma_dr_wav_cue_point *pCuePoints; +} ma_dr_wav_cue; typedef enum { - drwav_acid_flag_one_shot = 1, - drwav_acid_flag_root_note_set = 2, - drwav_acid_flag_stretch = 4, - drwav_acid_flag_disk_based = 8, - drwav_acid_flag_acidizer = 16 -} drwav_acid_flag; + ma_dr_wav_acid_flag_one_shot = 1, + ma_dr_wav_acid_flag_root_note_set = 2, + ma_dr_wav_acid_flag_stretch = 4, + ma_dr_wav_acid_flag_disk_based = 8, + ma_dr_wav_acid_flag_acidizer = 16 +} ma_dr_wav_acid_flag; typedef struct { - drwav_uint32 flags; - drwav_uint16 midiUnityNote; - drwav_uint16 reserved1; + ma_uint32 flags; + ma_uint16 midiUnityNote; + ma_uint16 reserved1; float reserved2; - drwav_uint32 numBeats; - drwav_uint16 meterDenominator; - drwav_uint16 meterNumerator; + ma_uint32 numBeats; + ma_uint16 meterDenominator; + ma_uint16 meterNumerator; float tempo; -} drwav_acid; +} ma_dr_wav_acid; typedef struct { - drwav_uint32 cuePointId; - drwav_uint32 stringLength; + ma_uint32 cuePointId; + ma_uint32 stringLength; char* pString; -} drwav_list_label_or_note; +} ma_dr_wav_list_label_or_note; typedef struct { char* pDescription; @@ -59986,206 +59899,210 @@ typedef struct char* pOriginatorReference; char pOriginationDate[10]; char pOriginationTime[8]; - drwav_uint64 timeReference; - drwav_uint16 version; + ma_uint64 timeReference; + ma_uint16 version; char* pCodingHistory; - drwav_uint32 codingHistorySize; - drwav_uint8* pUMID; - drwav_uint16 loudnessValue; - drwav_uint16 loudnessRange; - drwav_uint16 maxTruePeakLevel; - drwav_uint16 maxMomentaryLoudness; - drwav_uint16 maxShortTermLoudness; -} drwav_bext; + ma_uint32 codingHistorySize; + ma_uint8* pUMID; + ma_uint16 loudnessValue; + ma_uint16 loudnessRange; + ma_uint16 maxTruePeakLevel; + ma_uint16 maxMomentaryLoudness; + ma_uint16 maxShortTermLoudness; +} ma_dr_wav_bext; typedef struct { - drwav_uint32 stringLength; + ma_uint32 stringLength; char* pString; -} drwav_list_info_text; +} ma_dr_wav_list_info_text; typedef struct { - drwav_uint32 cuePointId; - drwav_uint32 sampleLength; - drwav_uint8 purposeId[4]; - drwav_uint16 country; - drwav_uint16 language; - drwav_uint16 dialect; - drwav_uint16 codePage; - drwav_uint32 stringLength; + ma_uint32 cuePointId; + ma_uint32 sampleLength; + ma_uint8 purposeId[4]; + ma_uint16 country; + ma_uint16 language; + ma_uint16 dialect; + ma_uint16 codePage; + ma_uint32 stringLength; char* pString; -} drwav_list_labelled_cue_region; +} ma_dr_wav_list_labelled_cue_region; typedef enum { - drwav_metadata_location_invalid, - drwav_metadata_location_top_level, - drwav_metadata_location_inside_info_list, - drwav_metadata_location_inside_adtl_list -} drwav_metadata_location; + ma_dr_wav_metadata_location_invalid, + ma_dr_wav_metadata_location_top_level, + ma_dr_wav_metadata_location_inside_info_list, + ma_dr_wav_metadata_location_inside_adtl_list +} ma_dr_wav_metadata_location; typedef struct { - drwav_uint8 id[4]; - drwav_metadata_location chunkLocation; - drwav_uint32 dataSizeInBytes; - drwav_uint8* pData; -} drwav_unknown_metadata; + ma_uint8 id[4]; + ma_dr_wav_metadata_location chunkLocation; + ma_uint32 dataSizeInBytes; + ma_uint8* pData; +} ma_dr_wav_unknown_metadata; typedef struct { - drwav_metadata_type type; + ma_dr_wav_metadata_type type; union { - drwav_cue cue; - drwav_smpl smpl; - drwav_acid acid; - drwav_inst inst; - drwav_bext bext; - drwav_list_label_or_note labelOrNote; - drwav_list_labelled_cue_region labelledCueRegion; - drwav_list_info_text infoText; - drwav_unknown_metadata unknown; + ma_dr_wav_cue cue; + ma_dr_wav_smpl smpl; + ma_dr_wav_acid acid; + ma_dr_wav_inst inst; + ma_dr_wav_bext bext; + ma_dr_wav_list_label_or_note labelOrNote; + ma_dr_wav_list_labelled_cue_region labelledCueRegion; + ma_dr_wav_list_info_text infoText; + ma_dr_wav_unknown_metadata unknown; } data; -} drwav_metadata; +} ma_dr_wav_metadata; typedef struct { - drwav_read_proc onRead; - drwav_write_proc onWrite; - drwav_seek_proc onSeek; + ma_dr_wav_read_proc onRead; + ma_dr_wav_write_proc onWrite; + ma_dr_wav_seek_proc onSeek; void* pUserData; - drwav_allocation_callbacks allocationCallbacks; - drwav_container container; - drwav_fmt fmt; - drwav_uint32 sampleRate; - drwav_uint16 channels; - drwav_uint16 bitsPerSample; - drwav_uint16 translatedFormatTag; - drwav_uint64 totalPCMFrameCount; - drwav_uint64 dataChunkDataSize; - drwav_uint64 dataChunkDataPos; - drwav_uint64 bytesRemaining; - drwav_uint64 readCursorInPCMFrames; - drwav_uint64 dataChunkDataSizeTargetWrite; - drwav_bool32 isSequentialWrite; - drwav_metadata_type allowedMetadataTypes; - drwav_metadata* pMetadata; - drwav_uint32 metadataCount; - drwav__memory_stream memoryStream; - drwav__memory_stream_write memoryStreamWrite; + ma_allocation_callbacks allocationCallbacks; + ma_dr_wav_container container; + ma_dr_wav_fmt fmt; + ma_uint32 sampleRate; + ma_uint16 channels; + ma_uint16 bitsPerSample; + ma_uint16 translatedFormatTag; + ma_uint64 totalPCMFrameCount; + ma_uint64 dataChunkDataSize; + ma_uint64 dataChunkDataPos; + ma_uint64 bytesRemaining; + ma_uint64 readCursorInPCMFrames; + ma_uint64 dataChunkDataSizeTargetWrite; + ma_bool32 isSequentialWrite; + ma_dr_wav_metadata* pMetadata; + ma_uint32 metadataCount; + ma_dr_wav__memory_stream memoryStream; + ma_dr_wav__memory_stream_write memoryStreamWrite; struct { - drwav_uint32 bytesRemainingInBlock; - drwav_uint16 predictor[2]; - drwav_int32 delta[2]; - drwav_int32 cachedFrames[4]; - drwav_uint32 cachedFrameCount; - drwav_int32 prevFrames[2][2]; + ma_uint32 bytesRemainingInBlock; + ma_uint16 predictor[2]; + ma_int32 delta[2]; + ma_int32 cachedFrames[4]; + ma_uint32 cachedFrameCount; + ma_int32 prevFrames[2][2]; } msadpcm; struct { - drwav_uint32 bytesRemainingInBlock; - drwav_int32 predictor[2]; - drwav_int32 stepIndex[2]; - drwav_int32 cachedFrames[16]; - drwav_uint32 cachedFrameCount; + ma_uint32 bytesRemainingInBlock; + ma_int32 predictor[2]; + ma_int32 stepIndex[2]; + ma_int32 cachedFrames[16]; + ma_uint32 cachedFrameCount; } ima; -} drwav; -DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount); -DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount); -DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav); -DRWAV_API drwav_result drwav_uninit(drwav* pWav); -DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut); -DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex); -DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor); -DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength); -DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData); -DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); -DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); -DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData); -#ifndef DR_WAV_NO_CONVERSION_API -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut); -DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount); -DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount); -DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount); -DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut); -DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount); -DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount); -DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount); -DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut); -DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount); -DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount); -DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount); -DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); -DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount); + struct + { + ma_bool8 isLE; + ma_bool8 isUnsigned; + } aiff; +} ma_dr_wav; +MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount); +MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount); +MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav); +MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav); +MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut); +MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex); +MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor); +MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength); +MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData); +MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); +MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); +MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData); +#ifndef MA_DR_WAV_NO_CONVERSION_API +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut); +MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount); +MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount); +MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount); +MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut); +MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount); +MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount); +MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut); +MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount); +MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount); +MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount); +MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); +MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount); #endif -#ifndef DR_WAV_NO_STDIO -DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_WAV_NO_STDIO +MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); #endif -DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks); -#ifndef DR_WAV_NO_CONVERSION_API -DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -#ifndef DR_WAV_NO_STDIO -DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_WAV_NO_CONVERSION_API +MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_WAV_NO_STDIO +MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); #endif -DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks); #endif -DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks); -DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data); -DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data); -DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data); -DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data); -DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data); -DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data); -DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data); -DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]); -DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b); +MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data); +MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data); +MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data); +MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data); +MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data); +MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data); +MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data); +MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]); +MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b); #ifdef __cplusplus } #endif @@ -60195,354 +60112,284 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b); #if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) /* dr_flac_h begin */ -#ifndef dr_flac_h -#define dr_flac_h +#ifndef ma_dr_flac_h +#define ma_dr_flac_h #ifdef __cplusplus extern "C" { #endif -#define DRFLAC_STRINGIFY(x) #x -#define DRFLAC_XSTRINGIFY(x) DRFLAC_STRINGIFY(x) -#define DRFLAC_VERSION_MAJOR 0 -#define DRFLAC_VERSION_MINOR 12 -#define DRFLAC_VERSION_REVISION 39 -#define DRFLAC_VERSION_STRING DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MAJOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_MINOR) "." DRFLAC_XSTRINGIFY(DRFLAC_VERSION_REVISION) +#define MA_DR_FLAC_STRINGIFY(x) #x +#define MA_DR_FLAC_XSTRINGIFY(x) MA_DR_FLAC_STRINGIFY(x) +#define MA_DR_FLAC_VERSION_MAJOR 0 +#define MA_DR_FLAC_VERSION_MINOR 12 +#define MA_DR_FLAC_VERSION_REVISION 41 +#define MA_DR_FLAC_VERSION_STRING MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MAJOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_MINOR) "." MA_DR_FLAC_XSTRINGIFY(MA_DR_FLAC_VERSION_REVISION) #include -typedef signed char drflac_int8; -typedef unsigned char drflac_uint8; -typedef signed short drflac_int16; -typedef unsigned short drflac_uint16; -typedef signed int drflac_int32; -typedef unsigned int drflac_uint32; -#if defined(_MSC_VER) && !defined(__clang__) - typedef signed __int64 drflac_int64; - typedef unsigned __int64 drflac_uint64; -#else - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif - #endif - typedef signed long long drflac_int64; - typedef unsigned long long drflac_uint64; - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop - #endif -#endif -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined(_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) - typedef drflac_uint64 drflac_uintptr; -#else - typedef drflac_uint32 drflac_uintptr; -#endif -typedef drflac_uint8 drflac_bool8; -typedef drflac_uint32 drflac_bool32; -#define DRFLAC_TRUE 1 -#define DRFLAC_FALSE 0 -#if !defined(DRFLAC_API) - #if defined(DRFLAC_DLL) - #if defined(_WIN32) - #define DRFLAC_DLL_IMPORT __declspec(dllimport) - #define DRFLAC_DLL_EXPORT __declspec(dllexport) - #define DRFLAC_DLL_PRIVATE static - #else - #if defined(__GNUC__) && __GNUC__ >= 4 - #define DRFLAC_DLL_IMPORT __attribute__((visibility("default"))) - #define DRFLAC_DLL_EXPORT __attribute__((visibility("default"))) - #define DRFLAC_DLL_PRIVATE __attribute__((visibility("hidden"))) - #else - #define DRFLAC_DLL_IMPORT - #define DRFLAC_DLL_EXPORT - #define DRFLAC_DLL_PRIVATE static - #endif - #endif - #if defined(DR_FLAC_IMPLEMENTATION) || defined(DRFLAC_IMPLEMENTATION) - #define DRFLAC_API DRFLAC_DLL_EXPORT - #else - #define DRFLAC_API DRFLAC_DLL_IMPORT - #endif - #define DRFLAC_PRIVATE DRFLAC_DLL_PRIVATE - #else - #define DRFLAC_API extern - #define DRFLAC_PRIVATE static - #endif -#endif #if defined(_MSC_VER) && _MSC_VER >= 1700 - #define DRFLAC_DEPRECATED __declspec(deprecated) + #define MA_DR_FLAC_DEPRECATED __declspec(deprecated) #elif (defined(__GNUC__) && __GNUC__ >= 4) - #define DRFLAC_DEPRECATED __attribute__((deprecated)) + #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated)) #elif defined(__has_feature) #if __has_feature(attribute_deprecated) - #define DRFLAC_DEPRECATED __attribute__((deprecated)) + #define MA_DR_FLAC_DEPRECATED __attribute__((deprecated)) #else - #define DRFLAC_DEPRECATED + #define MA_DR_FLAC_DEPRECATED #endif #else - #define DRFLAC_DEPRECATED + #define MA_DR_FLAC_DEPRECATED #endif -DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision); -DRFLAC_API const char* drflac_version_string(void); -#ifndef DR_FLAC_BUFFER_SIZE -#define DR_FLAC_BUFFER_SIZE 4096 +MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); +MA_API const char* ma_dr_flac_version_string(void); +#ifndef MA_DR_FLAC_BUFFER_SIZE +#define MA_DR_FLAC_BUFFER_SIZE 4096 #endif -#if defined(_WIN64) || defined(_LP64) || defined(__LP64__) -#define DRFLAC_64BIT -#endif -#ifdef DRFLAC_64BIT -typedef drflac_uint64 drflac_cache_t; +#ifdef MA_64BIT +typedef ma_uint64 ma_dr_flac_cache_t; #else -typedef drflac_uint32 drflac_cache_t; +typedef ma_uint32 ma_dr_flac_cache_t; #endif -#define DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO 0 -#define DRFLAC_METADATA_BLOCK_TYPE_PADDING 1 -#define DRFLAC_METADATA_BLOCK_TYPE_APPLICATION 2 -#define DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3 -#define DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4 -#define DRFLAC_METADATA_BLOCK_TYPE_CUESHEET 5 -#define DRFLAC_METADATA_BLOCK_TYPE_PICTURE 6 -#define DRFLAC_METADATA_BLOCK_TYPE_INVALID 127 -#define DRFLAC_PICTURE_TYPE_OTHER 0 -#define DRFLAC_PICTURE_TYPE_FILE_ICON 1 -#define DRFLAC_PICTURE_TYPE_OTHER_FILE_ICON 2 -#define DRFLAC_PICTURE_TYPE_COVER_FRONT 3 -#define DRFLAC_PICTURE_TYPE_COVER_BACK 4 -#define DRFLAC_PICTURE_TYPE_LEAFLET_PAGE 5 -#define DRFLAC_PICTURE_TYPE_MEDIA 6 -#define DRFLAC_PICTURE_TYPE_LEAD_ARTIST 7 -#define DRFLAC_PICTURE_TYPE_ARTIST 8 -#define DRFLAC_PICTURE_TYPE_CONDUCTOR 9 -#define DRFLAC_PICTURE_TYPE_BAND 10 -#define DRFLAC_PICTURE_TYPE_COMPOSER 11 -#define DRFLAC_PICTURE_TYPE_LYRICIST 12 -#define DRFLAC_PICTURE_TYPE_RECORDING_LOCATION 13 -#define DRFLAC_PICTURE_TYPE_DURING_RECORDING 14 -#define DRFLAC_PICTURE_TYPE_DURING_PERFORMANCE 15 -#define DRFLAC_PICTURE_TYPE_SCREEN_CAPTURE 16 -#define DRFLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17 -#define DRFLAC_PICTURE_TYPE_ILLUSTRATION 18 -#define DRFLAC_PICTURE_TYPE_BAND_LOGOTYPE 19 -#define DRFLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO 0 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING 1 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION 2 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE 3 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT 4 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET 5 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE 6 +#define MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID 127 +#define MA_DR_FLAC_PICTURE_TYPE_OTHER 0 +#define MA_DR_FLAC_PICTURE_TYPE_FILE_ICON 1 +#define MA_DR_FLAC_PICTURE_TYPE_OTHER_FILE_ICON 2 +#define MA_DR_FLAC_PICTURE_TYPE_COVER_FRONT 3 +#define MA_DR_FLAC_PICTURE_TYPE_COVER_BACK 4 +#define MA_DR_FLAC_PICTURE_TYPE_LEAFLET_PAGE 5 +#define MA_DR_FLAC_PICTURE_TYPE_MEDIA 6 +#define MA_DR_FLAC_PICTURE_TYPE_LEAD_ARTIST 7 +#define MA_DR_FLAC_PICTURE_TYPE_ARTIST 8 +#define MA_DR_FLAC_PICTURE_TYPE_CONDUCTOR 9 +#define MA_DR_FLAC_PICTURE_TYPE_BAND 10 +#define MA_DR_FLAC_PICTURE_TYPE_COMPOSER 11 +#define MA_DR_FLAC_PICTURE_TYPE_LYRICIST 12 +#define MA_DR_FLAC_PICTURE_TYPE_RECORDING_LOCATION 13 +#define MA_DR_FLAC_PICTURE_TYPE_DURING_RECORDING 14 +#define MA_DR_FLAC_PICTURE_TYPE_DURING_PERFORMANCE 15 +#define MA_DR_FLAC_PICTURE_TYPE_SCREEN_CAPTURE 16 +#define MA_DR_FLAC_PICTURE_TYPE_BRIGHT_COLORED_FISH 17 +#define MA_DR_FLAC_PICTURE_TYPE_ILLUSTRATION 18 +#define MA_DR_FLAC_PICTURE_TYPE_BAND_LOGOTYPE 19 +#define MA_DR_FLAC_PICTURE_TYPE_PUBLISHER_LOGOTYPE 20 typedef enum { - drflac_container_native, - drflac_container_ogg, - drflac_container_unknown -} drflac_container; + ma_dr_flac_container_native, + ma_dr_flac_container_ogg, + ma_dr_flac_container_unknown +} ma_dr_flac_container; typedef enum { - drflac_seek_origin_start, - drflac_seek_origin_current -} drflac_seek_origin; + ma_dr_flac_seek_origin_start, + ma_dr_flac_seek_origin_current +} ma_dr_flac_seek_origin; typedef struct { - drflac_uint64 firstPCMFrame; - drflac_uint64 flacFrameOffset; - drflac_uint16 pcmFrameCount; -} drflac_seekpoint; + ma_uint64 firstPCMFrame; + ma_uint64 flacFrameOffset; + ma_uint16 pcmFrameCount; +} ma_dr_flac_seekpoint; typedef struct { - drflac_uint16 minBlockSizeInPCMFrames; - drflac_uint16 maxBlockSizeInPCMFrames; - drflac_uint32 minFrameSizeInPCMFrames; - drflac_uint32 maxFrameSizeInPCMFrames; - drflac_uint32 sampleRate; - drflac_uint8 channels; - drflac_uint8 bitsPerSample; - drflac_uint64 totalPCMFrameCount; - drflac_uint8 md5[16]; -} drflac_streaminfo; + ma_uint16 minBlockSizeInPCMFrames; + ma_uint16 maxBlockSizeInPCMFrames; + ma_uint32 minFrameSizeInPCMFrames; + ma_uint32 maxFrameSizeInPCMFrames; + ma_uint32 sampleRate; + ma_uint8 channels; + ma_uint8 bitsPerSample; + ma_uint64 totalPCMFrameCount; + ma_uint8 md5[16]; +} ma_dr_flac_streaminfo; typedef struct { - drflac_uint32 type; + ma_uint32 type; const void* pRawData; - drflac_uint32 rawDataSize; + ma_uint32 rawDataSize; union { - drflac_streaminfo streaminfo; + ma_dr_flac_streaminfo streaminfo; struct { int unused; } padding; struct { - drflac_uint32 id; + ma_uint32 id; const void* pData; - drflac_uint32 dataSize; + ma_uint32 dataSize; } application; struct { - drflac_uint32 seekpointCount; - const drflac_seekpoint* pSeekpoints; + ma_uint32 seekpointCount; + const ma_dr_flac_seekpoint* pSeekpoints; } seektable; struct { - drflac_uint32 vendorLength; + ma_uint32 vendorLength; const char* vendor; - drflac_uint32 commentCount; + ma_uint32 commentCount; const void* pComments; } vorbis_comment; struct { char catalog[128]; - drflac_uint64 leadInSampleCount; - drflac_bool32 isCD; - drflac_uint8 trackCount; + ma_uint64 leadInSampleCount; + ma_bool32 isCD; + ma_uint8 trackCount; const void* pTrackData; } cuesheet; struct { - drflac_uint32 type; - drflac_uint32 mimeLength; + ma_uint32 type; + ma_uint32 mimeLength; const char* mime; - drflac_uint32 descriptionLength; + ma_uint32 descriptionLength; const char* description; - drflac_uint32 width; - drflac_uint32 height; - drflac_uint32 colorDepth; - drflac_uint32 indexColorCount; - drflac_uint32 pictureDataSize; - const drflac_uint8* pPictureData; + ma_uint32 width; + ma_uint32 height; + ma_uint32 colorDepth; + ma_uint32 indexColorCount; + ma_uint32 pictureDataSize; + const ma_uint8* pPictureData; } picture; } data; -} drflac_metadata; -typedef size_t (* drflac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef drflac_bool32 (* drflac_seek_proc)(void* pUserData, int offset, drflac_seek_origin origin); -typedef void (* drflac_meta_proc)(void* pUserData, drflac_metadata* pMetadata); +} ma_dr_flac_metadata; +typedef size_t (* ma_dr_flac_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); +typedef ma_bool32 (* ma_dr_flac_seek_proc)(void* pUserData, int offset, ma_dr_flac_seek_origin origin); +typedef void (* ma_dr_flac_meta_proc)(void* pUserData, ma_dr_flac_metadata* pMetadata); typedef struct { - void* pUserData; - void* (* onMalloc)(size_t sz, void* pUserData); - void* (* onRealloc)(void* p, size_t sz, void* pUserData); - void (* onFree)(void* p, void* pUserData); -} drflac_allocation_callbacks; -typedef struct -{ - const drflac_uint8* data; + const ma_uint8* data; size_t dataSize; size_t currentReadPos; -} drflac__memory_stream; +} ma_dr_flac__memory_stream; typedef struct { - drflac_read_proc onRead; - drflac_seek_proc onSeek; + ma_dr_flac_read_proc onRead; + ma_dr_flac_seek_proc onSeek; void* pUserData; size_t unalignedByteCount; - drflac_cache_t unalignedCache; - drflac_uint32 nextL2Line; - drflac_uint32 consumedBits; - drflac_cache_t cacheL2[DR_FLAC_BUFFER_SIZE/sizeof(drflac_cache_t)]; - drflac_cache_t cache; - drflac_uint16 crc16; - drflac_cache_t crc16Cache; - drflac_uint32 crc16CacheIgnoredBytes; -} drflac_bs; + ma_dr_flac_cache_t unalignedCache; + ma_uint32 nextL2Line; + ma_uint32 consumedBits; + ma_dr_flac_cache_t cacheL2[MA_DR_FLAC_BUFFER_SIZE/sizeof(ma_dr_flac_cache_t)]; + ma_dr_flac_cache_t cache; + ma_uint16 crc16; + ma_dr_flac_cache_t crc16Cache; + ma_uint32 crc16CacheIgnoredBytes; +} ma_dr_flac_bs; typedef struct { - drflac_uint8 subframeType; - drflac_uint8 wastedBitsPerSample; - drflac_uint8 lpcOrder; - drflac_int32* pSamplesS32; -} drflac_subframe; + ma_uint8 subframeType; + ma_uint8 wastedBitsPerSample; + ma_uint8 lpcOrder; + ma_int32* pSamplesS32; +} ma_dr_flac_subframe; typedef struct { - drflac_uint64 pcmFrameNumber; - drflac_uint32 flacFrameNumber; - drflac_uint32 sampleRate; - drflac_uint16 blockSizeInPCMFrames; - drflac_uint8 channelAssignment; - drflac_uint8 bitsPerSample; - drflac_uint8 crc8; -} drflac_frame_header; + ma_uint64 pcmFrameNumber; + ma_uint32 flacFrameNumber; + ma_uint32 sampleRate; + ma_uint16 blockSizeInPCMFrames; + ma_uint8 channelAssignment; + ma_uint8 bitsPerSample; + ma_uint8 crc8; +} ma_dr_flac_frame_header; typedef struct { - drflac_frame_header header; - drflac_uint32 pcmFramesRemaining; - drflac_subframe subframes[8]; -} drflac_frame; + ma_dr_flac_frame_header header; + ma_uint32 pcmFramesRemaining; + ma_dr_flac_subframe subframes[8]; +} ma_dr_flac_frame; typedef struct { - drflac_meta_proc onMeta; + ma_dr_flac_meta_proc onMeta; void* pUserDataMD; - drflac_allocation_callbacks allocationCallbacks; - drflac_uint32 sampleRate; - drflac_uint8 channels; - drflac_uint8 bitsPerSample; - drflac_uint16 maxBlockSizeInPCMFrames; - drflac_uint64 totalPCMFrameCount; - drflac_container container; - drflac_uint32 seekpointCount; - drflac_frame currentFLACFrame; - drflac_uint64 currentPCMFrame; - drflac_uint64 firstFLACFramePosInBytes; - drflac__memory_stream memoryStream; - drflac_int32* pDecodedSamples; - drflac_seekpoint* pSeekpoints; + ma_allocation_callbacks allocationCallbacks; + ma_uint32 sampleRate; + ma_uint8 channels; + ma_uint8 bitsPerSample; + ma_uint16 maxBlockSizeInPCMFrames; + ma_uint64 totalPCMFrameCount; + ma_dr_flac_container container; + ma_uint32 seekpointCount; + ma_dr_flac_frame currentFLACFrame; + ma_uint64 currentPCMFrame; + ma_uint64 firstFLACFramePosInBytes; + ma_dr_flac__memory_stream memoryStream; + ma_int32* pDecodedSamples; + ma_dr_flac_seekpoint* pSeekpoints; void* _oggbs; - drflac_bool32 _noSeekTableSeek : 1; - drflac_bool32 _noBinarySearchSeek : 1; - drflac_bool32 _noBruteForceSeek : 1; - drflac_bs bs; - drflac_uint8 pExtraData[1]; -} drflac; -DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API void drflac_close(drflac* pFlac); -DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut); -DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut); -DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut); -DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex); -#ifndef DR_FLAC_NO_STDIO -DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); + ma_bool32 _noSeekTableSeek : 1; + ma_bool32 _noBinarySearchSeek : 1; + ma_bool32 _noBruteForceSeek : 1; + ma_dr_flac_bs bs; + ma_uint8 pExtraData[1]; +} ma_dr_flac; +MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API void ma_dr_flac_close(ma_dr_flac* pFlac); +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut); +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut); +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut); +MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex); +#ifndef MA_DR_FLAC_NO_STDIO +MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); #endif -DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -#ifndef DR_FLAC_NO_STDIO -DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_FLAC_NO_STDIO +MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); #endif -DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks); -DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); typedef struct { - drflac_uint32 countRemaining; + ma_uint32 countRemaining; const char* pRunningData; -} drflac_vorbis_comment_iterator; -DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments); -DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut); +} ma_dr_flac_vorbis_comment_iterator; +MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments); +MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut); typedef struct { - drflac_uint32 countRemaining; + ma_uint32 countRemaining; const char* pRunningData; -} drflac_cuesheet_track_iterator; +} ma_dr_flac_cuesheet_track_iterator; typedef struct { - drflac_uint64 offset; - drflac_uint8 index; - drflac_uint8 reserved[3]; -} drflac_cuesheet_track_index; + ma_uint64 offset; + ma_uint8 index; + ma_uint8 reserved[3]; +} ma_dr_flac_cuesheet_track_index; typedef struct { - drflac_uint64 offset; - drflac_uint8 trackNumber; + ma_uint64 offset; + ma_uint8 trackNumber; char ISRC[12]; - drflac_bool8 isAudio; - drflac_bool8 preEmphasis; - drflac_uint8 indexCount; - const drflac_cuesheet_track_index* pIndexPoints; -} drflac_cuesheet_track; -DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData); -DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack); + ma_bool8 isAudio; + ma_bool8 preEmphasis; + ma_uint8 indexCount; + const ma_dr_flac_cuesheet_track_index* pIndexPoints; +} ma_dr_flac_cuesheet_track; +MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData); +MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack); #ifdef __cplusplus } #endif @@ -60552,249 +60399,109 @@ DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterat #if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) /* dr_mp3_h begin */ -#ifndef dr_mp3_h -#define dr_mp3_h +#ifndef ma_dr_mp3_h +#define ma_dr_mp3_h #ifdef __cplusplus extern "C" { #endif -#define DRMP3_STRINGIFY(x) #x -#define DRMP3_XSTRINGIFY(x) DRMP3_STRINGIFY(x) -#define DRMP3_VERSION_MAJOR 0 -#define DRMP3_VERSION_MINOR 6 -#define DRMP3_VERSION_REVISION 34 -#define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION) +#define MA_DR_MP3_STRINGIFY(x) #x +#define MA_DR_MP3_XSTRINGIFY(x) MA_DR_MP3_STRINGIFY(x) +#define MA_DR_MP3_VERSION_MAJOR 0 +#define MA_DR_MP3_VERSION_MINOR 6 +#define MA_DR_MP3_VERSION_REVISION 37 +#define MA_DR_MP3_VERSION_STRING MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MAJOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_MINOR) "." MA_DR_MP3_XSTRINGIFY(MA_DR_MP3_VERSION_REVISION) #include -typedef signed char drmp3_int8; -typedef unsigned char drmp3_uint8; -typedef signed short drmp3_int16; -typedef unsigned short drmp3_uint16; -typedef signed int drmp3_int32; -typedef unsigned int drmp3_uint32; -#if defined(_MSC_VER) && !defined(__clang__) - typedef signed __int64 drmp3_int64; - typedef unsigned __int64 drmp3_uint64; -#else - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif - #endif - typedef signed long long drmp3_int64; - typedef unsigned long long drmp3_uint64; - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop - #endif -#endif -#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__) - typedef drmp3_uint64 drmp3_uintptr; -#else - typedef drmp3_uint32 drmp3_uintptr; -#endif -typedef drmp3_uint8 drmp3_bool8; -typedef drmp3_uint32 drmp3_bool32; -#define DRMP3_TRUE 1 -#define DRMP3_FALSE 0 -#if !defined(DRMP3_API) - #if defined(DRMP3_DLL) - #if defined(_WIN32) - #define DRMP3_DLL_IMPORT __declspec(dllimport) - #define DRMP3_DLL_EXPORT __declspec(dllexport) - #define DRMP3_DLL_PRIVATE static - #else - #if defined(__GNUC__) && __GNUC__ >= 4 - #define DRMP3_DLL_IMPORT __attribute__((visibility("default"))) - #define DRMP3_DLL_EXPORT __attribute__((visibility("default"))) - #define DRMP3_DLL_PRIVATE __attribute__((visibility("hidden"))) - #else - #define DRMP3_DLL_IMPORT - #define DRMP3_DLL_EXPORT - #define DRMP3_DLL_PRIVATE static - #endif - #endif - #if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION) - #define DRMP3_API DRMP3_DLL_EXPORT - #else - #define DRMP3_API DRMP3_DLL_IMPORT - #endif - #define DRMP3_PRIVATE DRMP3_DLL_PRIVATE - #else - #define DRMP3_API extern - #define DRMP3_PRIVATE static - #endif -#endif -typedef drmp3_int32 drmp3_result; -#define DRMP3_SUCCESS 0 -#define DRMP3_ERROR -1 -#define DRMP3_INVALID_ARGS -2 -#define DRMP3_INVALID_OPERATION -3 -#define DRMP3_OUT_OF_MEMORY -4 -#define DRMP3_OUT_OF_RANGE -5 -#define DRMP3_ACCESS_DENIED -6 -#define DRMP3_DOES_NOT_EXIST -7 -#define DRMP3_ALREADY_EXISTS -8 -#define DRMP3_TOO_MANY_OPEN_FILES -9 -#define DRMP3_INVALID_FILE -10 -#define DRMP3_TOO_BIG -11 -#define DRMP3_PATH_TOO_LONG -12 -#define DRMP3_NAME_TOO_LONG -13 -#define DRMP3_NOT_DIRECTORY -14 -#define DRMP3_IS_DIRECTORY -15 -#define DRMP3_DIRECTORY_NOT_EMPTY -16 -#define DRMP3_END_OF_FILE -17 -#define DRMP3_NO_SPACE -18 -#define DRMP3_BUSY -19 -#define DRMP3_IO_ERROR -20 -#define DRMP3_INTERRUPT -21 -#define DRMP3_UNAVAILABLE -22 -#define DRMP3_ALREADY_IN_USE -23 -#define DRMP3_BAD_ADDRESS -24 -#define DRMP3_BAD_SEEK -25 -#define DRMP3_BAD_PIPE -26 -#define DRMP3_DEADLOCK -27 -#define DRMP3_TOO_MANY_LINKS -28 -#define DRMP3_NOT_IMPLEMENTED -29 -#define DRMP3_NO_MESSAGE -30 -#define DRMP3_BAD_MESSAGE -31 -#define DRMP3_NO_DATA_AVAILABLE -32 -#define DRMP3_INVALID_DATA -33 -#define DRMP3_TIMEOUT -34 -#define DRMP3_NO_NETWORK -35 -#define DRMP3_NOT_UNIQUE -36 -#define DRMP3_NOT_SOCKET -37 -#define DRMP3_NO_ADDRESS -38 -#define DRMP3_BAD_PROTOCOL -39 -#define DRMP3_PROTOCOL_UNAVAILABLE -40 -#define DRMP3_PROTOCOL_NOT_SUPPORTED -41 -#define DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED -42 -#define DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED -43 -#define DRMP3_SOCKET_NOT_SUPPORTED -44 -#define DRMP3_CONNECTION_RESET -45 -#define DRMP3_ALREADY_CONNECTED -46 -#define DRMP3_NOT_CONNECTED -47 -#define DRMP3_CONNECTION_REFUSED -48 -#define DRMP3_NO_HOST -49 -#define DRMP3_IN_PROGRESS -50 -#define DRMP3_CANCELLED -51 -#define DRMP3_MEMORY_ALREADY_MAPPED -52 -#define DRMP3_AT_END -53 -#define DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 -#define DRMP3_MAX_SAMPLES_PER_FRAME (DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2) -#ifdef _MSC_VER - #define DRMP3_INLINE __forceinline -#elif defined(__GNUC__) - #if defined(__STRICT_ANSI__) - #define DRMP3_GNUC_INLINE_HINT __inline__ - #else - #define DRMP3_GNUC_INLINE_HINT inline - #endif - #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) - #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT __attribute__((always_inline)) - #else - #define DRMP3_INLINE DRMP3_GNUC_INLINE_HINT - #endif -#elif defined(__WATCOMC__) - #define DRMP3_INLINE __inline -#else - #define DRMP3_INLINE -#endif -DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision); -DRMP3_API const char* drmp3_version_string(void); +#define MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152 +#define MA_DR_MP3_MAX_SAMPLES_PER_FRAME (MA_DR_MP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2) +MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision); +MA_API const char* ma_dr_mp3_version_string(void); typedef struct { int frame_bytes, channels, hz, layer, bitrate_kbps; -} drmp3dec_frame_info; +} ma_dr_mp3dec_frame_info; typedef struct { float mdct_overlap[2][9*32], qmf_state[15*2*32]; int reserv, free_format_bytes; - drmp3_uint8 header[4], reserv_buf[511]; -} drmp3dec; -DRMP3_API void drmp3dec_init(drmp3dec *dec); -DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info); -DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples); + ma_uint8 header[4], reserv_buf[511]; +} ma_dr_mp3dec; +MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec); +MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info); +MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples); typedef enum { - drmp3_seek_origin_start, - drmp3_seek_origin_current -} drmp3_seek_origin; + ma_dr_mp3_seek_origin_start, + ma_dr_mp3_seek_origin_current +} ma_dr_mp3_seek_origin; typedef struct { - drmp3_uint64 seekPosInBytes; - drmp3_uint64 pcmFrameIndex; - drmp3_uint16 mp3FramesToDiscard; - drmp3_uint16 pcmFramesToDiscard; -} drmp3_seek_point; -typedef size_t (* drmp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); -typedef drmp3_bool32 (* drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin); + ma_uint64 seekPosInBytes; + ma_uint64 pcmFrameIndex; + ma_uint16 mp3FramesToDiscard; + ma_uint16 pcmFramesToDiscard; +} ma_dr_mp3_seek_point; +typedef size_t (* ma_dr_mp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); +typedef ma_bool32 (* ma_dr_mp3_seek_proc)(void* pUserData, int offset, ma_dr_mp3_seek_origin origin); typedef struct { + ma_uint32 channels; + ma_uint32 sampleRate; +} ma_dr_mp3_config; +typedef struct +{ + ma_dr_mp3dec decoder; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_dr_mp3_read_proc onRead; + ma_dr_mp3_seek_proc onSeek; void* pUserData; - void* (* onMalloc)(size_t sz, void* pUserData); - void* (* onRealloc)(void* p, size_t sz, void* pUserData); - void (* onFree)(void* p, void* pUserData); -} drmp3_allocation_callbacks; -typedef struct -{ - drmp3_uint32 channels; - drmp3_uint32 sampleRate; -} drmp3_config; -typedef struct -{ - drmp3dec decoder; - drmp3_uint32 channels; - drmp3_uint32 sampleRate; - drmp3_read_proc onRead; - drmp3_seek_proc onSeek; - void* pUserData; - drmp3_allocation_callbacks allocationCallbacks; - drmp3_uint32 mp3FrameChannels; - drmp3_uint32 mp3FrameSampleRate; - drmp3_uint32 pcmFramesConsumedInMP3Frame; - drmp3_uint32 pcmFramesRemainingInMP3Frame; - drmp3_uint8 pcmFrames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME]; - drmp3_uint64 currentPCMFrame; - drmp3_uint64 streamCursor; - drmp3_seek_point* pSeekPoints; - drmp3_uint32 seekPointCount; + ma_allocation_callbacks allocationCallbacks; + ma_uint32 mp3FrameChannels; + ma_uint32 mp3FrameSampleRate; + ma_uint32 pcmFramesConsumedInMP3Frame; + ma_uint32 pcmFramesRemainingInMP3Frame; + ma_uint8 pcmFrames[sizeof(float)*MA_DR_MP3_MAX_SAMPLES_PER_FRAME]; + ma_uint64 currentPCMFrame; + ma_uint64 streamCursor; + ma_dr_mp3_seek_point* pSeekPoints; + ma_uint32 seekPointCount; size_t dataSize; size_t dataCapacity; size_t dataConsumed; - drmp3_uint8* pData; - drmp3_bool32 atEnd : 1; + ma_uint8* pData; + ma_bool32 atEnd : 1; struct { - const drmp3_uint8* pData; + const ma_uint8* pData; size_t dataSize; size_t currentReadPos; } memory; -} drmp3; -DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks); -DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks); -#ifndef DR_MP3_NO_STDIO -DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks); -DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks); +} ma_dr_mp3; +MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_MP3_NO_STDIO +MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks); #endif -DRMP3_API void drmp3_uninit(drmp3* pMP3); -DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut); -DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut); -DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex); -DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3); -DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3); -DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount); -DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints); -DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints); -DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); -DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); -DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); -DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); -#ifndef DR_MP3_NO_STDIO -DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); -DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks); +MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3); +MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut); +MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut); +MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex); +MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3); +MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3); +MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount); +MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints); +MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints); +MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +#ifndef MA_DR_MP3_NO_STDIO +MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks); #endif -DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks); -DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks); +MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks); #ifdef __cplusplus } #endif @@ -61008,7 +60715,7 @@ static ma_result ma_decoder_internal_on_tell__custom(void* pUserData, ma_int64* } -static ma_result ma_decoder_init_from_vtable(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +static ma_result ma_decoder_init_from_vtable__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { ma_result result; ma_decoding_backend_config backendConfig; @@ -61037,6 +60744,93 @@ static ma_result ma_decoder_init_from_vtable(const ma_decoding_backend_vtable* p return MA_SUCCESS; } +static ma_result ma_decoder_init_from_file__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoding_backend_config backendConfig; + ma_data_source* pBackend; + + MA_ASSERT(pVTable != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pVTable->onInitFile == NULL) { + return MA_NOT_IMPLEMENTED; + } + + backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); + + result = pVTable->onInitFile(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the backend from this vtable. */ + } + + /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ + pDecoder->pBackend = pBackend; + pDecoder->pBackendVTable = pVTable; + pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; + + return MA_SUCCESS; +} + +static ma_result ma_decoder_init_from_file_w__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoding_backend_config backendConfig; + ma_data_source* pBackend; + + MA_ASSERT(pVTable != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pVTable->onInitFileW == NULL) { + return MA_NOT_IMPLEMENTED; + } + + backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); + + result = pVTable->onInitFileW(pVTableUserData, pFilePath, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the backend from this vtable. */ + } + + /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ + pDecoder->pBackend = pBackend; + pDecoder->pBackendVTable = pVTable; + pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; + + return MA_SUCCESS; +} + +static ma_result ma_decoder_init_from_memory__internal(const ma_decoding_backend_vtable* pVTable, void* pVTableUserData, const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + ma_decoding_backend_config backendConfig; + ma_data_source* pBackend; + + MA_ASSERT(pVTable != NULL); + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pVTable->onInitMemory == NULL) { + return MA_NOT_IMPLEMENTED; + } + + backendConfig = ma_decoding_backend_config_init(pConfig->format, pConfig->seekPointCount); + + result = pVTable->onInitMemory(pVTableUserData, pData, dataSize, &backendConfig, &pDecoder->allocationCallbacks, &pBackend); + if (result != MA_SUCCESS) { + return result; /* Failed to initialize the backend from this vtable. */ + } + + /* Getting here means we were able to initialize the backend so we can now initialize the decoder. */ + pDecoder->pBackend = pBackend; + pDecoder->pBackendVTable = pVTable; + pDecoder->pBackendUserData = pConfig->pCustomBackendUserData; + + return MA_SUCCESS; +} + static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) @@ -61054,8 +60848,8 @@ static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConf /* The order each backend is listed is what defines the priority. */ for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; - if (pVTable != NULL && pVTable->onInit != NULL) { - result = ma_decoder_init_from_vtable(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder); + if (pVTable != NULL) { + result = ma_decoder_init_from_vtable__internal(pVTable, pConfig->pCustomBackendUserData, pConfig, pDecoder); if (result == MA_SUCCESS) { return MA_SUCCESS; } else { @@ -61074,9 +60868,96 @@ static ma_result ma_decoder_init_custom__internal(const ma_decoder_config* pConf return MA_NO_BACKEND; } +static ma_result ma_decoder_init_custom_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = MA_NO_BACKEND; + size_t ivtable; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pConfig->ppCustomBackendVTables == NULL) { + return MA_NO_BACKEND; + } + + /* The order each backend is listed is what defines the priority. */ + for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { + const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; + if (pVTable != NULL) { + result = ma_decoder_init_from_file__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder); + if (result == MA_SUCCESS) { + return MA_SUCCESS; + } + } else { + /* No vtable. */ + } + } + + /* Getting here means we couldn't find a backend. */ + return MA_NO_BACKEND; +} + +static ma_result ma_decoder_init_custom_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = MA_NO_BACKEND; + size_t ivtable; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pConfig->ppCustomBackendVTables == NULL) { + return MA_NO_BACKEND; + } + + /* The order each backend is listed is what defines the priority. */ + for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { + const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; + if (pVTable != NULL) { + result = ma_decoder_init_from_file_w__internal(pVTable, pConfig->pCustomBackendUserData, pFilePath, pConfig, pDecoder); + if (result == MA_SUCCESS) { + return MA_SUCCESS; + } + } else { + /* No vtable. */ + } + } + + /* Getting here means we couldn't find a backend. */ + return MA_NO_BACKEND; +} + +static ma_result ma_decoder_init_custom_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result = MA_NO_BACKEND; + size_t ivtable; + + MA_ASSERT(pConfig != NULL); + MA_ASSERT(pDecoder != NULL); + + if (pConfig->ppCustomBackendVTables == NULL) { + return MA_NO_BACKEND; + } + + /* The order each backend is listed is what defines the priority. */ + for (ivtable = 0; ivtable < pConfig->customBackendCount; ivtable += 1) { + const ma_decoding_backend_vtable* pVTable = pConfig->ppCustomBackendVTables[ivtable]; + if (pVTable != NULL) { + result = ma_decoder_init_from_memory__internal(pVTable, pConfig->pCustomBackendUserData, pData, dataSize, pConfig, pDecoder); + if (result == MA_SUCCESS) { + return MA_SUCCESS; + } + } else { + /* No vtable. */ + } + } + + /* Getting here means we couldn't find a backend. */ + return MA_NO_BACKEND; +} + /* WAV */ -#ifdef dr_wav_h +#ifdef ma_dr_wav_h #define MA_HAS_WAV typedef struct @@ -61088,7 +60969,7 @@ typedef struct void* pReadSeekTellUserData; ma_format format; /* Can be f32, s16 or s32. */ #if !defined(MA_NO_WAV) - drwav dr; + ma_dr_wav dr; #endif } ma_wav; @@ -61142,25 +61023,6 @@ static ma_data_source_vtable g_ma_wav_ds_vtable = #if !defined(MA_NO_WAV) -static drwav_allocation_callbacks drwav_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks) -{ - drwav_allocation_callbacks callbacks; - - if (pAllocationCallbacks != NULL) { - callbacks.onMalloc = pAllocationCallbacks->onMalloc; - callbacks.onRealloc = pAllocationCallbacks->onRealloc; - callbacks.onFree = pAllocationCallbacks->onFree; - callbacks.pUserData = pAllocationCallbacks->pUserData; - } else { - callbacks.onMalloc = ma__malloc_default; - callbacks.onRealloc = ma__realloc_default; - callbacks.onFree = ma__free_default; - callbacks.pUserData = NULL; - } - - return callbacks; -} - static size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) { ma_wav* pWav = (ma_wav*)pUserData; @@ -61175,7 +61037,7 @@ static size_t ma_wav_dr_callback__read(void* pUserData, void* pBufferOut, size_t return bytesRead; } -static drwav_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, drwav_seek_origin origin) +static ma_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, ma_dr_wav_seek_origin origin) { ma_wav* pWav = (ma_wav*)pUserData; ma_result result; @@ -61184,7 +61046,7 @@ static drwav_bool32 ma_wav_dr_callback__seek(void* pUserData, int offset, drwav_ MA_ASSERT(pWav != NULL); maSeekOrigin = ma_seek_origin_start; - if (origin == drwav_seek_origin_current) { + if (origin == ma_dr_wav_seek_origin_current) { maSeekOrigin = ma_seek_origin_current; } @@ -61226,6 +61088,47 @@ static ma_result ma_wav_init_internal(const ma_decoding_backend_config* pConfig, return MA_SUCCESS; } +static ma_result ma_wav_post_init(ma_wav* pWav) +{ + /* + If an explicit format was not specified, try picking the closest match based on the internal + format. The format needs to be supported by miniaudio. + */ + if (pWav->format == ma_format_unknown) { + switch (pWav->dr.translatedFormatTag) + { + case MA_DR_WAVE_FORMAT_PCM: + { + if (pWav->dr.bitsPerSample == 8) { + pWav->format = ma_format_u8; + } else if (pWav->dr.bitsPerSample == 16) { + pWav->format = ma_format_s16; + } else if (pWav->dr.bitsPerSample == 24) { + pWav->format = ma_format_s24; + } else if (pWav->dr.bitsPerSample == 32) { + pWav->format = ma_format_s32; + } + } break; + + case MA_DR_WAVE_FORMAT_IEEE_FLOAT: + { + if (pWav->dr.bitsPerSample == 32) { + pWav->format = ma_format_f32; + } + } break; + + default: break; + } + + /* Fall back to f32 if we couldn't find anything. */ + if (pWav->format == ma_format_unknown) { + pWav->format = ma_format_f32; + } + } + + return MA_SUCCESS; +} + MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_wav* pWav) { ma_result result; @@ -61246,49 +61149,14 @@ MA_API ma_result ma_wav_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_p #if !defined(MA_NO_WAV) { - drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drwav_bool32 wavResult; + ma_bool32 wavResult; - wavResult = drwav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, &wavAllocationCallbacks); + wavResult = ma_dr_wav_init(&pWav->dr, ma_wav_dr_callback__read, ma_wav_dr_callback__seek, pWav, pAllocationCallbacks); if (wavResult != MA_TRUE) { return MA_INVALID_FILE; } - /* - If an explicit format was not specified, try picking the closest match based on the internal - format. The format needs to be supported by miniaudio. - */ - if (pWav->format == ma_format_unknown) { - switch (pWav->dr.translatedFormatTag) - { - case DR_WAVE_FORMAT_PCM: - { - if (pWav->dr.bitsPerSample == 8) { - pWav->format = ma_format_u8; - } else if (pWav->dr.bitsPerSample == 16) { - pWav->format = ma_format_s16; - } else if (pWav->dr.bitsPerSample == 24) { - pWav->format = ma_format_s24; - } else if (pWav->dr.bitsPerSample == 32) { - pWav->format = ma_format_s32; - } - } break; - - case DR_WAVE_FORMAT_IEEE_FLOAT: - { - if (pWav->dr.bitsPerSample == 32) { - pWav->format = ma_format_f32; - } - } break; - - default: break; - } - - /* Fall back to f32 if we couldn't find anything. */ - if (pWav->format == ma_format_unknown) { - pWav->format = ma_format_f32; - } - } + ma_wav_post_init(pWav); return MA_SUCCESS; } @@ -61312,14 +61180,15 @@ MA_API ma_result ma_wav_init_file(const char* pFilePath, const ma_decoding_backe #if !defined(MA_NO_WAV) { - drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drwav_bool32 wavResult; + ma_bool32 wavResult; - wavResult = drwav_init_file(&pWav->dr, pFilePath, &wavAllocationCallbacks); + wavResult = ma_dr_wav_init_file(&pWav->dr, pFilePath, pAllocationCallbacks); if (wavResult != MA_TRUE) { return MA_INVALID_FILE; } + ma_wav_post_init(pWav); + return MA_SUCCESS; } #else @@ -61343,14 +61212,15 @@ MA_API ma_result ma_wav_init_file_w(const wchar_t* pFilePath, const ma_decoding_ #if !defined(MA_NO_WAV) { - drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drwav_bool32 wavResult; + ma_bool32 wavResult; - wavResult = drwav_init_file_w(&pWav->dr, pFilePath, &wavAllocationCallbacks); + wavResult = ma_dr_wav_init_file_w(&pWav->dr, pFilePath, pAllocationCallbacks); if (wavResult != MA_TRUE) { return MA_INVALID_FILE; } + ma_wav_post_init(pWav); + return MA_SUCCESS; } #else @@ -61374,14 +61244,15 @@ MA_API ma_result ma_wav_init_memory(const void* pData, size_t dataSize, const ma #if !defined(MA_NO_WAV) { - drwav_allocation_callbacks wavAllocationCallbacks = drwav_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drwav_bool32 wavResult; + ma_bool32 wavResult; - wavResult = drwav_init_memory(&pWav->dr, pData, dataSize, &wavAllocationCallbacks); + wavResult = ma_dr_wav_init_memory(&pWav->dr, pData, dataSize, pAllocationCallbacks); if (wavResult != MA_TRUE) { return MA_INVALID_FILE; } + ma_wav_post_init(pWav); + return MA_SUCCESS; } #else @@ -61405,7 +61276,7 @@ MA_API void ma_wav_uninit(ma_wav* pWav, const ma_allocation_callbacks* pAllocati #if !defined(MA_NO_WAV) { - drwav_uninit(&pWav->dr); + ma_dr_wav_uninit(&pWav->dr); } #else { @@ -61444,28 +61315,28 @@ MA_API ma_result ma_wav_read_pcm_frames(ma_wav* pWav, void* pFramesOut, ma_uint6 { case ma_format_f32: { - totalFramesRead = drwav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut); + totalFramesRead = ma_dr_wav_read_pcm_frames_f32(&pWav->dr, frameCount, (float*)pFramesOut); } break; case ma_format_s16: { - totalFramesRead = drwav_read_pcm_frames_s16(&pWav->dr, frameCount, (drwav_int16*)pFramesOut); + totalFramesRead = ma_dr_wav_read_pcm_frames_s16(&pWav->dr, frameCount, (ma_int16*)pFramesOut); } break; case ma_format_s32: { - totalFramesRead = drwav_read_pcm_frames_s32(&pWav->dr, frameCount, (drwav_int32*)pFramesOut); + totalFramesRead = ma_dr_wav_read_pcm_frames_s32(&pWav->dr, frameCount, (ma_int32*)pFramesOut); } break; /* Fallback to a raw read. */ case ma_format_unknown: return MA_INVALID_OPERATION; /* <-- this should never be hit because initialization would just fall back to a supported format. */ default: { - totalFramesRead = drwav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut); + totalFramesRead = ma_dr_wav_read_pcm_frames(&pWav->dr, frameCount, pFramesOut); } break; } - /* In the future we'll update dr_wav to return MA_AT_END for us. */ + /* In the future we'll update ma_dr_wav to return MA_AT_END for us. */ if (totalFramesRead == 0) { result = MA_AT_END; } @@ -61502,10 +61373,10 @@ MA_API ma_result ma_wav_seek_to_pcm_frame(ma_wav* pWav, ma_uint64 frameIndex) #if !defined(MA_NO_WAV) { - drwav_bool32 wavResult; + ma_bool32 wavResult; - wavResult = drwav_seek_to_pcm_frame(&pWav->dr, frameIndex); - if (wavResult != DRWAV_TRUE) { + wavResult = ma_dr_wav_seek_to_pcm_frame(&pWav->dr, frameIndex); + if (wavResult != MA_TRUE) { return MA_ERROR; } @@ -61586,9 +61457,9 @@ MA_API ma_result ma_wav_get_cursor_in_pcm_frames(ma_wav* pWav, ma_uint64* pCurso #if !defined(MA_NO_WAV) { - drwav_result wavResult = drwav_get_cursor_in_pcm_frames(&pWav->dr, pCursor); - if (wavResult != DRWAV_SUCCESS) { - return (ma_result)wavResult; /* dr_wav result codes map to miniaudio's. */ + ma_result wavResult = ma_dr_wav_get_cursor_in_pcm_frames(&pWav->dr, pCursor); + if (wavResult != MA_SUCCESS) { + return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */ } return MA_SUCCESS; @@ -61616,9 +61487,9 @@ MA_API ma_result ma_wav_get_length_in_pcm_frames(ma_wav* pWav, ma_uint64* pLengt #if !defined(MA_NO_WAV) { - drwav_result wavResult = drwav_get_length_in_pcm_frames(&pWav->dr, pLength); - if (wavResult != DRWAV_SUCCESS) { - return (ma_result)wavResult; /* dr_wav result codes map to miniaudio's. */ + ma_result wavResult = ma_dr_wav_get_length_in_pcm_frames(&pWav->dr, pLength); + if (wavResult != MA_SUCCESS) { + return (ma_result)wavResult; /* ma_dr_wav result codes map to miniaudio's. */ } return MA_SUCCESS; @@ -61750,12 +61621,27 @@ static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_wav = static ma_result ma_decoder_init_wav__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder); + return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_wav, NULL, pConfig, pDecoder); } -#endif /* dr_wav_h */ + +static ma_result ma_decoder_init_wav_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_wav_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_wav, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_wav_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_wav, NULL, pData, dataSize, pConfig, pDecoder); +} +#endif /* ma_dr_wav_h */ /* FLAC */ -#ifdef dr_flac_h +#ifdef ma_dr_flac_h #define MA_HAS_FLAC typedef struct @@ -61767,7 +61653,7 @@ typedef struct void* pReadSeekTellUserData; ma_format format; /* Can be f32, s16 or s32. */ #if !defined(MA_NO_FLAC) - drflac* dr; + ma_dr_flac* dr; #endif } ma_flac; @@ -61821,25 +61707,6 @@ static ma_data_source_vtable g_ma_flac_ds_vtable = #if !defined(MA_NO_FLAC) -static drflac_allocation_callbacks drflac_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks) -{ - drflac_allocation_callbacks callbacks; - - if (pAllocationCallbacks != NULL) { - callbacks.onMalloc = pAllocationCallbacks->onMalloc; - callbacks.onRealloc = pAllocationCallbacks->onRealloc; - callbacks.onFree = pAllocationCallbacks->onFree; - callbacks.pUserData = pAllocationCallbacks->pUserData; - } else { - callbacks.onMalloc = ma__malloc_default; - callbacks.onRealloc = ma__realloc_default; - callbacks.onFree = ma__free_default; - callbacks.pUserData = NULL; - } - - return callbacks; -} - static size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) { ma_flac* pFlac = (ma_flac*)pUserData; @@ -61854,7 +61721,7 @@ static size_t ma_flac_dr_callback__read(void* pUserData, void* pBufferOut, size_ return bytesRead; } -static drflac_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, drflac_seek_origin origin) +static ma_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, ma_dr_flac_seek_origin origin) { ma_flac* pFlac = (ma_flac*)pUserData; ma_result result; @@ -61863,7 +61730,7 @@ static drflac_bool32 ma_flac_dr_callback__seek(void* pUserData, int offset, drfl MA_ASSERT(pFlac != NULL); maSeekOrigin = ma_seek_origin_start; - if (origin == drflac_seek_origin_current) { + if (origin == ma_dr_flac_seek_origin_current) { maSeekOrigin = ma_seek_origin_current; } @@ -61925,9 +61792,7 @@ MA_API ma_result ma_flac_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_ #if !defined(MA_NO_FLAC) { - drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - - pFlac->dr = drflac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, &flacAllocationCallbacks); + pFlac->dr = ma_dr_flac_open(ma_flac_dr_callback__read, ma_flac_dr_callback__seek, pFlac, pAllocationCallbacks); if (pFlac->dr == NULL) { return MA_INVALID_FILE; } @@ -61954,9 +61819,7 @@ MA_API ma_result ma_flac_init_file(const char* pFilePath, const ma_decoding_back #if !defined(MA_NO_FLAC) { - drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - - pFlac->dr = drflac_open_file(pFilePath, &flacAllocationCallbacks); + pFlac->dr = ma_dr_flac_open_file(pFilePath, pAllocationCallbacks); if (pFlac->dr == NULL) { return MA_INVALID_FILE; } @@ -61984,9 +61847,7 @@ MA_API ma_result ma_flac_init_file_w(const wchar_t* pFilePath, const ma_decoding #if !defined(MA_NO_FLAC) { - drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - - pFlac->dr = drflac_open_file_w(pFilePath, &flacAllocationCallbacks); + pFlac->dr = ma_dr_flac_open_file_w(pFilePath, pAllocationCallbacks); if (pFlac->dr == NULL) { return MA_INVALID_FILE; } @@ -62014,9 +61875,7 @@ MA_API ma_result ma_flac_init_memory(const void* pData, size_t dataSize, const m #if !defined(MA_NO_FLAC) { - drflac_allocation_callbacks flacAllocationCallbacks = drflac_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - - pFlac->dr = drflac_open_memory(pData, dataSize, &flacAllocationCallbacks); + pFlac->dr = ma_dr_flac_open_memory(pData, dataSize, pAllocationCallbacks); if (pFlac->dr == NULL) { return MA_INVALID_FILE; } @@ -62044,7 +61903,7 @@ MA_API void ma_flac_uninit(ma_flac* pFlac, const ma_allocation_callbacks* pAlloc #if !defined(MA_NO_FLAC) { - drflac_close(pFlac->dr); + ma_dr_flac_close(pFlac->dr); } #else { @@ -62083,17 +61942,17 @@ MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_ui { case ma_format_f32: { - totalFramesRead = drflac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut); + totalFramesRead = ma_dr_flac_read_pcm_frames_f32(pFlac->dr, frameCount, (float*)pFramesOut); } break; case ma_format_s16: { - totalFramesRead = drflac_read_pcm_frames_s16(pFlac->dr, frameCount, (drflac_int16*)pFramesOut); + totalFramesRead = ma_dr_flac_read_pcm_frames_s16(pFlac->dr, frameCount, (ma_int16*)pFramesOut); } break; case ma_format_s32: { - totalFramesRead = drflac_read_pcm_frames_s32(pFlac->dr, frameCount, (drflac_int32*)pFramesOut); + totalFramesRead = ma_dr_flac_read_pcm_frames_s32(pFlac->dr, frameCount, (ma_int32*)pFramesOut); } break; case ma_format_u8: @@ -62105,7 +61964,7 @@ MA_API ma_result ma_flac_read_pcm_frames(ma_flac* pFlac, void* pFramesOut, ma_ui }; } - /* In the future we'll update dr_flac to return MA_AT_END for us. */ + /* In the future we'll update ma_dr_flac to return MA_AT_END for us. */ if (totalFramesRead == 0) { result = MA_AT_END; } @@ -62142,10 +62001,10 @@ MA_API ma_result ma_flac_seek_to_pcm_frame(ma_flac* pFlac, ma_uint64 frameIndex) #if !defined(MA_NO_FLAC) { - drflac_bool32 flacResult; + ma_bool32 flacResult; - flacResult = drflac_seek_to_pcm_frame(pFlac->dr, frameIndex); - if (flacResult != DRFLAC_TRUE) { + flacResult = ma_dr_flac_seek_to_pcm_frame(pFlac->dr, frameIndex); + if (flacResult != MA_TRUE) { return MA_ERROR; } @@ -62384,12 +62243,27 @@ static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_flac = static ma_result ma_decoder_init_flac__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder); + return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_flac, NULL, pConfig, pDecoder); } -#endif /* dr_flac_h */ + +static ma_result ma_decoder_init_flac_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_flac_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_flac, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_flac_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_flac, NULL, pData, dataSize, pConfig, pDecoder); +} +#endif /* ma_dr_flac_h */ /* MP3 */ -#ifdef dr_mp3_h +#ifdef ma_dr_mp3_h #define MA_HAS_MP3 typedef struct @@ -62401,9 +62275,9 @@ typedef struct void* pReadSeekTellUserData; ma_format format; /* Can be f32 or s16. */ #if !defined(MA_NO_MP3) - drmp3 dr; - drmp3_uint32 seekPointCount; - drmp3_seek_point* pSeekPoints; /* Only used if seek table generation is used. */ + ma_dr_mp3 dr; + ma_uint32 seekPointCount; + ma_dr_mp3_seek_point* pSeekPoints; /* Only used if seek table generation is used. */ #endif } ma_mp3; @@ -62457,25 +62331,6 @@ static ma_data_source_vtable g_ma_mp3_ds_vtable = #if !defined(MA_NO_MP3) -static drmp3_allocation_callbacks drmp3_allocation_callbacks_from_miniaudio(const ma_allocation_callbacks* pAllocationCallbacks) -{ - drmp3_allocation_callbacks callbacks; - - if (pAllocationCallbacks != NULL) { - callbacks.onMalloc = pAllocationCallbacks->onMalloc; - callbacks.onRealloc = pAllocationCallbacks->onRealloc; - callbacks.onFree = pAllocationCallbacks->onFree; - callbacks.pUserData = pAllocationCallbacks->pUserData; - } else { - callbacks.onMalloc = ma__malloc_default; - callbacks.onRealloc = ma__realloc_default; - callbacks.onFree = ma__free_default; - callbacks.pUserData = NULL; - } - - return callbacks; -} - static size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t bytesToRead) { ma_mp3* pMP3 = (ma_mp3*)pUserData; @@ -62490,7 +62345,7 @@ static size_t ma_mp3_dr_callback__read(void* pUserData, void* pBufferOut, size_t return bytesRead; } -static drmp3_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, drmp3_seek_origin origin) +static ma_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) { ma_mp3* pMP3 = (ma_mp3*)pUserData; ma_result result; @@ -62499,7 +62354,7 @@ static drmp3_bool32 ma_mp3_dr_callback__seek(void* pUserData, int offset, drmp3_ MA_ASSERT(pMP3 != NULL); maSeekOrigin = ma_seek_origin_start; - if (origin == drmp3_seek_origin_current) { + if (origin == ma_dr_mp3_seek_origin_current) { maSeekOrigin = ma_seek_origin_current; } @@ -62543,28 +62398,28 @@ static ma_result ma_mp3_init_internal(const ma_decoding_backend_config* pConfig, static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) { - drmp3_bool32 mp3Result; - drmp3_uint32 seekPointCount = 0; - drmp3_seek_point* pSeekPoints = NULL; + ma_bool32 mp3Result; + ma_uint32 seekPointCount = 0; + ma_dr_mp3_seek_point* pSeekPoints = NULL; MA_ASSERT(pMP3 != NULL); MA_ASSERT(pConfig != NULL); seekPointCount = pConfig->seekPointCount; if (seekPointCount > 0) { - pSeekPoints = (drmp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks); + pSeekPoints = (ma_dr_mp3_seek_point*)ma_malloc(sizeof(*pMP3->pSeekPoints) * seekPointCount, pAllocationCallbacks); if (pSeekPoints == NULL) { return MA_OUT_OF_MEMORY; } } - mp3Result = drmp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints); + mp3Result = ma_dr_mp3_calculate_seek_points(&pMP3->dr, &seekPointCount, pSeekPoints); if (mp3Result != MA_TRUE) { ma_free(pSeekPoints, pAllocationCallbacks); return MA_ERROR; } - mp3Result = drmp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints); + mp3Result = ma_dr_mp3_bind_seek_table(&pMP3->dr, seekPointCount, pSeekPoints); if (mp3Result != MA_TRUE) { ma_free(pSeekPoints, pAllocationCallbacks); return MA_ERROR; @@ -62576,6 +62431,18 @@ static ma_result ma_mp3_generate_seek_table(ma_mp3* pMP3, const ma_decoding_back return MA_SUCCESS; } +static ma_result ma_mp3_post_init(ma_mp3* pMP3, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks) +{ + ma_result result; + + result = ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); + if (result != MA_SUCCESS) { + return result; + } + + return MA_SUCCESS; +} + MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_mp3* pMP3) { ma_result result; @@ -62596,15 +62463,14 @@ MA_API ma_result ma_mp3_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_p #if !defined(MA_NO_MP3) { - drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drmp3_bool32 mp3Result; + ma_bool32 mp3Result; - mp3Result = drmp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, &mp3AllocationCallbacks); + mp3Result = ma_dr_mp3_init(&pMP3->dr, ma_mp3_dr_callback__read, ma_mp3_dr_callback__seek, pMP3, pAllocationCallbacks); if (mp3Result != MA_TRUE) { return MA_INVALID_FILE; } - ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); + ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); return MA_SUCCESS; } @@ -62628,15 +62494,14 @@ MA_API ma_result ma_mp3_init_file(const char* pFilePath, const ma_decoding_backe #if !defined(MA_NO_MP3) { - drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drmp3_bool32 mp3Result; + ma_bool32 mp3Result; - mp3Result = drmp3_init_file(&pMP3->dr, pFilePath, &mp3AllocationCallbacks); + mp3Result = ma_dr_mp3_init_file(&pMP3->dr, pFilePath, pAllocationCallbacks); if (mp3Result != MA_TRUE) { return MA_INVALID_FILE; } - ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); + ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); return MA_SUCCESS; } @@ -62661,15 +62526,14 @@ MA_API ma_result ma_mp3_init_file_w(const wchar_t* pFilePath, const ma_decoding_ #if !defined(MA_NO_MP3) { - drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drmp3_bool32 mp3Result; + ma_bool32 mp3Result; - mp3Result = drmp3_init_file_w(&pMP3->dr, pFilePath, &mp3AllocationCallbacks); + mp3Result = ma_dr_mp3_init_file_w(&pMP3->dr, pFilePath, pAllocationCallbacks); if (mp3Result != MA_TRUE) { return MA_INVALID_FILE; } - ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); + ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); return MA_SUCCESS; } @@ -62694,15 +62558,14 @@ MA_API ma_result ma_mp3_init_memory(const void* pData, size_t dataSize, const ma #if !defined(MA_NO_MP3) { - drmp3_allocation_callbacks mp3AllocationCallbacks = drmp3_allocation_callbacks_from_miniaudio(pAllocationCallbacks); - drmp3_bool32 mp3Result; + ma_bool32 mp3Result; - mp3Result = drmp3_init_memory(&pMP3->dr, pData, dataSize, &mp3AllocationCallbacks); + mp3Result = ma_dr_mp3_init_memory(&pMP3->dr, pData, dataSize, pAllocationCallbacks); if (mp3Result != MA_TRUE) { return MA_INVALID_FILE; } - ma_mp3_generate_seek_table(pMP3, pConfig, pAllocationCallbacks); + ma_mp3_post_init(pMP3, pConfig, pAllocationCallbacks); return MA_SUCCESS; } @@ -62725,7 +62588,7 @@ MA_API void ma_mp3_uninit(ma_mp3* pMP3, const ma_allocation_callbacks* pAllocati #if !defined(MA_NO_MP3) { - drmp3_uninit(&pMP3->dr); + ma_dr_mp3_uninit(&pMP3->dr); } #else { @@ -62767,12 +62630,12 @@ MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint6 { case ma_format_f32: { - totalFramesRead = drmp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut); + totalFramesRead = ma_dr_mp3_read_pcm_frames_f32(&pMP3->dr, frameCount, (float*)pFramesOut); } break; case ma_format_s16: { - totalFramesRead = drmp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (drmp3_int16*)pFramesOut); + totalFramesRead = ma_dr_mp3_read_pcm_frames_s16(&pMP3->dr, frameCount, (ma_int16*)pFramesOut); } break; case ma_format_u8: @@ -62785,7 +62648,7 @@ MA_API ma_result ma_mp3_read_pcm_frames(ma_mp3* pMP3, void* pFramesOut, ma_uint6 }; } - /* In the future we'll update dr_mp3 to return MA_AT_END for us. */ + /* In the future we'll update ma_dr_mp3 to return MA_AT_END for us. */ if (totalFramesRead == 0) { result = MA_AT_END; } @@ -62818,10 +62681,10 @@ MA_API ma_result ma_mp3_seek_to_pcm_frame(ma_mp3* pMP3, ma_uint64 frameIndex) #if !defined(MA_NO_MP3) { - drmp3_bool32 mp3Result; + ma_bool32 mp3Result; - mp3Result = drmp3_seek_to_pcm_frame(&pMP3->dr, frameIndex); - if (mp3Result != DRMP3_TRUE) { + mp3Result = ma_dr_mp3_seek_to_pcm_frame(&pMP3->dr, frameIndex); + if (mp3Result != MA_TRUE) { return MA_ERROR; } @@ -62929,7 +62792,7 @@ MA_API ma_result ma_mp3_get_length_in_pcm_frames(ma_mp3* pMP3, ma_uint64* pLengt #if !defined(MA_NO_MP3) { - *pLength = drmp3_get_pcm_frame_count(&pMP3->dr); + *pLength = ma_dr_mp3_get_pcm_frame_count(&pMP3->dr); return MA_SUCCESS; } @@ -63060,9 +62923,24 @@ static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_mp3 = static ma_result ma_decoder_init_mp3__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder); + return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pConfig, pDecoder); } -#endif /* dr_mp3_h */ + +static ma_result ma_decoder_init_mp3_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_mp3_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_mp3_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_mp3, NULL, pData, dataSize, pConfig, pDecoder); +} +#endif /* ma_dr_mp3_h */ /* Vorbis */ #ifdef STB_VORBIS_INCLUDE_STB_VORBIS_H @@ -63642,8 +63520,6 @@ MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 } result = ma_stbvorbis_read_pcm_frames(pVorbis, buffer, framesToRead, &framesRead); - pVorbis->cursor += framesRead; - if (result != MA_SUCCESS) { return result; } @@ -63879,7 +63755,22 @@ static ma_decoding_backend_vtable g_ma_decoding_backend_vtable_stbvorbis = static ma_result ma_decoder_init_vorbis__internal(const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - return ma_decoder_init_from_vtable(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder); + return ma_decoder_init_from_vtable__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_vorbis_from_file__internal(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_vorbis_from_file_w__internal(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_file_w__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pFilePath, pConfig, pDecoder); +} + +static ma_result ma_decoder_init_vorbis_from_memory__internal(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + return ma_decoder_init_from_memory__internal(&g_ma_decoding_backend_vtable_stbvorbis, NULL, pData, dataSize, pConfig, pDecoder); } #endif /* STB_VORBIS_INCLUDE_STB_VORBIS_H */ @@ -63946,10 +63837,6 @@ static ma_result ma_decoder__preinit(ma_decoder_read_proc onRead, ma_decoder_see MA_ZERO_OBJECT(pDecoder); - if (onRead == NULL || onSeek == NULL) { - return MA_INVALID_ARGS; - } - dataSourceConfig = ma_data_source_config_init(); dataSourceConfig.vtable = &g_ma_decoder_data_source_vtable; @@ -64193,7 +64080,7 @@ static ma_result ma_decoder__on_tell_memory(ma_decoder* pDecoder, ma_int64* pCur return MA_SUCCESS; } -static ma_result ma_decoder__preinit_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +static ma_result ma_decoder__preinit_memory_wrapper(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { ma_result result = ma_decoder__preinit(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, ma_decoder__on_tell_memory, NULL, pConfig, pDecoder); if (result != MA_SUCCESS) { @@ -64214,17 +64101,121 @@ static ma_result ma_decoder__preinit_memory(const void* pData, size_t dataSize, MA_API ma_result ma_decoder_init_memory(const void* pData, size_t dataSize, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - ma_decoder_config config; ma_result result; + ma_decoder_config config; - config = ma_decoder_config_init_copy(pConfig); /* Make sure the config is not NULL. */ + config = ma_decoder_config_init_copy(pConfig); - result = ma_decoder__preinit_memory(pData, dataSize, &config, pDecoder); + result = ma_decoder__preinit(NULL, NULL, NULL, NULL, &config, pDecoder); if (result != MA_SUCCESS) { return result; } - return ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder); + if (pData == NULL || dataSize == 0) { + return MA_INVALID_ARGS; + } + + /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ + result = MA_NO_BACKEND; + + if (config.encodingFormat != ma_encoding_format_unknown) { + #ifdef MA_HAS_WAV + if (config.encodingFormat == ma_encoding_format_wav) { + result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (config.encodingFormat == ma_encoding_format_flac) { + result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (config.encodingFormat == ma_encoding_format_mp3) { + result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (config.encodingFormat == ma_encoding_format_vorbis) { + result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + } + + if (result != MA_SUCCESS) { + /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ + + /* + We use trial and error to open a decoder. We prioritize custom decoders so that if they + implement the same encoding format they take priority over the built-in decoders. + */ + result = ma_decoder_init_custom_from_memory__internal(pData, dataSize, &config, pDecoder); + + /* + If we get to this point and we still haven't found a decoder, and the caller has requested a + specific encoding format, there's no hope for it. Abort. + */ + if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { + return MA_NO_BACKEND; + } + + /* Use trial and error for stock decoders. */ + if (result != MA_SUCCESS) { + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS) { + result = ma_decoder_init_wav_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS) { + result = ma_decoder_init_flac_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS) { + result = ma_decoder_init_mp3_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS) { + result = ma_decoder_init_vorbis_from_memory__internal(pData, dataSize, &config, pDecoder); + } + #endif + } + } + + /* + If at this point we still haven't successfully initialized the decoder it most likely means + the backend doesn't have an implementation for loading from a file path. We'll try using + miniaudio's built-in file IO for loading file. + */ + if (result == MA_SUCCESS) { + /* Initialization was successful. Finish up. */ + result = ma_decoder__postinit(&config, pDecoder); + if (result != MA_SUCCESS) { + /* + The backend was initialized successfully, but for some reason post-initialization failed. This is most likely + due to an out of memory error. We're going to abort with an error here and not try to recover. + */ + if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { + pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); + } + + return result; + } + } else { + /* Probably no implementation for loading from a block of memory. Use miniaudio's abstraction instead. */ + result = ma_decoder__preinit_memory_wrapper(pData, dataSize, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_decoder_init__internal(ma_decoder__on_read_memory, ma_decoder__on_seek_memory, NULL, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + } + + return MA_SUCCESS; } @@ -64691,14 +64682,305 @@ MA_API ma_result ma_decoder_init_vfs_w(ma_vfs* pVFS, const wchar_t* pFilePath, c return MA_SUCCESS; } + +static ma_result ma_decoder__preinit_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + + result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + if (pFilePath == NULL || pFilePath[0] == '\0') { + return MA_INVALID_ARGS; + } + + return MA_SUCCESS; +} + MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - return ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder); + ma_result result; + ma_decoder_config config; + + config = ma_decoder_config_init_copy(pConfig); + result = ma_decoder__preinit_file(pFilePath, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ + result = MA_NO_BACKEND; + + if (config.encodingFormat != ma_encoding_format_unknown) { + #ifdef MA_HAS_WAV + if (config.encodingFormat == ma_encoding_format_wav) { + result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (config.encodingFormat == ma_encoding_format_flac) { + result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (config.encodingFormat == ma_encoding_format_mp3) { + result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (config.encodingFormat == ma_encoding_format_vorbis) { + result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + } + + if (result != MA_SUCCESS) { + /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ + + /* + We use trial and error to open a decoder. We prioritize custom decoders so that if they + implement the same encoding format they take priority over the built-in decoders. + */ + result = ma_decoder_init_custom_from_file__internal(pFilePath, &config, pDecoder); + + /* + If we get to this point and we still haven't found a decoder, and the caller has requested a + specific encoding format, there's no hope for it. Abort. + */ + if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { + return MA_NO_BACKEND; + } + + /* First try loading based on the file extension so we don't waste time opening and closing files. */ + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "wav")) { + result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "flac")) { + result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "mp3")) { + result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS && ma_path_extension_equal(pFilePath, "ogg")) { + result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + + /* + If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we + need only iterate over our stock decoders. + */ + if (result != MA_SUCCESS) { + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS) { + result = ma_decoder_init_wav_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS) { + result = ma_decoder_init_flac_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS) { + result = ma_decoder_init_mp3_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS) { + result = ma_decoder_init_vorbis_from_file__internal(pFilePath, &config, pDecoder); + } + #endif + } + } + + /* + If at this point we still haven't successfully initialized the decoder it most likely means + the backend doesn't have an implementation for loading from a file path. We'll try using + miniaudio's built-in file IO for loading file. + */ + if (result == MA_SUCCESS) { + /* Initialization was successful. Finish up. */ + result = ma_decoder__postinit(&config, pDecoder); + if (result != MA_SUCCESS) { + /* + The backend was initialized successfully, but for some reason post-initialization failed. This is most likely + due to an out of memory error. We're going to abort with an error here and not try to recover. + */ + if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { + pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); + } + + return result; + } + } else { + /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ + result = ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return MA_SUCCESS; + } + } + + return MA_SUCCESS; +} + +static ma_result ma_decoder__preinit_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) +{ + ma_result result; + + result = ma_decoder__preinit(NULL, NULL, NULL, NULL, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + if (pFilePath == NULL || pFilePath[0] == '\0') { + return MA_INVALID_ARGS; + } + + return MA_SUCCESS; } MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decoder_config* pConfig, ma_decoder* pDecoder) { - return ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder); + ma_result result; + ma_decoder_config config; + + config = ma_decoder_config_init_copy(pConfig); + result = ma_decoder__preinit_file_w(pFilePath, &config, pDecoder); + if (result != MA_SUCCESS) { + return result; + } + + /* If the backend has support for loading from a file path we'll want to use that. If that all fails we'll fall back to the VFS path. */ + result = MA_NO_BACKEND; + + if (config.encodingFormat != ma_encoding_format_unknown) { + #ifdef MA_HAS_WAV + if (config.encodingFormat == ma_encoding_format_wav) { + result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (config.encodingFormat == ma_encoding_format_flac) { + result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (config.encodingFormat == ma_encoding_format_mp3) { + result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (config.encodingFormat == ma_encoding_format_vorbis) { + result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + } + + if (result != MA_SUCCESS) { + /* Getting here means we weren't able to initialize a decoder of a specific encoding format. */ + + /* + We use trial and error to open a decoder. We prioritize custom decoders so that if they + implement the same encoding format they take priority over the built-in decoders. + */ + result = ma_decoder_init_custom_from_file_w__internal(pFilePath, &config, pDecoder); + + /* + If we get to this point and we still haven't found a decoder, and the caller has requested a + specific encoding format, there's no hope for it. Abort. + */ + if (result != MA_SUCCESS && config.encodingFormat != ma_encoding_format_unknown) { + return MA_NO_BACKEND; + } + + /* First try loading based on the file extension so we don't waste time opening and closing files. */ + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"wav")) { + result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"flac")) { + result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"mp3")) { + result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS && ma_path_extension_equal_w(pFilePath, L"ogg")) { + result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + + /* + If we still haven't got a result just use trial and error. Custom decoders have already been attempted, so here we + need only iterate over our stock decoders. + */ + if (result != MA_SUCCESS) { + #ifdef MA_HAS_WAV + if (result != MA_SUCCESS) { + result = ma_decoder_init_wav_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_FLAC + if (result != MA_SUCCESS) { + result = ma_decoder_init_flac_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_MP3 + if (result != MA_SUCCESS) { + result = ma_decoder_init_mp3_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + #ifdef MA_HAS_VORBIS + if (result != MA_SUCCESS) { + result = ma_decoder_init_vorbis_from_file_w__internal(pFilePath, &config, pDecoder); + } + #endif + } + } + + /* + If at this point we still haven't successfully initialized the decoder it most likely means + the backend doesn't have an implementation for loading from a file path. We'll try using + miniaudio's built-in file IO for loading file. + */ + if (result == MA_SUCCESS) { + /* Initialization was successful. Finish up. */ + result = ma_decoder__postinit(&config, pDecoder); + if (result != MA_SUCCESS) { + /* + The backend was initialized successfully, but for some reason post-initialization failed. This is most likely + due to an out of memory error. We're going to abort with an error here and not try to recover. + */ + if (pDecoder->pBackendVTable != NULL && pDecoder->pBackendVTable->onUninit != NULL) { + pDecoder->pBackendVTable->onUninit(pDecoder->pBackendUserData, &pDecoder->pBackend, &pDecoder->allocationCallbacks); + } + + return result; + } + } else { + /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ + result = ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder); + if (result != MA_SUCCESS) { + return MA_SUCCESS; + } + } + + return MA_SUCCESS; } MA_API ma_result ma_decoder_uninit(ma_decoder* pDecoder) @@ -65192,42 +65474,42 @@ static size_t ma_encoder__internal_on_write_wav(void* pUserData, const void* pDa return bytesWritten; } -static drwav_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, drwav_seek_origin origin) +static ma_bool32 ma_encoder__internal_on_seek_wav(void* pUserData, int offset, ma_dr_wav_seek_origin origin) { ma_encoder* pEncoder = (ma_encoder*)pUserData; ma_result result; MA_ASSERT(pEncoder != NULL); - result = pEncoder->onSeek(pEncoder, offset, (origin == drwav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); + result = pEncoder->onSeek(pEncoder, offset, (origin == ma_dr_wav_seek_origin_start) ? ma_seek_origin_start : ma_seek_origin_current); if (result != MA_SUCCESS) { - return DRWAV_FALSE; + return MA_FALSE; } else { - return DRWAV_TRUE; + return MA_TRUE; } } static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder) { - drwav_data_format wavFormat; - drwav_allocation_callbacks allocationCallbacks; - drwav* pWav; + ma_dr_wav_data_format wavFormat; + ma_allocation_callbacks allocationCallbacks; + ma_dr_wav* pWav; MA_ASSERT(pEncoder != NULL); - pWav = (drwav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks); + pWav = (ma_dr_wav*)ma_malloc(sizeof(*pWav), &pEncoder->config.allocationCallbacks); if (pWav == NULL) { return MA_OUT_OF_MEMORY; } - wavFormat.container = drwav_container_riff; + wavFormat.container = ma_dr_wav_container_riff; wavFormat.channels = pEncoder->config.channels; wavFormat.sampleRate = pEncoder->config.sampleRate; wavFormat.bitsPerSample = ma_get_bytes_per_sample(pEncoder->config.format) * 8; if (pEncoder->config.format == ma_format_f32) { - wavFormat.format = DR_WAVE_FORMAT_IEEE_FLOAT; + wavFormat.format = MA_DR_WAVE_FORMAT_IEEE_FLOAT; } else { - wavFormat.format = DR_WAVE_FORMAT_PCM; + wavFormat.format = MA_DR_WAVE_FORMAT_PCM; } allocationCallbacks.pUserData = pEncoder->config.allocationCallbacks.pUserData; @@ -65235,7 +65517,7 @@ static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder) allocationCallbacks.onRealloc = pEncoder->config.allocationCallbacks.onRealloc; allocationCallbacks.onFree = pEncoder->config.allocationCallbacks.onFree; - if (!drwav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) { + if (!ma_dr_wav_init_write(pWav, &wavFormat, ma_encoder__internal_on_write_wav, ma_encoder__internal_on_seek_wav, pEncoder, &allocationCallbacks)) { return MA_ERROR; } @@ -65246,28 +65528,28 @@ static ma_result ma_encoder__on_init_wav(ma_encoder* pEncoder) static void ma_encoder__on_uninit_wav(ma_encoder* pEncoder) { - drwav* pWav; + ma_dr_wav* pWav; MA_ASSERT(pEncoder != NULL); - pWav = (drwav*)pEncoder->pInternalEncoder; + pWav = (ma_dr_wav*)pEncoder->pInternalEncoder; MA_ASSERT(pWav != NULL); - drwav_uninit(pWav); + ma_dr_wav_uninit(pWav); ma_free(pWav, &pEncoder->config.allocationCallbacks); } static ma_result ma_encoder__on_write_pcm_frames_wav(ma_encoder* pEncoder, const void* pFramesIn, ma_uint64 frameCount, ma_uint64* pFramesWritten) { - drwav* pWav; + ma_dr_wav* pWav; ma_uint64 framesWritten; MA_ASSERT(pEncoder != NULL); - pWav = (drwav*)pEncoder->pInternalEncoder; + pWav = (ma_dr_wav*)pEncoder->pInternalEncoder; MA_ASSERT(pWav != NULL); - framesWritten = drwav_write_pcm_frames(pWav, frameCount, pFramesIn); + framesWritten = ma_dr_wav_write_pcm_frames(pWav, frameCount, pFramesIn); if (pFramesWritten != NULL) { *pFramesWritten = framesWritten; @@ -65645,12 +65927,12 @@ static ma_int16 ma_waveform_sine_s16(double time, double amplitude) return ma_pcm_sample_f32_to_s16(ma_waveform_sine_f32(time, amplitude)); } -static float ma_waveform_square_f32(double time, double amplitude) +static float ma_waveform_square_f32(double time, double dutyCycle, double amplitude) { double f = time - (ma_int64)time; double r; - if (f < 0.5) { + if (f < dutyCycle) { r = amplitude; } else { r = -amplitude; @@ -65659,9 +65941,9 @@ static float ma_waveform_square_f32(double time, double amplitude) return (float)r; } -static ma_int16 ma_waveform_square_s16(double time, double amplitude) +static ma_int16 ma_waveform_square_s16(double time, double dutyCycle, double amplitude) { - return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, amplitude)); + return ma_pcm_sample_f32_to_s16(ma_waveform_square_f32(time, dutyCycle, amplitude)); } static float ma_waveform_triangle_f32(double time, double amplitude) @@ -65736,7 +66018,7 @@ static void ma_waveform_read_pcm_frames__sine(ma_waveform* pWaveform, void* pFra } } -static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, void* pFramesOut, ma_uint64 frameCount) +static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, double dutyCycle, void* pFramesOut, ma_uint64 frameCount) { ma_uint64 iFrame; ma_uint64 iChannel; @@ -65749,7 +66031,7 @@ static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, void* pF if (pWaveform->config.format == ma_format_f32) { float* pFramesOutF32 = (float*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.amplitude); + float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { @@ -65759,7 +66041,7 @@ static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, void* pF } else if (pWaveform->config.format == ma_format_s16) { ma_int16* pFramesOutS16 = (ma_int16*)pFramesOut; for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - ma_int16 s = ma_waveform_square_s16(pWaveform->time, pWaveform->config.amplitude); + ma_int16 s = ma_waveform_square_s16(pWaveform->time, dutyCycle, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { @@ -65768,7 +66050,7 @@ static void ma_waveform_read_pcm_frames__square(ma_waveform* pWaveform, void* pF } } else { for (iFrame = 0; iFrame < frameCount; iFrame += 1) { - float s = ma_waveform_square_f32(pWaveform->time, pWaveform->config.amplitude); + float s = ma_waveform_square_f32(pWaveform->time, dutyCycle, pWaveform->config.amplitude); pWaveform->time += pWaveform->advance; for (iChannel = 0; iChannel < pWaveform->config.channels; iChannel += 1) { @@ -65886,7 +66168,7 @@ MA_API ma_result ma_waveform_read_pcm_frames(ma_waveform* pWaveform, void* pFram case ma_waveform_type_square: { - ma_waveform_read_pcm_frames__square(pWaveform, pFramesOut, frameCount); + ma_waveform_read_pcm_frames__square(pWaveform, 0.5, pFramesOut, frameCount); } break; case ma_waveform_type_triangle: @@ -65923,6 +66205,142 @@ MA_API ma_result ma_waveform_seek_to_pcm_frame(ma_waveform* pWaveform, ma_uint64 return MA_SUCCESS; } +MA_API ma_pulsewave_config ma_pulsewave_config_init(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, double dutyCycle, double amplitude, double frequency) +{ + ma_pulsewave_config config; + + MA_ZERO_OBJECT(&config); + config.format = format; + config.channels = channels; + config.sampleRate = sampleRate; + config.dutyCycle = dutyCycle; + config.amplitude = amplitude; + config.frequency = frequency; + + return config; +} + +MA_API ma_result ma_pulsewave_init(const ma_pulsewave_config* pConfig, ma_pulsewave* pWaveform) +{ + ma_result result; + ma_waveform_config config; + + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pWaveform); + + config = ma_waveform_config_init( + pConfig->format, + pConfig->channels, + pConfig->sampleRate, + ma_waveform_type_square, + pConfig->amplitude, + pConfig->frequency + ); + + result = ma_waveform_init(&config, &pWaveform->waveform); + ma_pulsewave_set_duty_cycle(pWaveform, pConfig->dutyCycle); + + return result; +} + +MA_API void ma_pulsewave_uninit(ma_pulsewave* pWaveform) +{ + if (pWaveform == NULL) { + return; + } + + ma_waveform_uninit(&pWaveform->waveform); +} + +MA_API ma_result ma_pulsewave_read_pcm_frames(ma_pulsewave* pWaveform, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + if (frameCount == 0) { + return MA_INVALID_ARGS; + } + + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + if (pFramesOut != NULL) { + ma_waveform_read_pcm_frames__square(&pWaveform->waveform, pWaveform->config.dutyCycle, pFramesOut, frameCount); + } else { + pWaveform->waveform.time += pWaveform->waveform.advance * (ma_int64)frameCount; /* Cast to int64 required for VC6. Won't affect anything in practice. */ + } + + if (pFramesRead != NULL) { + *pFramesRead = frameCount; + } + + return MA_SUCCESS; +} + +MA_API ma_result ma_pulsewave_seek_to_pcm_frame(ma_pulsewave* pWaveform, ma_uint64 frameIndex) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + ma_waveform_seek_to_pcm_frame(&pWaveform->waveform, frameIndex); + + return MA_SUCCESS; +} + +MA_API ma_result ma_pulsewave_set_amplitude(ma_pulsewave* pWaveform, double amplitude) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.amplitude = amplitude; + ma_waveform_set_amplitude(&pWaveform->waveform, amplitude); + + return MA_SUCCESS; +} + +MA_API ma_result ma_pulsewave_set_frequency(ma_pulsewave* pWaveform, double frequency) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.frequency = frequency; + ma_waveform_set_frequency(&pWaveform->waveform, frequency); + + return MA_SUCCESS; +} + +MA_API ma_result ma_pulsewave_set_sample_rate(ma_pulsewave* pWaveform, ma_uint32 sampleRate) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.sampleRate = sampleRate; + ma_waveform_set_sample_rate(&pWaveform->waveform, sampleRate); + + return MA_SUCCESS; +} + +MA_API ma_result ma_pulsewave_set_duty_cycle(ma_pulsewave* pWaveform, double dutyCycle) +{ + if (pWaveform == NULL) { + return MA_INVALID_ARGS; + } + + pWaveform->config.dutyCycle = dutyCycle; + + return MA_SUCCESS; +} + + MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels, ma_noise_type type, ma_int32 seed, double amplitude) { @@ -66959,12 +67377,12 @@ static ma_result ma_resource_manager_data_buffer_node_remove_by_key(ma_resource_ static ma_resource_manager_data_supply_type ma_resource_manager_data_buffer_node_get_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode) { - return (ma_resource_manager_data_supply_type)c89atomic_load_i32(&pDataBufferNode->data.type); + return (ma_resource_manager_data_supply_type)ma_atomic_load_i32(&pDataBufferNode->data.type); } static void ma_resource_manager_data_buffer_node_set_data_supply_type(ma_resource_manager_data_buffer_node* pDataBufferNode, ma_resource_manager_data_supply_type supplyType) { - c89atomic_exchange_i32(&pDataBufferNode->data.type, supplyType); + ma_atomic_exchange_i32(&pDataBufferNode->data.type, supplyType); } static ma_result ma_resource_manager_data_buffer_node_increment_ref(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, ma_uint32* pNewRefCount) @@ -66976,7 +67394,7 @@ static ma_result ma_resource_manager_data_buffer_node_increment_ref(ma_resource_ (void)pResourceManager; - refCount = c89atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1; + refCount = ma_atomic_fetch_add_32(&pDataBufferNode->refCount, 1) + 1; if (pNewRefCount != NULL) { *pNewRefCount = refCount; @@ -66994,7 +67412,7 @@ static ma_result ma_resource_manager_data_buffer_node_decrement_ref(ma_resource_ (void)pResourceManager; - refCount = c89atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1; + refCount = ma_atomic_fetch_sub_32(&pDataBufferNode->refCount, 1) - 1; if (pNewRefCount != NULL) { *pNewRefCount = refCount; @@ -67033,7 +67451,7 @@ static ma_result ma_resource_manager_data_buffer_node_result(const ma_resource_m { MA_ASSERT(pDataBufferNode != NULL); - return (ma_result)c89atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */ + return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBufferNode->result); /* Need a naughty const-cast here. */ } @@ -67621,7 +68039,7 @@ static ma_result ma_resource_manager_data_buffer_uninit_connector(ma_resource_ma static ma_uint32 ma_resource_manager_data_buffer_node_next_execution_order(ma_resource_manager_data_buffer_node* pDataBufferNode) { MA_ASSERT(pDataBufferNode != NULL); - return c89atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1); + return ma_atomic_fetch_add_32(&pDataBufferNode->executionCounter, 1); } static ma_result ma_resource_manager_data_buffer_node_init_supply_encoded(ma_resource_manager* pResourceManager, ma_resource_manager_data_buffer_node* pDataBufferNode, const char* pFilePath, const wchar_t* pFilePathW) @@ -68092,7 +68510,7 @@ static ma_result ma_resource_manager_data_buffer_node_acquire(ma_resource_manage } /* Getting here means we were successful. Make sure the status of the node is updated accordingly. */ - c89atomic_exchange_i32(&pDataBufferNode->result, result); + ma_atomic_exchange_i32(&pDataBufferNode->result, result); } else { /* Loading asynchronously. We may need to wait for initialization. */ if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { @@ -68197,7 +68615,7 @@ stage2: ma_job job; /* We need to mark the node as unavailable for the sake of the resource manager worker threads. */ - c89atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE); + ma_atomic_exchange_i32(&pDataBufferNode->result, MA_UNAVAILABLE); job = ma_job_init(MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER_NODE); job.order = ma_resource_manager_data_buffer_node_next_execution_order(pDataBufferNode); @@ -68236,7 +68654,7 @@ stage2: static ma_uint32 ma_resource_manager_data_buffer_next_execution_order(ma_resource_manager_data_buffer* pDataBuffer) { MA_ASSERT(pDataBuffer != NULL); - return c89atomic_fetch_add_32(&pDataBuffer->executionCounter, 1); + return ma_atomic_fetch_add_32(&pDataBuffer->executionCounter, 1); } static ma_result ma_resource_manager_data_buffer_cb__read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) @@ -68269,7 +68687,7 @@ static ma_result ma_resource_manager_data_buffer_cb__set_looping(ma_data_source* ma_resource_manager_data_buffer* pDataBuffer = (ma_resource_manager_data_buffer*)pDataSource; MA_ASSERT(pDataBuffer != NULL); - c89atomic_exchange_32(&pDataBuffer->isLooping, isLooping); + ma_atomic_exchange_32(&pDataBuffer->isLooping, isLooping); /* The looping state needs to be set on the connector as well or else looping won't work when we read audio data. */ ma_data_source_set_looping(ma_resource_manager_data_buffer_get_connector(pDataBuffer), isLooping); @@ -68365,7 +68783,7 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma if (async == MA_FALSE || ma_resource_manager_data_buffer_node_result(pDataBufferNode) == MA_SUCCESS) { /* Loading synchronously or the data has already been fully loaded. We can just initialize the connector from here without a job. */ result = ma_resource_manager_data_buffer_init_connector(pDataBuffer, pConfig, NULL, NULL); - c89atomic_exchange_i32(&pDataBuffer->result, result); + ma_atomic_exchange_i32(&pDataBuffer->result, result); ma_resource_manager_pipeline_notifications_signal_all_notifications(¬ifications); goto done; @@ -68383,7 +68801,7 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma worker thread is aware of it's busy state. If the LOAD_DATA_BUFFER job sees a status other than MA_BUSY, it'll assume an error and fall through to an early exit. */ - c89atomic_exchange_i32(&pDataBuffer->result, MA_BUSY); + ma_atomic_exchange_i32(&pDataBuffer->result, MA_BUSY); /* Acquire fences a second time. These will be released by the async thread. */ ma_resource_manager_pipeline_notifications_acquire_all_fences(¬ifications); @@ -68411,7 +68829,7 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma if (result != MA_SUCCESS) { /* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */ ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER job. %s.\n", ma_result_description(result)); - c89atomic_exchange_i32(&pDataBuffer->result, result); + ma_atomic_exchange_i32(&pDataBuffer->result, result); /* Release the fences after the result has been set on the data buffer. */ ma_resource_manager_pipeline_notifications_release_all_fences(¬ifications); @@ -68540,7 +68958,7 @@ MA_API ma_result ma_resource_manager_data_buffer_uninit(ma_resource_manager_data We need to mark the node as unavailable so we don't try reading from it anymore, but also to let the loading thread know that it needs to abort it's loading procedure. */ - c89atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE); + ma_atomic_exchange_i32(&pDataBuffer->result, MA_UNAVAILABLE); result = ma_resource_manager_inline_notification_init(pDataBuffer->pResourceManager, ¬ification); if (result != MA_SUCCESS) { @@ -68800,7 +69218,7 @@ MA_API ma_result ma_resource_manager_data_buffer_result(const ma_resource_manage return MA_INVALID_ARGS; } - return (ma_result)c89atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */ + return (ma_result)ma_atomic_load_i32((ma_result*)&pDataBuffer->result); /* Need a naughty const-cast here. */ } MA_API ma_result ma_resource_manager_data_buffer_set_looping(ma_resource_manager_data_buffer* pDataBuffer, ma_bool32 isLooping) @@ -68953,19 +69371,19 @@ MA_API ma_result ma_resource_manager_unregister_data_w(ma_resource_manager* pRes static ma_uint32 ma_resource_manager_data_stream_next_execution_order(ma_resource_manager_data_stream* pDataStream) { MA_ASSERT(pDataStream != NULL); - return c89atomic_fetch_add_32(&pDataStream->executionCounter, 1); + return ma_atomic_fetch_add_32(&pDataStream->executionCounter, 1); } static ma_bool32 ma_resource_manager_data_stream_is_decoder_at_end(const ma_resource_manager_data_stream* pDataStream) { MA_ASSERT(pDataStream != NULL); - return c89atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd); + return ma_atomic_load_32((ma_bool32*)&pDataStream->isDecoderAtEnd); } static ma_uint32 ma_resource_manager_data_stream_seek_counter(const ma_resource_manager_data_stream* pDataStream) { MA_ASSERT(pDataStream != NULL); - return c89atomic_load_32((ma_uint32*)&pDataStream->seekCounter); + return ma_atomic_load_32((ma_uint32*)&pDataStream->seekCounter); } @@ -68999,7 +69417,7 @@ static ma_result ma_resource_manager_data_stream_cb__set_looping(ma_data_source* ma_resource_manager_data_stream* pDataStream = (ma_resource_manager_data_stream*)pDataSource; MA_ASSERT(pDataStream != NULL); - c89atomic_exchange_32(&pDataStream->isLooping, isLooping); + ma_atomic_exchange_32(&pDataStream->isLooping, isLooping); return MA_SUCCESS; } @@ -69022,7 +69440,7 @@ static void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_mana absoluteCursor = absoluteCursor % pDataStream->totalLengthInPCMFrames; } - c89atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor); + ma_atomic_exchange_64(&pDataStream->absoluteCursor, absoluteCursor); } MA_API ma_result ma_resource_manager_data_stream_init_ex(ma_resource_manager* pResourceManager, const ma_resource_manager_data_source_config* pConfig, ma_resource_manager_data_stream* pDataStream) @@ -69185,7 +69603,7 @@ MA_API ma_result ma_resource_manager_data_stream_uninit(ma_resource_manager_data } /* The first thing to do is set the result to unavailable. This will prevent future page decoding. */ - c89atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE); + ma_atomic_exchange_i32(&pDataStream->result, MA_UNAVAILABLE); /* We need to post a job to ensure we're not in the middle or decoding or anything. Because the object is owned by the caller, we'll need @@ -69252,11 +69670,11 @@ static void ma_resource_manager_data_stream_fill_page(ma_resource_manager_data_s /* Just read straight from the decoder. It will deal with ranges and looping for us. */ result = ma_data_source_read_pcm_frames(&pDataStream->decoder, pPageData, pageSizeInFrames, &totalFramesReadForThisPage); if (result == MA_AT_END || totalFramesReadForThisPage < pageSizeInFrames) { - c89atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE); + ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_TRUE); } - c89atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage); - c89atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE); + ma_atomic_exchange_32(&pDataStream->pageFrameCount[pageIndex], (ma_uint32)totalFramesReadForThisPage); + ma_atomic_exchange_32(&pDataStream->isPageValid[pageIndex], MA_TRUE); } static void ma_resource_manager_data_stream_fill_pages(ma_resource_manager_data_stream* pDataStream) @@ -69301,14 +69719,14 @@ static ma_result ma_resource_manager_data_stream_map(ma_resource_manager_data_st } /* If the page we're on is invalid it means we've caught up to the job thread. */ - if (c89atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) { + if (ma_atomic_load_32(&pDataStream->isPageValid[pDataStream->currentPageIndex]) == MA_FALSE) { framesAvailable = 0; } else { /* The page we're on is valid so we must have some frames available. We need to make sure that we don't overflow into the next page, even if it's valid. The reason is that the unmap process will only post an update for one page at a time. Keeping mapping tied to page boundaries makes this simpler. */ - ma_uint32 currentPageFrameCount = c89atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]); + ma_uint32 currentPageFrameCount = ma_atomic_load_32(&pDataStream->pageFrameCount[pDataStream->currentPageIndex]); MA_ASSERT(currentPageFrameCount >= pDataStream->relativeCursor); framesAvailable = currentPageFrameCount - pDataStream->relativeCursor; @@ -69360,7 +69778,7 @@ static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_ pageSizeInFrames = ma_resource_manager_data_stream_get_page_size_in_frames(pDataStream); /* The absolute cursor needs to be updated for ma_resource_manager_data_stream_get_cursor_in_pcm_frames(). */ - ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, c89atomic_load_64(&pDataStream->absoluteCursor) + frameCount); + ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, ma_atomic_load_64(&pDataStream->absoluteCursor) + frameCount); /* Here is where we need to check if we need to load a new page, and if so, post a job to load it. */ newRelativeCursor = pDataStream->relativeCursor + (ma_uint32)frameCount; @@ -69376,7 +69794,7 @@ static ma_result ma_resource_manager_data_stream_unmap(ma_resource_manager_data_ job.data.resourceManager.pageDataStream.pageIndex = pDataStream->currentPageIndex; /* The page needs to be marked as invalid so that the public API doesn't try reading from it. */ - c89atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE); + ma_atomic_exchange_32(&pDataStream->isPageValid[pDataStream->currentPageIndex], MA_FALSE); /* Before posting the job we need to make sure we set some state. */ pDataStream->relativeCursor = newRelativeCursor; @@ -69479,15 +69897,15 @@ MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_m } /* If we're not already seeking and we're sitting on the same frame, just make this a no-op. */ - if (c89atomic_load_32(&pDataStream->seekCounter) == 0) { - if (c89atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) { + if (ma_atomic_load_32(&pDataStream->seekCounter) == 0) { + if (ma_atomic_load_64(&pDataStream->absoluteCursor) == frameIndex) { return MA_SUCCESS; } } /* Increment the seek counter first to indicate to read_paged_pcm_frames() and map_paged_pcm_frames() that we are in the middle of a seek and MA_BUSY should be returned. */ - c89atomic_fetch_add_32(&pDataStream->seekCounter, 1); + ma_atomic_fetch_add_32(&pDataStream->seekCounter, 1); /* Update the absolute cursor so that ma_resource_manager_data_stream_get_cursor_in_pcm_frames() returns the new position. */ ma_resource_manager_data_stream_set_absolute_cursor(pDataStream, frameIndex); @@ -69499,11 +69917,11 @@ MA_API ma_result ma_resource_manager_data_stream_seek_to_pcm_frame(ma_resource_m */ pDataStream->relativeCursor = 0; pDataStream->currentPageIndex = 0; - c89atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE); - c89atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE); + ma_atomic_exchange_32(&pDataStream->isPageValid[0], MA_FALSE); + ma_atomic_exchange_32(&pDataStream->isPageValid[1], MA_FALSE); /* Make sure the data stream is not marked as at the end or else if we seek in response to hitting the end, we won't be able to read any more data. */ - c89atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE); + ma_atomic_exchange_32(&pDataStream->isDecoderAtEnd, MA_FALSE); /* The public API is not allowed to touch the internal decoder so we need to use a job to perform the seek. When seeking, the job thread will assume both pages @@ -69579,7 +69997,7 @@ MA_API ma_result ma_resource_manager_data_stream_get_cursor_in_pcm_frames(ma_res return MA_INVALID_OPERATION; } - *pCursor = c89atomic_load_64(&pDataStream->absoluteCursor); + *pCursor = ma_atomic_load_64(&pDataStream->absoluteCursor); return MA_SUCCESS; } @@ -69625,7 +70043,7 @@ MA_API ma_result ma_resource_manager_data_stream_result(const ma_resource_manage return MA_INVALID_ARGS; } - return (ma_result)c89atomic_load_i32(&pDataStream->result); + return (ma_result)ma_atomic_load_i32(&pDataStream->result); } MA_API ma_result ma_resource_manager_data_stream_set_looping(ma_resource_manager_data_stream* pDataStream, ma_bool32 isLooping) @@ -69639,7 +70057,7 @@ MA_API ma_bool32 ma_resource_manager_data_stream_is_looping(const ma_resource_ma return MA_FALSE; } - return c89atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */ + return ma_atomic_load_32((ma_bool32*)&pDataStream->isLooping); /* Naughty const-cast. Value won't change from here in practice (maybe from another thread). */ } MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resource_manager_data_stream* pDataStream, ma_uint64* pAvailableFrames) @@ -69664,10 +70082,10 @@ MA_API ma_result ma_resource_manager_data_stream_get_available_frames(ma_resourc relativeCursor = pDataStream->relativeCursor; availableFrames = 0; - if (c89atomic_load_32(&pDataStream->isPageValid[pageIndex0])) { - availableFrames += c89atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor; - if (c89atomic_load_32(&pDataStream->isPageValid[pageIndex1])) { - availableFrames += c89atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]); + if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex0])) { + availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex0]) - relativeCursor; + if (ma_atomic_load_32(&pDataStream->isPageValid[pageIndex1])) { + availableFrames += ma_atomic_load_32(&pDataStream->pageFrameCount[pageIndex1]); } } @@ -69973,7 +70391,7 @@ static ma_result ma_job_process__resource_manager__load_data_buffer_node(ma_job* MA_ASSERT(pDataBufferNode->isDataOwnedByResourceManager == MA_TRUE); /* The data should always be owned by the resource manager. */ /* The data buffer is not getting deleted, but we may be getting executed out of order. If so, we need to push the job back onto the queue and return. */ - if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) { + if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ } @@ -70084,7 +70502,7 @@ done: immediately deletes it before we've got to this point. In this case, pDataBuffer->result will be MA_UNAVAILABLE, and setting it to MA_SUCCESS or any other error code would cause the buffer to look like it's in a state that it's not. */ - c89atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); + ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); /* At this point initialization is complete and we can signal the notification if any. */ if (pJob->data.resourceManager.loadDataBufferNode.pInitNotification != NULL) { @@ -70105,7 +70523,7 @@ done: } /* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */ - c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); + ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); /* A busy result should be considered successful from the point of view of the job system. */ if (result == MA_BUSY) { @@ -70128,7 +70546,7 @@ static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.freeDataBufferNode.pDataBufferNode; MA_ASSERT(pDataBufferNode != NULL); - if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) { + if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } @@ -70143,7 +70561,7 @@ static ma_result ma_job_process__resource_manager__free_data_buffer_node(ma_job* ma_fence_release(pJob->data.resourceManager.freeDataBufferNode.pDoneFence); } - c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); + ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); return MA_SUCCESS; } @@ -70161,7 +70579,7 @@ static ma_result ma_job_process__resource_manager__page_data_buffer_node(ma_job* pDataBufferNode = (ma_resource_manager_data_buffer_node*)pJob->data.resourceManager.pageDataBufferNode.pDataBufferNode; MA_ASSERT(pDataBufferNode != NULL); - if (pJob->order != c89atomic_load_32(&pDataBufferNode->executionPointer)) { + if (pJob->order != ma_atomic_load_32(&pDataBufferNode->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } @@ -70204,7 +70622,7 @@ done: } /* Make sure we set the result of node in case some error occurred. */ - c89atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); + ma_atomic_compare_and_swap_i32(&pDataBufferNode->result, MA_BUSY, result); /* Signal the notification after setting the result in case the notification callback wants to inspect the result code. */ if (result != MA_BUSY) { @@ -70217,7 +70635,7 @@ done: } } - c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); + ma_atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); return result; } @@ -70241,7 +70659,7 @@ static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob pResourceManager = pDataBuffer->pResourceManager; - if (pJob->order != c89atomic_load_32(&pDataBuffer->executionPointer)) { + if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Attempting to execute out of order. Probably interleaved with a MA_JOB_TYPE_RESOURCE_MANAGER_FREE_DATA_BUFFER job. */ } @@ -70299,7 +70717,7 @@ static ma_result ma_job_process__resource_manager__load_data_buffer(ma_job* pJob done: /* Only move away from a busy code so that we don't trash any existing error codes. */ - c89atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result); + ma_atomic_compare_and_swap_i32(&pDataBuffer->result, MA_BUSY, result); /* Only signal the other threads after the result has been set just for cleanliness sake. */ if (pJob->data.resourceManager.loadDataBuffer.pDoneNotification != NULL) { @@ -70322,7 +70740,7 @@ done: } } - c89atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); + ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); return result; } @@ -70338,7 +70756,7 @@ static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob pResourceManager = pDataBuffer->pResourceManager; - if (pJob->order != c89atomic_load_32(&pDataBuffer->executionPointer)) { + if (pJob->order != ma_atomic_load_32(&pDataBuffer->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } @@ -70353,7 +70771,7 @@ static ma_result ma_job_process__resource_manager__free_data_buffer(ma_job* pJob ma_fence_release(pJob->data.resourceManager.freeDataBuffer.pDoneFence); } - c89atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); + ma_atomic_fetch_add_32(&pDataBuffer->executionPointer, 1); return MA_SUCCESS; } @@ -70372,7 +70790,7 @@ static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob pResourceManager = pDataStream->pResourceManager; - if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { + if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } @@ -70433,7 +70851,7 @@ done: ma_free(pJob->data.resourceManager.loadDataStream.pFilePathW, &pResourceManager->config.allocationCallbacks); /* We can only change the status away from MA_BUSY. If it's set to anything else it means an error has occurred somewhere or the uninitialization process has started (most likely). */ - c89atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result); + ma_atomic_compare_and_swap_i32(&pDataStream->result, MA_BUSY, result); /* Only signal the other threads after the result has been set just for cleanliness sake. */ if (pJob->data.resourceManager.loadDataStream.pInitNotification != NULL) { @@ -70443,7 +70861,7 @@ done: ma_fence_release(pJob->data.resourceManager.loadDataStream.pInitFence); } - c89atomic_fetch_add_32(&pDataStream->executionPointer, 1); + ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); return result; } @@ -70459,7 +70877,7 @@ static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob pResourceManager = pDataStream->pResourceManager; - if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { + if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } @@ -70485,7 +70903,7 @@ static ma_result ma_job_process__resource_manager__free_data_stream(ma_job* pJob ma_fence_release(pJob->data.resourceManager.freeDataStream.pDoneFence); } - /*c89atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/ + /*ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1);*/ return MA_SUCCESS; } @@ -70502,7 +70920,7 @@ static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob pResourceManager = pDataStream->pResourceManager; - if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { + if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } @@ -70515,7 +70933,7 @@ static ma_result ma_job_process__resource_manager__page_data_stream(ma_job* pJob ma_resource_manager_data_stream_fill_page(pDataStream, pJob->data.resourceManager.pageDataStream.pageIndex); done: - c89atomic_fetch_add_32(&pDataStream->executionPointer, 1); + ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); return result; } @@ -70532,7 +70950,7 @@ static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob pResourceManager = pDataStream->pResourceManager; - if (pJob->order != c89atomic_load_32(&pDataStream->executionPointer)) { + if (pJob->order != ma_atomic_load_32(&pDataStream->executionPointer)) { return ma_resource_manager_post_job(pResourceManager, pJob); /* Out of order. */ } @@ -70552,10 +70970,10 @@ static ma_result ma_job_process__resource_manager__seek_data_stream(ma_job* pJob ma_resource_manager_data_stream_fill_pages(pDataStream); /* We need to let the public API know that we're done seeking. */ - c89atomic_fetch_sub_32(&pDataStream->seekCounter, 1); + ma_atomic_fetch_sub_32(&pDataStream->seekCounter, 1); done: - c89atomic_fetch_add_32(&pDataStream->executionPointer, 1); + ma_atomic_fetch_add_32(&pDataStream->executionPointer, 1); return result; } @@ -70654,14 +71072,14 @@ MA_API ma_node_graph_config ma_node_graph_config_init(ma_uint32 channels) static void ma_node_graph_set_is_reading(ma_node_graph* pNodeGraph, ma_bool32 isReading) { MA_ASSERT(pNodeGraph != NULL); - c89atomic_exchange_32(&pNodeGraph->isReading, isReading); + ma_atomic_exchange_32(&pNodeGraph->isReading, isReading); } #if 0 static ma_bool32 ma_node_graph_is_reading(ma_node_graph* pNodeGraph) { MA_ASSERT(pNodeGraph != NULL); - return c89atomic_load_32(&pNodeGraph->isReading); + return ma_atomic_load_32(&pNodeGraph->isReading); } #endif @@ -70911,26 +71329,26 @@ static ma_uint32 ma_node_output_bus_get_channels(const ma_node_output_bus* pOutp static void ma_node_output_bus_set_has_read(ma_node_output_bus* pOutputBus, ma_bool32 hasRead) { if (hasRead) { - c89atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); + ma_atomic_fetch_or_32(&pOutputBus->flags, MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); } else { - c89atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); + ma_atomic_fetch_and_32(&pOutputBus->flags, (ma_uint32)~MA_NODE_OUTPUT_BUS_FLAG_HAS_READ); } } static ma_bool32 ma_node_output_bus_has_read(ma_node_output_bus* pOutputBus) { - return (c89atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0; + return (ma_atomic_load_32(&pOutputBus->flags) & MA_NODE_OUTPUT_BUS_FLAG_HAS_READ) != 0; } static void ma_node_output_bus_set_is_attached(ma_node_output_bus* pOutputBus, ma_bool32 isAttached) { - c89atomic_exchange_32(&pOutputBus->isAttached, isAttached); + ma_atomic_exchange_32(&pOutputBus->isAttached, isAttached); } static ma_bool32 ma_node_output_bus_is_attached(ma_node_output_bus* pOutputBus) { - return c89atomic_load_32(&pOutputBus->isAttached); + return ma_atomic_load_32(&pOutputBus->isAttached); } @@ -70942,14 +71360,14 @@ static ma_result ma_node_output_bus_set_volume(ma_node_output_bus* pOutputBus, f volume = 0.0f; } - c89atomic_exchange_f32(&pOutputBus->volume, volume); + ma_atomic_exchange_f32(&pOutputBus->volume, volume); return MA_SUCCESS; } static float ma_node_output_bus_get_volume(const ma_node_output_bus* pOutputBus) { - return c89atomic_load_f32((float*)&pOutputBus->volume); + return ma_atomic_load_f32((float*)&pOutputBus->volume); } @@ -70986,17 +71404,17 @@ static void ma_node_input_bus_unlock(ma_node_input_bus* pInputBus) static void ma_node_input_bus_next_begin(ma_node_input_bus* pInputBus) { - c89atomic_fetch_add_32(&pInputBus->nextCounter, 1); + ma_atomic_fetch_add_32(&pInputBus->nextCounter, 1); } static void ma_node_input_bus_next_end(ma_node_input_bus* pInputBus) { - c89atomic_fetch_sub_32(&pInputBus->nextCounter, 1); + ma_atomic_fetch_sub_32(&pInputBus->nextCounter, 1); } static ma_uint32 ma_node_input_bus_get_next_counter(ma_node_input_bus* pInputBus) { - return c89atomic_load_32(&pInputBus->nextCounter); + return ma_atomic_load_32(&pInputBus->nextCounter); } @@ -71031,21 +71449,21 @@ static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInp */ ma_node_input_bus_lock(pInputBus); { - ma_node_output_bus* pOldPrev = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pPrev); - ma_node_output_bus* pOldNext = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pNext); + ma_node_output_bus* pOldPrev = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pPrev); + ma_node_output_bus* pOldNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext); if (pOldPrev != NULL) { - c89atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */ + ma_atomic_exchange_ptr(&pOldPrev->pNext, pOldNext); /* <-- This is where the output bus is detached from the list. */ } if (pOldNext != NULL) { - c89atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */ + ma_atomic_exchange_ptr(&pOldNext->pPrev, pOldPrev); /* <-- This is required for detachment. */ } } ma_node_input_bus_unlock(pInputBus); /* At this point the output bus is detached and the linked list is completely unaware of it. Reset some data for safety. */ - c89atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */ - c89atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */ + ma_atomic_exchange_ptr(&pOutputBus->pNext, NULL); /* Using atomic exchanges here, mainly for the benefit of analysis tools which don't always recognize spinlocks. */ + ma_atomic_exchange_ptr(&pOutputBus->pPrev, NULL); /* As above. */ pOutputBus->pInputNode = NULL; pOutputBus->inputNodeInputBusIndex = 0; @@ -71069,7 +71487,7 @@ static void ma_node_input_bus_detach__no_output_bus_lock(ma_node_input_bus* pInp } /* Part 2: Wait for any reads to complete. */ - while (c89atomic_load_32(&pOutputBus->refCount) > 0) { + while (ma_atomic_load_32(&pOutputBus->refCount) > 0) { ma_yield(); } @@ -71100,7 +71518,7 @@ static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_outpu ma_node_output_bus_lock(pOutputBus); { - ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pInputNode); + ma_node_output_bus* pOldInputNode = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pInputNode); /* Detach from any existing attachment first if necessary. */ if (pOldInputNode != NULL) { @@ -71130,18 +71548,18 @@ static void ma_node_input_bus_attach(ma_node_input_bus* pInputBus, ma_node_outpu ma_node_input_bus_lock(pInputBus); { ma_node_output_bus* pNewPrev = &pInputBus->head; - ma_node_output_bus* pNewNext = (ma_node_output_bus*)c89atomic_load_ptr(&pInputBus->head.pNext); + ma_node_output_bus* pNewNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); /* Update the local output bus. */ - c89atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev); - c89atomic_exchange_ptr(&pOutputBus->pNext, pNewNext); + ma_atomic_exchange_ptr(&pOutputBus->pPrev, pNewPrev); + ma_atomic_exchange_ptr(&pOutputBus->pNext, pNewNext); /* Update the other output buses to point back to the local output bus. */ - c89atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */ + ma_atomic_exchange_ptr(&pInputBus->head.pNext, pOutputBus); /* <-- This is where the output bus is actually attached to the input bus. */ /* Do the previous pointer last. This is only used for detachment. */ if (pNewNext != NULL) { - c89atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus); + ma_atomic_exchange_ptr(&pNewNext->pPrev, pOutputBus); } } ma_node_input_bus_unlock(pInputBus); @@ -71169,7 +71587,7 @@ static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, { pNext = pOutputBus; for (;;) { - pNext = (ma_node_output_bus*)c89atomic_load_ptr(&pNext->pNext); + pNext = (ma_node_output_bus*)ma_atomic_load_ptr(&pNext->pNext); if (pNext == NULL) { break; /* Reached the end. */ } @@ -71184,11 +71602,11 @@ static ma_node_output_bus* ma_node_input_bus_next(ma_node_input_bus* pInputBus, /* We need to increment the reference count of the selected node. */ if (pNext != NULL) { - c89atomic_fetch_add_32(&pNext->refCount, 1); + ma_atomic_fetch_add_32(&pNext->refCount, 1); } /* The previous node is no longer being referenced. */ - c89atomic_fetch_sub_32(&pOutputBus->refCount, 1); + ma_atomic_fetch_sub_32(&pOutputBus->refCount, 1); } ma_node_input_bus_next_end(pInputBus); @@ -71800,7 +72218,7 @@ static ma_result ma_node_detach_full(ma_node* pNode) linked list logic. We don't need to worry about the audio thread referencing these because the step above severed the connection to the graph. */ - for (pOutputBus = (ma_node_output_bus*)c89atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)c89atomic_load_ptr(&pOutputBus->pNext)) { + for (pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pInputBus->head.pNext); pOutputBus != NULL; pOutputBus = (ma_node_output_bus*)ma_atomic_load_ptr(&pOutputBus->pNext)) { ma_node_detach_output_bus(pOutputBus->pNode, pOutputBus->outputBusIndex); /* This won't do any waiting in practice and should be efficient. */ } } @@ -71916,7 +72334,7 @@ MA_API ma_result ma_node_set_state(ma_node* pNode, ma_node_state state) return MA_INVALID_ARGS; } - c89atomic_exchange_i32(&pNodeBase->state, state); + ma_atomic_exchange_i32(&pNodeBase->state, state); return MA_SUCCESS; } @@ -71929,7 +72347,7 @@ MA_API ma_node_state ma_node_get_state(const ma_node* pNode) return ma_node_state_stopped; } - return (ma_node_state)c89atomic_load_i32(&pNodeBase->state); + return (ma_node_state)ma_atomic_load_i32(&pNodeBase->state); } MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_uint64 globalTime) @@ -71943,7 +72361,7 @@ MA_API ma_result ma_node_set_state_time(ma_node* pNode, ma_node_state state, ma_ return MA_INVALID_ARGS; } - c89atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime); + ma_atomic_exchange_64(&((ma_node_base*)pNode)->stateTimes[state], globalTime); return MA_SUCCESS; } @@ -71959,7 +72377,7 @@ MA_API ma_uint64 ma_node_get_state_time(const ma_node* pNode, ma_node_state stat return 0; } - return c89atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]); + return ma_atomic_load_64(&((ma_node_base*)pNode)->stateTimes[state]); } MA_API ma_node_state ma_node_get_state_by_time(const ma_node* pNode, ma_uint64 globalTime) @@ -72009,7 +72427,7 @@ MA_API ma_uint64 ma_node_get_time(const ma_node* pNode) return 0; } - return c89atomic_load_64(&((ma_node_base*)pNode)->localTime); + return ma_atomic_load_64(&((ma_node_base*)pNode)->localTime); } MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime) @@ -72018,7 +72436,7 @@ MA_API ma_result ma_node_set_time(ma_node* pNode, ma_uint64 localTime) return MA_INVALID_ARGS; } - c89atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime); + ma_atomic_exchange_64(&((ma_node_base*)pNode)->localTime, localTime); return MA_SUCCESS; } @@ -72393,7 +72811,7 @@ static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusInde ma_apply_volume_factor_f32(pFramesOut, totalFramesRead * ma_node_get_output_channels(pNodeBase, outputBusIndex), ma_node_output_bus_get_volume(&pNodeBase->pOutputBuses[outputBusIndex])); /* Advance our local time forward. */ - c89atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead); + ma_atomic_fetch_add_64(&pNodeBase->localTime, (ma_uint64)totalFramesRead); *pFramesRead = totalFramesRead + timeOffsetBeg; /* Must include the silenced section at the start of the buffer. */ return result; @@ -73520,7 +73938,7 @@ Engine static void ma_sound_set_at_end(ma_sound* pSound, ma_bool32 atEnd) { MA_ASSERT(pSound != NULL); - c89atomic_exchange_32(&pSound->atEnd, atEnd); + ma_atomic_exchange_32(&pSound->atEnd, atEnd); /* Fire any callbacks or events. */ if (atEnd) { @@ -73533,7 +73951,7 @@ static void ma_sound_set_at_end(ma_sound* pSound, ma_bool32 atEnd) static ma_bool32 ma_sound_get_at_end(const ma_sound* pSound) { MA_ASSERT(pSound != NULL); - return c89atomic_load_32(&pSound->atEnd); + return ma_atomic_load_32(&pSound->atEnd); } @@ -73559,7 +73977,7 @@ static void ma_engine_node_update_pitch_if_required(ma_engine_node* pEngineNode) MA_ASSERT(pEngineNode != NULL); - newPitch = c89atomic_load_explicit_f32(&pEngineNode->pitch, c89atomic_memory_order_acquire); + newPitch = ma_atomic_load_explicit_f32(&pEngineNode->pitch, ma_atomic_memory_order_acquire); if (pEngineNode->oldPitch != newPitch) { pEngineNode->oldPitch = newPitch; @@ -73582,14 +74000,14 @@ static ma_bool32 ma_engine_node_is_pitching_enabled(const ma_engine_node* pEngin MA_ASSERT(pEngineNode != NULL); /* Don't try to be clever by skiping resampling in the pitch=1 case or else you'll glitch when moving away from 1. */ - return !c89atomic_load_explicit_32(&pEngineNode->isPitchDisabled, c89atomic_memory_order_acquire); + return !ma_atomic_load_explicit_32(&pEngineNode->isPitchDisabled, ma_atomic_memory_order_acquire); } static ma_bool32 ma_engine_node_is_spatialization_enabled(const ma_engine_node* pEngineNode) { MA_ASSERT(pEngineNode != NULL); - return !c89atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, c89atomic_memory_order_acquire); + return !ma_atomic_load_explicit_32(&pEngineNode->isSpatializationDisabled, ma_atomic_memory_order_acquire); } static ma_uint64 ma_engine_node_get_required_input_frame_count(const ma_engine_node* pEngineNode, ma_uint64 outputFrameCount) @@ -73669,6 +74087,26 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo totalFramesProcessedIn = 0; totalFramesProcessedOut = 0; + /* Update the fader if applicable. */ + { + ma_uint64 fadeLengthInFrames = ma_atomic_uint64_get(&pEngineNode->fadeSettings.fadeLengthInFrames); + if (fadeLengthInFrames != ~(ma_uint64)0) { + float fadeVolumeBeg = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeBeg); + float fadeVolumeEnd = ma_atomic_float_get(&pEngineNode->fadeSettings.volumeEnd); + ma_int64 fadeStartOffsetInFrames = (ma_int64)ma_atomic_uint64_get(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames); + if (fadeStartOffsetInFrames == (ma_int64)(~(ma_uint64)0)) { + fadeStartOffsetInFrames = 0; + } else { + fadeStartOffsetInFrames -= ma_engine_get_time(pEngineNode->pEngine); + } + + ma_fader_set_fade_ex(&pEngineNode->fader, fadeVolumeBeg, fadeVolumeEnd, fadeLengthInFrames, fadeStartOffsetInFrames); + + /* Reset the fade length so we don't erroneously apply it again. */ + ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, ~(ma_uint64)0); + } + } + isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode); isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1; isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode); @@ -73687,10 +74125,10 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo the output buffer and then do all effects from that point directly in the output buffer in-place. - Note that we're always running the resampler. If we try to be clever and skip resampling - when the pitch is 1, we'll get a glitch when we move away from 1, back to 1, and then - away from 1 again. We'll want to implement any pitch=1 optimizations in the resampler - itself. + Note that we're always running the resampler if pitching is enabled, even when the pitch + is 1. If we try to be clever and skip resampling when the pitch is 1, we'll get a glitch + when we move away from 1, back to 1, and then away from 1 again. We'll want to implement + any pitch=1 optimizations in the resampler itself. There's a small optimization here that we'll utilize since it might be a fairly common case. When the input and output channel counts are the same, we'll read straight into the @@ -73858,14 +74296,14 @@ static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float } /* If we're seeking, do so now before reading. */ - seekTarget = c89atomic_load_64(&pSound->seekTarget); + seekTarget = ma_atomic_load_64(&pSound->seekTarget); if (seekTarget != MA_SEEK_TARGET_NONE) { ma_data_source_seek_to_pcm_frame(pSound->pDataSource, seekTarget); /* Any time-dependant effects need to have their times updated. */ ma_node_set_time(pSound, seekTarget); - c89atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE); + ma_atomic_exchange_64(&pSound->seekTarget, MA_SEEK_TARGET_NONE); } /* @@ -74188,6 +74626,10 @@ MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* p pEngineNode->isPitchDisabled = pConfig->isPitchDisabled; pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled; pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex; + ma_atomic_float_set(&pEngineNode->fadeSettings.volumeBeg, 1); + ma_atomic_float_set(&pEngineNode->fadeSettings.volumeEnd, 1); + ma_atomic_uint64_set(&pEngineNode->fadeSettings.fadeLengthInFrames, (~(ma_uint64)0)); + ma_atomic_uint64_set(&pEngineNode->fadeSettings.absoluteGlobalTimeInFrames, (~(ma_uint64)0)); /* <-- Indicates that the fade should start immediately. */ channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); @@ -74455,6 +74897,8 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng pEngine->monoExpansionMode = engineConfig.monoExpansionMode; pEngine->defaultVolumeSmoothTimeInPCMFrames = engineConfig.defaultVolumeSmoothTimeInPCMFrames; + pEngine->onProcess = engineConfig.onProcess; + pEngine->pProcessUserData = engineConfig.pProcessUserData; ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks); #if !defined(MA_NO_RESOURCE_MANAGER) @@ -74481,7 +74925,7 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng deviceConfig.playback.format = ma_format_f32; deviceConfig.playback.channels = engineConfig.channels; deviceConfig.sampleRate = engineConfig.sampleRate; - deviceConfig.dataCallback = ma_engine_data_callback_internal; + deviceConfig.dataCallback = (engineConfig.dataCallback != NULL) ? engineConfig.dataCallback : ma_engine_data_callback_internal; deviceConfig.pUserData = pEngine; deviceConfig.notificationCallback = engineConfig.notificationCallback; deviceConfig.periodSizeInFrames = engineConfig.periodSizeInFrames; @@ -74753,7 +75197,27 @@ MA_API void ma_engine_uninit(ma_engine* pEngine) MA_API ma_result ma_engine_read_pcm_frames(ma_engine* pEngine, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) { - return ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, pFramesRead); + ma_result result; + ma_uint64 framesRead = 0; + + if (pFramesRead != NULL) { + *pFramesRead = 0; + } + + result = ma_node_graph_read_pcm_frames(&pEngine->nodeGraph, pFramesOut, frameCount, &framesRead); + if (result != MA_SUCCESS) { + return result; + } + + if (pFramesRead != NULL) { + *pFramesRead = framesRead; + } + + if (pEngine->onProcess) { + pEngine->onProcess(pEngine->pProcessUserData, (float*)pFramesOut, framesRead); /* Safe cast to float* because the engine always works on floating point samples. */ + } + + return MA_SUCCESS; } MA_API ma_node_graph* ma_engine_get_node_graph(ma_engine* pEngine) @@ -74939,13 +75403,23 @@ MA_API ma_result ma_engine_set_volume(ma_engine* pEngine, float volume) return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, volume); } -MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB) +MA_API float ma_engine_get_volume(ma_engine* pEngine) { if (pEngine == NULL) { - return MA_INVALID_ARGS; + return 0; } - return ma_node_set_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0, ma_volume_db_to_linear(gainDB)); + return ma_node_get_output_bus_volume(ma_node_graph_get_endpoint(&pEngine->nodeGraph), 0); +} + +MA_API ma_result ma_engine_set_gain_db(ma_engine* pEngine, float gainDB) +{ + return ma_engine_set_volume(pEngine, ma_volume_db_to_linear(gainDB)); +} + +MA_API float ma_engine_get_gain_db(ma_engine* pEngine) +{ + return ma_volume_linear_to_db(ma_engine_get_volume(pEngine)); } @@ -75140,7 +75614,7 @@ MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePa is uninitialize it and reinitialize it. All we're doing is recycling memory. */ pSound = pNextSound; - c89atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1); + ma_atomic_fetch_sub_32(&pEngine->inlinedSoundCount, 1); break; } } @@ -75211,11 +75685,11 @@ MA_API ma_result ma_engine_play_sound_ex(ma_engine* pEngine, const char* pFilePa result = ma_sound_start(&pSound->sound); if (result != MA_SUCCESS) { /* Failed to start the sound. We need to mark it for recycling and return an error. */ - c89atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE); + ma_atomic_exchange_32(&pSound->sound.atEnd, MA_TRUE); return result; } - c89atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1); + ma_atomic_fetch_add_32(&pEngine->inlinedSoundCount, 1); return result; } @@ -75601,7 +76075,7 @@ MA_API ma_result ma_sound_start(ma_sound* pSound) } /* Make sure we clear the end indicator. */ - c89atomic_exchange_32(&pSound->atEnd, MA_FALSE); + ma_atomic_exchange_32(&pSound->atEnd, MA_FALSE); } /* Make sure the sound is started. If there's a start delay, the sound won't actually start until the start time is reached. */ @@ -75622,6 +76096,31 @@ MA_API ma_result ma_sound_stop(ma_sound* pSound) return MA_SUCCESS; } +MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 fadeLengthInFrames) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* Stopping with a fade out requires us to schedule the stop into the future by the fade length. */ + ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, ma_engine_get_time(ma_sound_get_engine(pSound)) + fadeLengthInFrames, fadeLengthInFrames); + + return MA_SUCCESS; +} + +MA_API ma_result ma_sound_stop_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 fadeLengthInMilliseconds) +{ + ma_uint64 sampleRate; + + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); + + return ma_sound_stop_with_fade_in_pcm_frames(pSound, (fadeLengthInMilliseconds * sampleRate) / 1000); +} + MA_API void ma_sound_set_volume(ma_sound* pSound, float volume) { if (pSound == NULL) { @@ -75690,7 +76189,7 @@ MA_API void ma_sound_set_pitch(ma_sound* pSound, float pitch) return; } - c89atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, c89atomic_memory_order_release); + ma_atomic_exchange_explicit_f32(&pSound->engineNode.pitch, pitch, ma_atomic_memory_order_release); } MA_API float ma_sound_get_pitch(const ma_sound* pSound) @@ -75699,7 +76198,7 @@ MA_API float ma_sound_get_pitch(const ma_sound* pSound) return 0; } - return c89atomic_load_f32(&pSound->engineNode.pitch); /* Naughty const-cast for this. */ + return ma_atomic_load_f32(&pSound->engineNode.pitch); /* Naughty const-cast for this. */ } MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enabled) @@ -75708,7 +76207,7 @@ MA_API void ma_sound_set_spatialization_enabled(ma_sound* pSound, ma_bool32 enab return; } - c89atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, c89atomic_memory_order_release); + ma_atomic_exchange_explicit_32(&pSound->engineNode.isSpatializationDisabled, !enabled, ma_atomic_memory_order_release); } MA_API ma_bool32 ma_sound_is_spatialization_enabled(const ma_sound* pSound) @@ -75726,7 +76225,7 @@ MA_API void ma_sound_set_pinned_listener_index(ma_sound* pSound, ma_uint32 liste return; } - c89atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, c89atomic_memory_order_release); + ma_atomic_exchange_explicit_32(&pSound->engineNode.pinnedListenerIndex, listenerIndex, ma_atomic_memory_order_release); } MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound) @@ -75735,7 +76234,7 @@ MA_API ma_uint32 ma_sound_get_pinned_listener_index(const ma_sound* pSound) return MA_LISTENER_INDEX_CLOSEST; } - return c89atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, c89atomic_memory_order_acquire); + return ma_atomic_load_explicit_32(&pSound->engineNode.pinnedListenerIndex, ma_atomic_memory_order_acquire); } MA_API ma_uint32 ma_sound_get_listener_index(const ma_sound* pSound) @@ -76023,7 +76522,7 @@ MA_API void ma_sound_set_fade_in_pcm_frames(ma_sound* pSound, float volumeBeg, f return; } - ma_fader_set_fade(&pSound->engineNode.fader, volumeBeg, volumeEnd, fadeLengthInFrames); + ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, fadeLengthInFrames, (~(ma_uint64)0)); } MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds) @@ -76035,6 +76534,36 @@ MA_API void ma_sound_set_fade_in_milliseconds(ma_sound* pSound, float volumeBeg, ma_sound_set_fade_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * pSound->engineNode.fader.config.sampleRate) / 1000); } +MA_API void ma_sound_set_fade_start_in_pcm_frames(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInFrames, ma_uint64 absoluteGlobalTimeInFrames) +{ + if (pSound == NULL) { + return; + } + + /* + We don't want to update the fader at this point because we need to use the engine's current time + to derive the fader's start offset. The timer is being updated on the audio thread so in order to + do this as accurately as possible we'll need to defer this to the audio thread. + */ + ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeBeg, volumeBeg); + ma_atomic_float_set(&pSound->engineNode.fadeSettings.volumeEnd, volumeEnd); + ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.fadeLengthInFrames, fadeLengthInFrames); + ma_atomic_uint64_set(&pSound->engineNode.fadeSettings.absoluteGlobalTimeInFrames, absoluteGlobalTimeInFrames); +} + +MA_API void ma_sound_set_fade_start_in_milliseconds(ma_sound* pSound, float volumeBeg, float volumeEnd, ma_uint64 fadeLengthInMilliseconds, ma_uint64 absoluteGlobalTimeInMilliseconds) +{ + ma_uint32 sampleRate; + + if (pSound == NULL) { + return; + } + + sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); + + ma_sound_set_fade_start_in_pcm_frames(pSound, volumeBeg, volumeEnd, (fadeLengthInMilliseconds * sampleRate) / 1000, (absoluteGlobalTimeInMilliseconds * sampleRate) / 1000); +} + MA_API float ma_sound_get_current_fade_volume(const ma_sound* pSound) { if (pSound == NULL) { @@ -76068,7 +76597,7 @@ MA_API void ma_sound_set_stop_time_in_pcm_frames(ma_sound* pSound, ma_uint64 abs return; } - ma_node_set_state_time(pSound, ma_node_state_stopped, absoluteGlobalTimeInFrames); + ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, absoluteGlobalTimeInFrames, 0); } MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 absoluteGlobalTimeInMilliseconds) @@ -76080,6 +76609,36 @@ MA_API void ma_sound_set_stop_time_in_milliseconds(ma_sound* pSound, ma_uint64 a ma_sound_set_stop_time_in_pcm_frames(pSound, absoluteGlobalTimeInMilliseconds * ma_engine_get_sample_rate(ma_sound_get_engine(pSound)) / 1000); } +MA_API void ma_sound_set_stop_time_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInFrames, ma_uint64 fadeLengthInFrames) +{ + if (pSound == NULL) { + return; + } + + if (fadeLengthInFrames > 0) { + if (fadeLengthInFrames > stopAbsoluteGlobalTimeInFrames) { + fadeLengthInFrames = stopAbsoluteGlobalTimeInFrames; + } + + ma_sound_set_fade_start_in_pcm_frames(pSound, -1, 0, fadeLengthInFrames, stopAbsoluteGlobalTimeInFrames - fadeLengthInFrames); + } + + ma_node_set_state_time(pSound, ma_node_state_stopped, stopAbsoluteGlobalTimeInFrames); +} + +MA_API void ma_sound_set_stop_time_with_fade_in_milliseconds(ma_sound* pSound, ma_uint64 stopAbsoluteGlobalTimeInMilliseconds, ma_uint64 fadeLengthInMilliseconds) +{ + ma_uint32 sampleRate; + + if (pSound == NULL) { + return; + } + + sampleRate = ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); + + ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, (stopAbsoluteGlobalTimeInMilliseconds * sampleRate) / 1000, (fadeLengthInMilliseconds * sampleRate) / 1000); +} + MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound) { if (pSound == NULL) { @@ -76098,6 +76657,11 @@ MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound) return ma_node_get_time(pSound); } +MA_API ma_uint64 ma_sound_get_time_in_milliseconds(const ma_sound* pSound) +{ + return ma_sound_get_time_in_pcm_frames(pSound) * 1000 / ma_engine_get_sample_rate(ma_sound_get_engine(pSound)); +} + MA_API void ma_sound_set_looping(ma_sound* pSound, ma_bool32 isLooping) { if (pSound == NULL) { @@ -76153,7 +76717,7 @@ MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameInd } /* We can't be seeking while reading at the same time. We just set the seek target and get the mixing thread to do the actual seek. */ - c89atomic_exchange_64(&pSound->seekTarget, frameIndex); + ma_atomic_exchange_64(&pSound->seekTarget, frameIndex); return MA_SUCCESS; } @@ -76578,167 +77142,135 @@ MA_API ma_uint64 ma_sound_group_get_time_in_pcm_frames(const ma_sound_group* pGr Auto Generated ============== -All code below is auto-generated from a tool. This mostly consists of decoding backend implementations such as dr_wav, dr_flac, etc. If you find a bug in the +All code below is auto-generated from a tool. This mostly consists of decoding backend implementations such as ma_dr_wav, ma_dr_flac, etc. If you find a bug in the code below please report the bug to the respective repository for the relevant project (probably dr_libs). *************************************************************************************************************************************************************** **************************************************************************************************************************************************************/ #if !defined(MA_NO_WAV) && (!defined(MA_NO_DECODING) || !defined(MA_NO_ENCODING)) -#if !defined(DR_WAV_IMPLEMENTATION) && !defined(DRWAV_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ +#if !defined(MA_DR_WAV_IMPLEMENTATION) && !defined(MA_DR_WAV_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ /* dr_wav_c begin */ -#ifndef dr_wav_c -#define dr_wav_c +#ifndef ma_dr_wav_c +#define ma_dr_wav_c #ifdef __MRC__ #pragma options opt off #endif #include #include #include -#ifndef DR_WAV_NO_STDIO +#ifndef MA_DR_WAV_NO_STDIO #include -#ifndef DR_WAV_NO_WCHAR +#ifndef MA_DR_WAV_NO_WCHAR #include #endif #endif -#ifndef DRWAV_ASSERT +#ifndef MA_DR_WAV_ASSERT #include -#define DRWAV_ASSERT(expression) assert(expression) +#define MA_DR_WAV_ASSERT(expression) assert(expression) #endif -#ifndef DRWAV_MALLOC -#define DRWAV_MALLOC(sz) malloc((sz)) +#ifndef MA_DR_WAV_MALLOC +#define MA_DR_WAV_MALLOC(sz) malloc((sz)) #endif -#ifndef DRWAV_REALLOC -#define DRWAV_REALLOC(p, sz) realloc((p), (sz)) +#ifndef MA_DR_WAV_REALLOC +#define MA_DR_WAV_REALLOC(p, sz) realloc((p), (sz)) #endif -#ifndef DRWAV_FREE -#define DRWAV_FREE(p) free((p)) +#ifndef MA_DR_WAV_FREE +#define MA_DR_WAV_FREE(p) free((p)) #endif -#ifndef DRWAV_COPY_MEMORY -#define DRWAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#ifndef MA_DR_WAV_COPY_MEMORY +#define MA_DR_WAV_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) #endif -#ifndef DRWAV_ZERO_MEMORY -#define DRWAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#ifndef MA_DR_WAV_ZERO_MEMORY +#define MA_DR_WAV_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) #endif -#ifndef DRWAV_ZERO_OBJECT -#define DRWAV_ZERO_OBJECT(p) DRWAV_ZERO_MEMORY((p), sizeof(*p)) -#endif -#define drwav_countof(x) (sizeof(x) / sizeof(x[0])) -#define drwav_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) -#define drwav_min(a, b) (((a) < (b)) ? (a) : (b)) -#define drwav_max(a, b) (((a) > (b)) ? (a) : (b)) -#define drwav_clamp(x, lo, hi) (drwav_max((lo), drwav_min((hi), (x)))) -#define drwav_offset_ptr(p, offset) (((drwav_uint8*)(p)) + (offset)) -#define DRWAV_MAX_SIMD_VECTOR_SIZE 64 -#if defined(__x86_64__) || defined(_M_X64) - #define DRWAV_X64 -#elif defined(__i386) || defined(_M_IX86) - #define DRWAV_X86 -#elif defined(__arm__) || defined(_M_ARM) - #define DRWAV_ARM -#endif -#ifdef _MSC_VER - #define DRWAV_INLINE __forceinline -#elif defined(__GNUC__) - #if defined(__STRICT_ANSI__) - #define DRWAV_GNUC_INLINE_HINT __inline__ - #else - #define DRWAV_GNUC_INLINE_HINT inline - #endif - #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) - #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT __attribute__((always_inline)) - #else - #define DRWAV_INLINE DRWAV_GNUC_INLINE_HINT - #endif -#elif defined(__WATCOMC__) - #define DRWAV_INLINE __inline -#else - #define DRWAV_INLINE -#endif -#if defined(SIZE_MAX) - #define DRWAV_SIZE_MAX SIZE_MAX -#else - #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) - #define DRWAV_SIZE_MAX ((drwav_uint64)0xFFFFFFFFFFFFFFFF) - #else - #define DRWAV_SIZE_MAX 0xFFFFFFFF - #endif +#ifndef MA_DR_WAV_ZERO_OBJECT +#define MA_DR_WAV_ZERO_OBJECT(p) MA_DR_WAV_ZERO_MEMORY((p), sizeof(*p)) #endif +#define ma_dr_wav_countof(x) (sizeof(x) / sizeof(x[0])) +#define ma_dr_wav_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) +#define ma_dr_wav_min(a, b) (((a) < (b)) ? (a) : (b)) +#define ma_dr_wav_max(a, b) (((a) > (b)) ? (a) : (b)) +#define ma_dr_wav_clamp(x, lo, hi) (ma_dr_wav_max((lo), ma_dr_wav_min((hi), (x)))) +#define ma_dr_wav_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) +#define MA_DR_WAV_MAX_SIMD_VECTOR_SIZE 32 +#define MA_DR_WAV_INT64_MIN ((ma_int64)0x80000000 << 32) +#define MA_DR_WAV_INT64_MAX ((((ma_int64)0x7FFFFFFF) << 32) | 0xFFFFFFFF) #if defined(_MSC_VER) && _MSC_VER >= 1400 - #define DRWAV_HAS_BYTESWAP16_INTRINSIC - #define DRWAV_HAS_BYTESWAP32_INTRINSIC - #define DRWAV_HAS_BYTESWAP64_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC #elif defined(__clang__) #if defined(__has_builtin) #if __has_builtin(__builtin_bswap16) - #define DRWAV_HAS_BYTESWAP16_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC #endif #if __has_builtin(__builtin_bswap32) - #define DRWAV_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC #endif #if __has_builtin(__builtin_bswap64) - #define DRWAV_HAS_BYTESWAP64_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC #endif #endif #elif defined(__GNUC__) #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define DRWAV_HAS_BYTESWAP32_INTRINSIC - #define DRWAV_HAS_BYTESWAP64_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC #endif #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define DRWAV_HAS_BYTESWAP16_INTRINSIC + #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC #endif #endif -DRWAV_API void drwav_version(drwav_uint32* pMajor, drwav_uint32* pMinor, drwav_uint32* pRevision) +MA_API void ma_dr_wav_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) { if (pMajor) { - *pMajor = DRWAV_VERSION_MAJOR; + *pMajor = MA_DR_WAV_VERSION_MAJOR; } if (pMinor) { - *pMinor = DRWAV_VERSION_MINOR; + *pMinor = MA_DR_WAV_VERSION_MINOR; } if (pRevision) { - *pRevision = DRWAV_VERSION_REVISION; + *pRevision = MA_DR_WAV_VERSION_REVISION; } } -DRWAV_API const char* drwav_version_string(void) +MA_API const char* ma_dr_wav_version_string(void) { - return DRWAV_VERSION_STRING; + return MA_DR_WAV_VERSION_STRING; } -#ifndef DRWAV_MAX_SAMPLE_RATE -#define DRWAV_MAX_SAMPLE_RATE 384000 +#ifndef MA_DR_WAV_MAX_SAMPLE_RATE +#define MA_DR_WAV_MAX_SAMPLE_RATE 384000 #endif -#ifndef DRWAV_MAX_CHANNELS -#define DRWAV_MAX_CHANNELS 256 +#ifndef MA_DR_WAV_MAX_CHANNELS +#define MA_DR_WAV_MAX_CHANNELS 256 #endif -#ifndef DRWAV_MAX_BITS_PER_SAMPLE -#define DRWAV_MAX_BITS_PER_SAMPLE 64 +#ifndef MA_DR_WAV_MAX_BITS_PER_SAMPLE +#define MA_DR_WAV_MAX_BITS_PER_SAMPLE 64 #endif -static const drwav_uint8 drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; -static const drwav_uint8 drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const drwav_uint8 drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const drwav_uint8 drwavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static const drwav_uint8 drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; -static DRWAV_INLINE int drwav__is_little_endian(void) +static const ma_uint8 ma_dr_wavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; +static const ma_uint8 ma_dr_wavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; +static const ma_uint8 ma_dr_wavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; +static const ma_uint8 ma_dr_wavGUID_W64_FACT[16] = {0x66,0x61,0x63,0x74, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; +static const ma_uint8 ma_dr_wavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; +static MA_INLINE int ma_dr_wav__is_little_endian(void) { -#if defined(DRWAV_X86) || defined(DRWAV_X64) - return DRWAV_TRUE; +#if defined(MA_X86) || defined(MA_X64) + return MA_TRUE; #elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN - return DRWAV_TRUE; + return MA_TRUE; #else int n = 1; return (*(char*)&n) == 1; #endif } -static DRWAV_INLINE void drwav_bytes_to_guid(const drwav_uint8* data, drwav_uint8* guid) +static MA_INLINE void ma_dr_wav_bytes_to_guid(const ma_uint8* data, ma_uint8* guid) { int i; for (i = 0; i < 16; ++i) { guid[i] = data[i]; } } -static DRWAV_INLINE drwav_uint16 drwav__bswap16(drwav_uint16 n) +static MA_INLINE ma_uint16 ma_dr_wav__bswap16(ma_uint16 n) { -#ifdef DRWAV_HAS_BYTESWAP16_INTRINSIC +#ifdef MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC #if defined(_MSC_VER) return _byteswap_ushort(n); #elif defined(__GNUC__) || defined(__clang__) @@ -76751,16 +77283,16 @@ static DRWAV_INLINE drwav_uint16 drwav__bswap16(drwav_uint16 n) ((n & 0x00FF) << 8); #endif } -static DRWAV_INLINE drwav_uint32 drwav__bswap32(drwav_uint32 n) +static MA_INLINE ma_uint32 ma_dr_wav__bswap32(ma_uint32 n) { -#ifdef DRWAV_HAS_BYTESWAP32_INTRINSIC +#ifdef MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC #if defined(_MSC_VER) return _byteswap_ulong(n); #elif defined(__GNUC__) || defined(__clang__) - #if defined(DRWAV_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRWAV_64BIT) - drwav_uint32 r; + #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT) + ma_uint32 r; __asm__ __volatile__ ( - #if defined(DRWAV_64BIT) + #if defined(MA_64BIT) "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) #else "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) @@ -76780,9 +77312,9 @@ static DRWAV_INLINE drwav_uint32 drwav__bswap32(drwav_uint32 n) ((n & 0x000000FF) << 24); #endif } -static DRWAV_INLINE drwav_uint64 drwav__bswap64(drwav_uint64 n) +static MA_INLINE ma_uint64 ma_dr_wav__bswap64(ma_uint64 n) { -#ifdef DRWAV_HAS_BYTESWAP64_INTRINSIC +#ifdef MA_DR_WAV_HAS_BYTESWAP64_INTRINSIC #if defined(_MSC_VER) return _byteswap_uint64(n); #elif defined(__GNUC__) || defined(__clang__) @@ -76791,88 +77323,82 @@ static DRWAV_INLINE drwav_uint64 drwav__bswap64(drwav_uint64 n) #error "This compiler does not support the byte swap intrinsic." #endif #else - return ((n & ((drwav_uint64)0xFF000000 << 32)) >> 56) | - ((n & ((drwav_uint64)0x00FF0000 << 32)) >> 40) | - ((n & ((drwav_uint64)0x0000FF00 << 32)) >> 24) | - ((n & ((drwav_uint64)0x000000FF << 32)) >> 8) | - ((n & ((drwav_uint64)0xFF000000 )) << 8) | - ((n & ((drwav_uint64)0x00FF0000 )) << 24) | - ((n & ((drwav_uint64)0x0000FF00 )) << 40) | - ((n & ((drwav_uint64)0x000000FF )) << 56); + return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) | + ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) | + ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) | + ((n & ((ma_uint64)0x000000FF << 32)) >> 8) | + ((n & ((ma_uint64)0xFF000000 )) << 8) | + ((n & ((ma_uint64)0x00FF0000 )) << 24) | + ((n & ((ma_uint64)0x0000FF00 )) << 40) | + ((n & ((ma_uint64)0x000000FF )) << 56); #endif } -static DRWAV_INLINE drwav_int16 drwav__bswap_s16(drwav_int16 n) +static MA_INLINE ma_int16 ma_dr_wav__bswap_s16(ma_int16 n) { - return (drwav_int16)drwav__bswap16((drwav_uint16)n); + return (ma_int16)ma_dr_wav__bswap16((ma_uint16)n); } -static DRWAV_INLINE void drwav__bswap_samples_s16(drwav_int16* pSamples, drwav_uint64 sampleCount) +static MA_INLINE void ma_dr_wav__bswap_samples_s16(ma_int16* pSamples, ma_uint64 sampleCount) { - drwav_uint64 iSample; + ma_uint64 iSample; for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = drwav__bswap_s16(pSamples[iSample]); + pSamples[iSample] = ma_dr_wav__bswap_s16(pSamples[iSample]); } } -static DRWAV_INLINE void drwav__bswap_s24(drwav_uint8* p) +static MA_INLINE void ma_dr_wav__bswap_s24(ma_uint8* p) { - drwav_uint8 t; + ma_uint8 t; t = p[0]; p[0] = p[2]; p[2] = t; } -static DRWAV_INLINE void drwav__bswap_samples_s24(drwav_uint8* pSamples, drwav_uint64 sampleCount) +static MA_INLINE void ma_dr_wav__bswap_samples_s24(ma_uint8* pSamples, ma_uint64 sampleCount) { - drwav_uint64 iSample; + ma_uint64 iSample; for (iSample = 0; iSample < sampleCount; iSample += 1) { - drwav_uint8* pSample = pSamples + (iSample*3); - drwav__bswap_s24(pSample); + ma_uint8* pSample = pSamples + (iSample*3); + ma_dr_wav__bswap_s24(pSample); } } -static DRWAV_INLINE drwav_int32 drwav__bswap_s32(drwav_int32 n) +static MA_INLINE ma_int32 ma_dr_wav__bswap_s32(ma_int32 n) { - return (drwav_int32)drwav__bswap32((drwav_uint32)n); + return (ma_int32)ma_dr_wav__bswap32((ma_uint32)n); } -static DRWAV_INLINE void drwav__bswap_samples_s32(drwav_int32* pSamples, drwav_uint64 sampleCount) +static MA_INLINE void ma_dr_wav__bswap_samples_s32(ma_int32* pSamples, ma_uint64 sampleCount) { - drwav_uint64 iSample; + ma_uint64 iSample; for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = drwav__bswap_s32(pSamples[iSample]); + pSamples[iSample] = ma_dr_wav__bswap_s32(pSamples[iSample]); } } -static DRWAV_INLINE float drwav__bswap_f32(float n) +static MA_INLINE ma_int64 ma_dr_wav__bswap_s64(ma_int64 n) +{ + return (ma_int64)ma_dr_wav__bswap64((ma_uint64)n); +} +static MA_INLINE void ma_dr_wav__bswap_samples_s64(ma_int64* pSamples, ma_uint64 sampleCount) +{ + ma_uint64 iSample; + for (iSample = 0; iSample < sampleCount; iSample += 1) { + pSamples[iSample] = ma_dr_wav__bswap_s64(pSamples[iSample]); + } +} +static MA_INLINE float ma_dr_wav__bswap_f32(float n) { union { - drwav_uint32 i; + ma_uint32 i; float f; } x; x.f = n; - x.i = drwav__bswap32(x.i); + x.i = ma_dr_wav__bswap32(x.i); return x.f; } -static DRWAV_INLINE void drwav__bswap_samples_f32(float* pSamples, drwav_uint64 sampleCount) +static MA_INLINE void ma_dr_wav__bswap_samples_f32(float* pSamples, ma_uint64 sampleCount) { - drwav_uint64 iSample; + ma_uint64 iSample; for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = drwav__bswap_f32(pSamples[iSample]); + pSamples[iSample] = ma_dr_wav__bswap_f32(pSamples[iSample]); } } -static DRWAV_INLINE double drwav__bswap_f64(double n) -{ - union { - drwav_uint64 i; - double f; - } x; - x.f = n; - x.i = drwav__bswap64(x.i); - return x.f; -} -static DRWAV_INLINE void drwav__bswap_samples_f64(double* pSamples, drwav_uint64 sampleCount) -{ - drwav_uint64 iSample; - for (iSample = 0; iSample < sampleCount; iSample += 1) { - pSamples[iSample] = drwav__bswap_f64(pSamples[iSample]); - } -} -static DRWAV_INLINE void drwav__bswap_samples_pcm(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample) +static MA_INLINE void ma_dr_wav__bswap_samples(void* pSamples, ma_uint64 sampleCount, ma_uint32 bytesPerSample) { switch (bytesPerSample) { @@ -76881,87 +77407,108 @@ static DRWAV_INLINE void drwav__bswap_samples_pcm(void* pSamples, drwav_uint64 s } break; case 2: { - drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount); + ma_dr_wav__bswap_samples_s16((ma_int16*)pSamples, sampleCount); } break; case 3: { - drwav__bswap_samples_s24((drwav_uint8*)pSamples, sampleCount); + ma_dr_wav__bswap_samples_s24((ma_uint8*)pSamples, sampleCount); } break; case 4: { - drwav__bswap_samples_s32((drwav_int32*)pSamples, sampleCount); - } break; - default: - { - DRWAV_ASSERT(DRWAV_FALSE); - } break; - } -} -static DRWAV_INLINE void drwav__bswap_samples_ieee(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample) -{ - switch (bytesPerSample) - { - #if 0 - case 2: - { - drwav__bswap_samples_f16((drwav_float16*)pSamples, sampleCount); - } break; - #endif - case 4: - { - drwav__bswap_samples_f32((float*)pSamples, sampleCount); + ma_dr_wav__bswap_samples_s32((ma_int32*)pSamples, sampleCount); } break; case 8: { - drwav__bswap_samples_f64((double*)pSamples, sampleCount); + ma_dr_wav__bswap_samples_s64((ma_int64*)pSamples, sampleCount); } break; default: { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); } break; } } -static DRWAV_INLINE void drwav__bswap_samples(void* pSamples, drwav_uint64 sampleCount, drwav_uint32 bytesPerSample, drwav_uint16 format) +MA_PRIVATE MA_INLINE ma_bool32 ma_dr_wav_is_container_be(ma_dr_wav_container container) { - switch (format) - { - case DR_WAVE_FORMAT_PCM: - { - drwav__bswap_samples_pcm(pSamples, sampleCount, bytesPerSample); - } break; - case DR_WAVE_FORMAT_IEEE_FLOAT: - { - drwav__bswap_samples_ieee(pSamples, sampleCount, bytesPerSample); - } break; - case DR_WAVE_FORMAT_ALAW: - case DR_WAVE_FORMAT_MULAW: - { - drwav__bswap_samples_s16((drwav_int16*)pSamples, sampleCount); - } break; - case DR_WAVE_FORMAT_ADPCM: - case DR_WAVE_FORMAT_DVI_ADPCM: - default: - { - DRWAV_ASSERT(DRWAV_FALSE); - } break; + if (container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_aiff) { + return MA_TRUE; + } else { + return MA_FALSE; } } -DRWAV_PRIVATE void* drwav__malloc_default(size_t sz, void* pUserData) +MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_le(const ma_uint8* data) +{ + return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8); +} +MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_be(const ma_uint8* data) +{ + return ((ma_uint16)data[1] << 0) | ((ma_uint16)data[0] << 8); +} +MA_PRIVATE MA_INLINE ma_uint16 ma_dr_wav_bytes_to_u16_ex(const ma_uint8* data, ma_dr_wav_container container) +{ + if (ma_dr_wav_is_container_be(container)) { + return ma_dr_wav_bytes_to_u16_be(data); + } else { + return ma_dr_wav_bytes_to_u16_le(data); + } +} +MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_le(const ma_uint8* data) +{ + return ((ma_uint32)data[0] << 0) | ((ma_uint32)data[1] << 8) | ((ma_uint32)data[2] << 16) | ((ma_uint32)data[3] << 24); +} +MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_be(const ma_uint8* data) +{ + return ((ma_uint32)data[3] << 0) | ((ma_uint32)data[2] << 8) | ((ma_uint32)data[1] << 16) | ((ma_uint32)data[0] << 24); +} +MA_PRIVATE MA_INLINE ma_uint32 ma_dr_wav_bytes_to_u32_ex(const ma_uint8* data, ma_dr_wav_container container) +{ + if (ma_dr_wav_is_container_be(container)) { + return ma_dr_wav_bytes_to_u32_be(data); + } else { + return ma_dr_wav_bytes_to_u32_le(data); + } +} +MA_PRIVATE ma_int64 ma_dr_wav_aiff_extented_to_s64(const ma_uint8* data) +{ + ma_uint32 exponent = ((ma_uint32)data[0] << 8) | data[1]; + ma_uint64 hi = ((ma_uint64)data[2] << 24) | ((ma_uint64)data[3] << 16) | ((ma_uint64)data[4] << 8) | ((ma_uint64)data[5] << 0); + ma_uint64 lo = ((ma_uint64)data[6] << 24) | ((ma_uint64)data[7] << 16) | ((ma_uint64)data[8] << 8) | ((ma_uint64)data[9] << 0); + ma_uint64 significand = (hi << 32) | lo; + int sign = exponent >> 15; + exponent &= 0x7FFF; + if (exponent == 0 && significand == 0) { + return 0; + } else if (exponent == 0x7FFF) { + return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX; + } + exponent -= 16383; + if (exponent > 63) { + return sign ? MA_DR_WAV_INT64_MIN : MA_DR_WAV_INT64_MAX; + } else if (exponent < 1) { + return 0; + } + significand >>= (63 - exponent); + if (sign) { + return -(ma_int64)significand; + } else { + return (ma_int64)significand; + } +} +MA_PRIVATE void* ma_dr_wav__malloc_default(size_t sz, void* pUserData) { (void)pUserData; - return DRWAV_MALLOC(sz); + return MA_DR_WAV_MALLOC(sz); } -DRWAV_PRIVATE void* drwav__realloc_default(void* p, size_t sz, void* pUserData) +MA_PRIVATE void* ma_dr_wav__realloc_default(void* p, size_t sz, void* pUserData) { (void)pUserData; - return DRWAV_REALLOC(p, sz); + return MA_DR_WAV_REALLOC(p, sz); } -DRWAV_PRIVATE void drwav__free_default(void* p, void* pUserData) +MA_PRIVATE void ma_dr_wav__free_default(void* p, void* pUserData) { (void)pUserData; - DRWAV_FREE(p); + MA_DR_WAV_FREE(p); } -DRWAV_PRIVATE void* drwav__malloc_from_callbacks(size_t sz, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE void* ma_dr_wav__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; @@ -76974,7 +77521,7 @@ DRWAV_PRIVATE void* drwav__malloc_from_callbacks(size_t sz, const drwav_allocati } return NULL; } -DRWAV_PRIVATE void* drwav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE void* ma_dr_wav__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; @@ -76989,14 +77536,14 @@ DRWAV_PRIVATE void* drwav__realloc_from_callbacks(void* p, size_t szNew, size_t return NULL; } if (p != NULL) { - DRWAV_COPY_MEMORY(p2, p, szOld); + MA_DR_WAV_COPY_MEMORY(p2, p, szOld); pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } return p2; } return NULL; } -DRWAV_PRIVATE void drwav__free_from_callbacks(void* p, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE void ma_dr_wav__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) { if (p == NULL || pAllocationCallbacks == NULL) { return; @@ -77005,369 +77552,288 @@ DRWAV_PRIVATE void drwav__free_from_callbacks(void* p, const drwav_allocation_ca pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } } -DRWAV_PRIVATE drwav_allocation_callbacks drwav_copy_allocation_callbacks_or_defaults(const drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE ma_allocation_callbacks ma_dr_wav_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { return *pAllocationCallbacks; } else { - drwav_allocation_callbacks allocationCallbacks; + ma_allocation_callbacks allocationCallbacks; allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = drwav__malloc_default; - allocationCallbacks.onRealloc = drwav__realloc_default; - allocationCallbacks.onFree = drwav__free_default; + allocationCallbacks.onMalloc = ma_dr_wav__malloc_default; + allocationCallbacks.onRealloc = ma_dr_wav__realloc_default; + allocationCallbacks.onFree = ma_dr_wav__free_default; return allocationCallbacks; } } -static DRWAV_INLINE drwav_bool32 drwav__is_compressed_format_tag(drwav_uint16 formatTag) +static MA_INLINE ma_bool32 ma_dr_wav__is_compressed_format_tag(ma_uint16 formatTag) { return - formatTag == DR_WAVE_FORMAT_ADPCM || - formatTag == DR_WAVE_FORMAT_DVI_ADPCM; + formatTag == MA_DR_WAVE_FORMAT_ADPCM || + formatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM; } -DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_riff(drwav_uint64 chunkSize) +MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_riff(ma_uint64 chunkSize) { return (unsigned int)(chunkSize % 2); } -DRWAV_PRIVATE unsigned int drwav__chunk_padding_size_w64(drwav_uint64 chunkSize) +MA_PRIVATE unsigned int ma_dr_wav__chunk_padding_size_w64(ma_uint64 chunkSize) { return (unsigned int)(chunkSize % 8); } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 samplesToRead, drwav_int16* pBufferOut); -DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount); -DRWAV_PRIVATE drwav_result drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_chunk_header* pHeaderOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut); +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 samplesToRead, ma_int16* pBufferOut); +MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount); +MA_PRIVATE ma_result ma_dr_wav__read_chunk_header(ma_dr_wav_read_proc onRead, void* pUserData, ma_dr_wav_container container, ma_uint64* pRunningBytesReadOut, ma_dr_wav_chunk_header* pHeaderOut) { - if (container == drwav_container_riff || container == drwav_container_rf64) { - drwav_uint8 sizeInBytes[4]; + if (container == ma_dr_wav_container_riff || container == ma_dr_wav_container_rifx || container == ma_dr_wav_container_rf64 || container == ma_dr_wav_container_aiff) { + ma_uint8 sizeInBytes[4]; if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) { - return DRWAV_AT_END; + return MA_AT_END; } if (onRead(pUserData, sizeInBytes, 4) != 4) { - return DRWAV_INVALID_FILE; + return MA_INVALID_FILE; } - pHeaderOut->sizeInBytes = drwav_bytes_to_u32(sizeInBytes); - pHeaderOut->paddingSize = drwav__chunk_padding_size_riff(pHeaderOut->sizeInBytes); + pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u32_ex(sizeInBytes, container); + pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_riff(pHeaderOut->sizeInBytes); *pRunningBytesReadOut += 8; - } else { - drwav_uint8 sizeInBytes[8]; + } else if (container == ma_dr_wav_container_w64) { + ma_uint8 sizeInBytes[8]; if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) { - return DRWAV_AT_END; + return MA_AT_END; } if (onRead(pUserData, sizeInBytes, 8) != 8) { - return DRWAV_INVALID_FILE; + return MA_INVALID_FILE; } - pHeaderOut->sizeInBytes = drwav_bytes_to_u64(sizeInBytes) - 24; - pHeaderOut->paddingSize = drwav__chunk_padding_size_w64(pHeaderOut->sizeInBytes); + pHeaderOut->sizeInBytes = ma_dr_wav_bytes_to_u64(sizeInBytes) - 24; + pHeaderOut->paddingSize = ma_dr_wav__chunk_padding_size_w64(pHeaderOut->sizeInBytes); *pRunningBytesReadOut += 24; + } else { + return MA_INVALID_FILE; } - return DRWAV_SUCCESS; + return MA_SUCCESS; } -DRWAV_PRIVATE drwav_bool32 drwav__seek_forward(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData) +MA_PRIVATE ma_bool32 ma_dr_wav__seek_forward(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) { - drwav_uint64 bytesRemainingToSeek = offset; + ma_uint64 bytesRemainingToSeek = offset; while (bytesRemainingToSeek > 0) { if (bytesRemainingToSeek > 0x7FFFFFFF) { - if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { - return DRWAV_FALSE; + if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { + return MA_FALSE; } bytesRemainingToSeek -= 0x7FFFFFFF; } else { - if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) { - return DRWAV_FALSE; + if (!onSeek(pUserData, (int)bytesRemainingToSeek, ma_dr_wav_seek_origin_current)) { + return MA_FALSE; } bytesRemainingToSeek = 0; } } - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_PRIVATE drwav_bool32 drwav__seek_from_start(drwav_seek_proc onSeek, drwav_uint64 offset, void* pUserData) +MA_PRIVATE ma_bool32 ma_dr_wav__seek_from_start(ma_dr_wav_seek_proc onSeek, ma_uint64 offset, void* pUserData) { if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, drwav_seek_origin_start); + return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_start); } - if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_start)) { - return DRWAV_FALSE; + if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_start)) { + return MA_FALSE; } offset -= 0x7FFFFFFF; for (;;) { if (offset <= 0x7FFFFFFF) { - return onSeek(pUserData, (int)offset, drwav_seek_origin_current); + return onSeek(pUserData, (int)offset, ma_dr_wav_seek_origin_current); } - if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { - return DRWAV_FALSE; + if (!onSeek(pUserData, 0x7FFFFFFF, ma_dr_wav_seek_origin_current)) { + return MA_FALSE; } offset -= 0x7FFFFFFF; } } -DRWAV_PRIVATE drwav_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_container container, drwav_uint64* pRunningBytesReadOut, drwav_fmt* fmtOut) -{ - drwav_chunk_header header; - drwav_uint8 fmt[16]; - if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) { - return DRWAV_FALSE; - } - while (((container == drwav_container_riff || container == drwav_container_rf64) && !drwav_fourcc_equal(header.id.fourcc, "fmt ")) || (container == drwav_container_w64 && !drwav_guid_equal(header.id.guid, drwavGUID_W64_FMT))) { - if (!drwav__seek_forward(onSeek, header.sizeInBytes + header.paddingSize, pUserData)) { - return DRWAV_FALSE; - } - *pRunningBytesReadOut += header.sizeInBytes + header.paddingSize; - if (drwav__read_chunk_header(onRead, pUserData, container, pRunningBytesReadOut, &header) != DRWAV_SUCCESS) { - return DRWAV_FALSE; - } - } - if (container == drwav_container_riff || container == drwav_container_rf64) { - if (!drwav_fourcc_equal(header.id.fourcc, "fmt ")) { - return DRWAV_FALSE; - } - } else { - if (!drwav_guid_equal(header.id.guid, drwavGUID_W64_FMT)) { - return DRWAV_FALSE; - } - } - if (onRead(pUserData, fmt, sizeof(fmt)) != sizeof(fmt)) { - return DRWAV_FALSE; - } - *pRunningBytesReadOut += sizeof(fmt); - fmtOut->formatTag = drwav_bytes_to_u16(fmt + 0); - fmtOut->channels = drwav_bytes_to_u16(fmt + 2); - fmtOut->sampleRate = drwav_bytes_to_u32(fmt + 4); - fmtOut->avgBytesPerSec = drwav_bytes_to_u32(fmt + 8); - fmtOut->blockAlign = drwav_bytes_to_u16(fmt + 12); - fmtOut->bitsPerSample = drwav_bytes_to_u16(fmt + 14); - fmtOut->extendedSize = 0; - fmtOut->validBitsPerSample = 0; - fmtOut->channelMask = 0; - DRWAV_ZERO_MEMORY(fmtOut->subFormat, sizeof(fmtOut->subFormat)); - if (header.sizeInBytes > 16) { - drwav_uint8 fmt_cbSize[2]; - int bytesReadSoFar = 0; - if (onRead(pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) { - return DRWAV_FALSE; - } - *pRunningBytesReadOut += sizeof(fmt_cbSize); - bytesReadSoFar = 18; - fmtOut->extendedSize = drwav_bytes_to_u16(fmt_cbSize); - if (fmtOut->extendedSize > 0) { - if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) { - if (fmtOut->extendedSize != 22) { - return DRWAV_FALSE; - } - } - if (fmtOut->formatTag == DR_WAVE_FORMAT_EXTENSIBLE) { - drwav_uint8 fmtext[22]; - if (onRead(pUserData, fmtext, fmtOut->extendedSize) != fmtOut->extendedSize) { - return DRWAV_FALSE; - } - fmtOut->validBitsPerSample = drwav_bytes_to_u16(fmtext + 0); - fmtOut->channelMask = drwav_bytes_to_u32(fmtext + 2); - drwav_bytes_to_guid(fmtext + 6, fmtOut->subFormat); - } else { - if (!onSeek(pUserData, fmtOut->extendedSize, drwav_seek_origin_current)) { - return DRWAV_FALSE; - } - } - *pRunningBytesReadOut += fmtOut->extendedSize; - bytesReadSoFar += fmtOut->extendedSize; - } - if (!onSeek(pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current)) { - return DRWAV_FALSE; - } - *pRunningBytesReadOut += (header.sizeInBytes - bytesReadSoFar); - } - if (header.paddingSize > 0) { - if (!onSeek(pUserData, header.paddingSize, drwav_seek_origin_current)) { - return DRWAV_FALSE; - } - *pRunningBytesReadOut += header.paddingSize; - } - return DRWAV_TRUE; -} -DRWAV_PRIVATE size_t drwav__on_read(drwav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor) +MA_PRIVATE size_t ma_dr_wav__on_read(ma_dr_wav_read_proc onRead, void* pUserData, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor) { size_t bytesRead; - DRWAV_ASSERT(onRead != NULL); - DRWAV_ASSERT(pCursor != NULL); + MA_DR_WAV_ASSERT(onRead != NULL); + MA_DR_WAV_ASSERT(pCursor != NULL); bytesRead = onRead(pUserData, pBufferOut, bytesToRead); *pCursor += bytesRead; return bytesRead; } #if 0 -DRWAV_PRIVATE drwav_bool32 drwav__on_seek(drwav_seek_proc onSeek, void* pUserData, int offset, drwav_seek_origin origin, drwav_uint64* pCursor) +MA_PRIVATE ma_bool32 ma_dr_wav__on_seek(ma_dr_wav_seek_proc onSeek, void* pUserData, int offset, ma_dr_wav_seek_origin origin, ma_uint64* pCursor) { - DRWAV_ASSERT(onSeek != NULL); - DRWAV_ASSERT(pCursor != NULL); + MA_DR_WAV_ASSERT(onSeek != NULL); + MA_DR_WAV_ASSERT(pCursor != NULL); if (!onSeek(pUserData, offset, origin)) { - return DRWAV_FALSE; + return MA_FALSE; } - if (origin == drwav_seek_origin_start) { + if (origin == ma_dr_wav_seek_origin_start) { *pCursor = offset; } else { *pCursor += offset; } - return DRWAV_TRUE; + return MA_TRUE; } #endif -#define DRWAV_SMPL_BYTES 36 -#define DRWAV_SMPL_LOOP_BYTES 24 -#define DRWAV_INST_BYTES 7 -#define DRWAV_ACID_BYTES 24 -#define DRWAV_CUE_BYTES 4 -#define DRWAV_BEXT_BYTES 602 -#define DRWAV_BEXT_DESCRIPTION_BYTES 256 -#define DRWAV_BEXT_ORIGINATOR_NAME_BYTES 32 -#define DRWAV_BEXT_ORIGINATOR_REF_BYTES 32 -#define DRWAV_BEXT_RESERVED_BYTES 180 -#define DRWAV_BEXT_UMID_BYTES 64 -#define DRWAV_CUE_POINT_BYTES 24 -#define DRWAV_LIST_LABEL_OR_NOTE_BYTES 4 -#define DRWAV_LIST_LABELLED_TEXT_BYTES 20 -#define DRWAV_METADATA_ALIGNMENT 8 +#define MA_DR_WAV_SMPL_BYTES 36 +#define MA_DR_WAV_SMPL_LOOP_BYTES 24 +#define MA_DR_WAV_INST_BYTES 7 +#define MA_DR_WAV_ACID_BYTES 24 +#define MA_DR_WAV_CUE_BYTES 4 +#define MA_DR_WAV_BEXT_BYTES 602 +#define MA_DR_WAV_BEXT_DESCRIPTION_BYTES 256 +#define MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES 32 +#define MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES 32 +#define MA_DR_WAV_BEXT_RESERVED_BYTES 180 +#define MA_DR_WAV_BEXT_UMID_BYTES 64 +#define MA_DR_WAV_CUE_POINT_BYTES 24 +#define MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES 4 +#define MA_DR_WAV_LIST_LABELLED_TEXT_BYTES 20 +#define MA_DR_WAV_METADATA_ALIGNMENT 8 typedef enum { - drwav__metadata_parser_stage_count, - drwav__metadata_parser_stage_read -} drwav__metadata_parser_stage; + ma_dr_wav__metadata_parser_stage_count, + ma_dr_wav__metadata_parser_stage_read +} ma_dr_wav__metadata_parser_stage; typedef struct { - drwav_read_proc onRead; - drwav_seek_proc onSeek; + ma_dr_wav_read_proc onRead; + ma_dr_wav_seek_proc onSeek; void *pReadSeekUserData; - drwav__metadata_parser_stage stage; - drwav_metadata *pMetadata; - drwav_uint32 metadataCount; - drwav_uint8 *pData; - drwav_uint8 *pDataCursor; - drwav_uint64 metadataCursor; - drwav_uint64 extraCapacity; -} drwav__metadata_parser; -DRWAV_PRIVATE size_t drwav__metadata_memory_capacity(drwav__metadata_parser* pParser) + ma_dr_wav__metadata_parser_stage stage; + ma_dr_wav_metadata *pMetadata; + ma_uint32 metadataCount; + ma_uint8 *pData; + ma_uint8 *pDataCursor; + ma_uint64 metadataCursor; + ma_uint64 extraCapacity; +} ma_dr_wav__metadata_parser; +MA_PRIVATE size_t ma_dr_wav__metadata_memory_capacity(ma_dr_wav__metadata_parser* pParser) { - drwav_uint64 cap = sizeof(drwav_metadata) * (drwav_uint64)pParser->metadataCount + pParser->extraCapacity; - if (cap > DRWAV_SIZE_MAX) { + ma_uint64 cap = sizeof(ma_dr_wav_metadata) * (ma_uint64)pParser->metadataCount + pParser->extraCapacity; + if (cap > MA_SIZE_MAX) { return 0; } return (size_t)cap; } -DRWAV_PRIVATE drwav_uint8* drwav__metadata_get_memory(drwav__metadata_parser* pParser, size_t size, size_t align) +MA_PRIVATE ma_uint8* ma_dr_wav__metadata_get_memory(ma_dr_wav__metadata_parser* pParser, size_t size, size_t align) { - drwav_uint8* pResult; + ma_uint8* pResult; if (align) { - drwav_uintptr modulo = (drwav_uintptr)pParser->pDataCursor % align; + ma_uintptr modulo = (ma_uintptr)pParser->pDataCursor % align; if (modulo != 0) { pParser->pDataCursor += align - modulo; } } pResult = pParser->pDataCursor; - DRWAV_ASSERT((pResult + size) <= (pParser->pData + drwav__metadata_memory_capacity(pParser))); + MA_DR_WAV_ASSERT((pResult + size) <= (pParser->pData + ma_dr_wav__metadata_memory_capacity(pParser))); pParser->pDataCursor += size; return pResult; } -DRWAV_PRIVATE void drwav__metadata_request_extra_memory_for_stage_2(drwav__metadata_parser* pParser, size_t bytes, size_t align) +MA_PRIVATE void ma_dr_wav__metadata_request_extra_memory_for_stage_2(ma_dr_wav__metadata_parser* pParser, size_t bytes, size_t align) { size_t extra = bytes + (align ? (align - 1) : 0); pParser->extraCapacity += extra; } -DRWAV_PRIVATE drwav_result drwav__metadata_alloc(drwav__metadata_parser* pParser, drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE ma_result ma_dr_wav__metadata_alloc(ma_dr_wav__metadata_parser* pParser, ma_allocation_callbacks* pAllocationCallbacks) { if (pParser->extraCapacity != 0 || pParser->metadataCount != 0) { pAllocationCallbacks->onFree(pParser->pData, pAllocationCallbacks->pUserData); - pParser->pData = (drwav_uint8*)pAllocationCallbacks->onMalloc(drwav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData); + pParser->pData = (ma_uint8*)pAllocationCallbacks->onMalloc(ma_dr_wav__metadata_memory_capacity(pParser), pAllocationCallbacks->pUserData); pParser->pDataCursor = pParser->pData; if (pParser->pData == NULL) { - return DRWAV_OUT_OF_MEMORY; + return MA_OUT_OF_MEMORY; } - pParser->pMetadata = (drwav_metadata*)drwav__metadata_get_memory(pParser, sizeof(drwav_metadata) * pParser->metadataCount, 1); + pParser->pMetadata = (ma_dr_wav_metadata*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_metadata) * pParser->metadataCount, 1); pParser->metadataCursor = 0; } - return DRWAV_SUCCESS; + return MA_SUCCESS; } -DRWAV_PRIVATE size_t drwav__metadata_parser_read(drwav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, drwav_uint64* pCursor) +MA_PRIVATE size_t ma_dr_wav__metadata_parser_read(ma_dr_wav__metadata_parser* pParser, void* pBufferOut, size_t bytesToRead, ma_uint64* pCursor) { if (pCursor != NULL) { - return drwav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor); + return ma_dr_wav__on_read(pParser->onRead, pParser->pReadSeekUserData, pBufferOut, bytesToRead, pCursor); } else { return pParser->onRead(pParser->pReadSeekUserData, pBufferOut, bytesToRead); } } -DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata) +MA_PRIVATE ma_uint64 ma_dr_wav__read_smpl_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata) { - drwav_uint8 smplHeaderData[DRWAV_SMPL_BYTES]; - drwav_uint64 totalBytesRead = 0; + ma_uint8 smplHeaderData[MA_DR_WAV_SMPL_BYTES]; + ma_uint64 totalBytesRead = 0; size_t bytesJustRead; if (pMetadata == NULL) { return 0; } - bytesJustRead = drwav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); - DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); - DRWAV_ASSERT(pChunkHeader != NULL); + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); + MA_DR_WAV_ASSERT(pChunkHeader != NULL); if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) { - drwav_uint32 iSampleLoop; - pMetadata->type = drwav_metadata_type_smpl; - pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0); - pMetadata->data.smpl.productId = drwav_bytes_to_u32(smplHeaderData + 4); - pMetadata->data.smpl.samplePeriodNanoseconds = drwav_bytes_to_u32(smplHeaderData + 8); - pMetadata->data.smpl.midiUnityNote = drwav_bytes_to_u32(smplHeaderData + 12); - pMetadata->data.smpl.midiPitchFraction = drwav_bytes_to_u32(smplHeaderData + 16); - pMetadata->data.smpl.smpteFormat = drwav_bytes_to_u32(smplHeaderData + 20); - pMetadata->data.smpl.smpteOffset = drwav_bytes_to_u32(smplHeaderData + 24); - pMetadata->data.smpl.sampleLoopCount = drwav_bytes_to_u32(smplHeaderData + 28); - pMetadata->data.smpl.samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(smplHeaderData + 32); - if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES) { - pMetadata->data.smpl.pLoops = (drwav_smpl_loop*)drwav__metadata_get_memory(pParser, sizeof(drwav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, DRWAV_METADATA_ALIGNMENT); + ma_uint32 iSampleLoop; + pMetadata->type = ma_dr_wav_metadata_type_smpl; + pMetadata->data.smpl.manufacturerId = ma_dr_wav_bytes_to_u32(smplHeaderData + 0); + pMetadata->data.smpl.productId = ma_dr_wav_bytes_to_u32(smplHeaderData + 4); + pMetadata->data.smpl.samplePeriodNanoseconds = ma_dr_wav_bytes_to_u32(smplHeaderData + 8); + pMetadata->data.smpl.midiUnityNote = ma_dr_wav_bytes_to_u32(smplHeaderData + 12); + pMetadata->data.smpl.midiPitchFraction = ma_dr_wav_bytes_to_u32(smplHeaderData + 16); + pMetadata->data.smpl.smpteFormat = ma_dr_wav_bytes_to_u32(smplHeaderData + 20); + pMetadata->data.smpl.smpteOffset = ma_dr_wav_bytes_to_u32(smplHeaderData + 24); + pMetadata->data.smpl.sampleLoopCount = ma_dr_wav_bytes_to_u32(smplHeaderData + 28); + pMetadata->data.smpl.samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(smplHeaderData + 32); + if (pMetadata->data.smpl.sampleLoopCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES) { + pMetadata->data.smpl.pLoops = (ma_dr_wav_smpl_loop*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_smpl_loop) * pMetadata->data.smpl.sampleLoopCount, MA_DR_WAV_METADATA_ALIGNMENT); for (iSampleLoop = 0; iSampleLoop < pMetadata->data.smpl.sampleLoopCount; ++iSampleLoop) { - drwav_uint8 smplLoopData[DRWAV_SMPL_LOOP_BYTES]; - bytesJustRead = drwav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); + ma_uint8 smplLoopData[MA_DR_WAV_SMPL_LOOP_BYTES]; + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, smplLoopData, sizeof(smplLoopData), &totalBytesRead); if (bytesJustRead == sizeof(smplLoopData)) { - pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = drwav_bytes_to_u32(smplLoopData + 0); - pMetadata->data.smpl.pLoops[iSampleLoop].type = drwav_bytes_to_u32(smplLoopData + 4); - pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 8); - pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = drwav_bytes_to_u32(smplLoopData + 12); - pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = drwav_bytes_to_u32(smplLoopData + 16); - pMetadata->data.smpl.pLoops[iSampleLoop].playCount = drwav_bytes_to_u32(smplLoopData + 20); + pMetadata->data.smpl.pLoops[iSampleLoop].cuePointId = ma_dr_wav_bytes_to_u32(smplLoopData + 0); + pMetadata->data.smpl.pLoops[iSampleLoop].type = ma_dr_wav_bytes_to_u32(smplLoopData + 4); + pMetadata->data.smpl.pLoops[iSampleLoop].firstSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 8); + pMetadata->data.smpl.pLoops[iSampleLoop].lastSampleByteOffset = ma_dr_wav_bytes_to_u32(smplLoopData + 12); + pMetadata->data.smpl.pLoops[iSampleLoop].sampleFraction = ma_dr_wav_bytes_to_u32(smplLoopData + 16); + pMetadata->data.smpl.pLoops[iSampleLoop].playCount = ma_dr_wav_bytes_to_u32(smplLoopData + 20); } else { break; } } if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { - pMetadata->data.smpl.pSamplerSpecificData = drwav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1); - DRWAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL); - drwav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead); + pMetadata->data.smpl.pSamplerSpecificData = ma_dr_wav__metadata_get_memory(pParser, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, 1); + MA_DR_WAV_ASSERT(pMetadata->data.smpl.pSamplerSpecificData != NULL); + ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes, &totalBytesRead); } } } return totalBytesRead; } -DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata* pMetadata) +MA_PRIVATE ma_uint64 ma_dr_wav__read_cue_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata* pMetadata) { - drwav_uint8 cueHeaderSectionData[DRWAV_CUE_BYTES]; - drwav_uint64 totalBytesRead = 0; + ma_uint8 cueHeaderSectionData[MA_DR_WAV_CUE_BYTES]; + ma_uint64 totalBytesRead = 0; size_t bytesJustRead; if (pMetadata == NULL) { return 0; } - bytesJustRead = drwav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); - DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); if (bytesJustRead == sizeof(cueHeaderSectionData)) { - pMetadata->type = drwav_metadata_type_cue; - pMetadata->data.cue.cuePointCount = drwav_bytes_to_u32(cueHeaderSectionData); - if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES) { - pMetadata->data.cue.pCuePoints = (drwav_cue_point*)drwav__metadata_get_memory(pParser, sizeof(drwav_cue_point) * pMetadata->data.cue.cuePointCount, DRWAV_METADATA_ALIGNMENT); - DRWAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL); + pMetadata->type = ma_dr_wav_metadata_type_cue; + pMetadata->data.cue.cuePointCount = ma_dr_wav_bytes_to_u32(cueHeaderSectionData); + if (pMetadata->data.cue.cuePointCount == (pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES) { + pMetadata->data.cue.pCuePoints = (ma_dr_wav_cue_point*)ma_dr_wav__metadata_get_memory(pParser, sizeof(ma_dr_wav_cue_point) * pMetadata->data.cue.cuePointCount, MA_DR_WAV_METADATA_ALIGNMENT); + MA_DR_WAV_ASSERT(pMetadata->data.cue.pCuePoints != NULL); if (pMetadata->data.cue.cuePointCount > 0) { - drwav_uint32 iCuePoint; + ma_uint32 iCuePoint; for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { - drwav_uint8 cuePointData[DRWAV_CUE_POINT_BYTES]; - bytesJustRead = drwav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead); + ma_uint8 cuePointData[MA_DR_WAV_CUE_POINT_BYTES]; + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cuePointData, sizeof(cuePointData), &totalBytesRead); if (bytesJustRead == sizeof(cuePointData)) { - pMetadata->data.cue.pCuePoints[iCuePoint].id = drwav_bytes_to_u32(cuePointData + 0); - pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = drwav_bytes_to_u32(cuePointData + 4); + pMetadata->data.cue.pCuePoints[iCuePoint].id = ma_dr_wav_bytes_to_u32(cuePointData + 0); + pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition = ma_dr_wav_bytes_to_u32(cuePointData + 4); pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[0] = cuePointData[8]; pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[1] = cuePointData[9]; pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[2] = cuePointData[10]; pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId[3] = cuePointData[11]; - pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = drwav_bytes_to_u32(cuePointData + 12); - pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = drwav_bytes_to_u32(cuePointData + 16); - pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = drwav_bytes_to_u32(cuePointData + 20); + pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart = ma_dr_wav_bytes_to_u32(cuePointData + 12); + pMetadata->data.cue.pCuePoints[iCuePoint].blockStart = ma_dr_wav_bytes_to_u32(cuePointData + 16); + pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset = ma_dr_wav_bytes_to_u32(cuePointData + 20); } else { break; } @@ -77377,50 +77843,50 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parse } return totalBytesRead; } -DRWAV_PRIVATE drwav_uint64 drwav__read_inst_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) +MA_PRIVATE ma_uint64 ma_dr_wav__read_inst_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata) { - drwav_uint8 instData[DRWAV_INST_BYTES]; - drwav_uint64 bytesRead; + ma_uint8 instData[MA_DR_WAV_INST_BYTES]; + ma_uint64 bytesRead; if (pMetadata == NULL) { return 0; } - bytesRead = drwav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); - DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); + bytesRead = ma_dr_wav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); if (bytesRead == sizeof(instData)) { - pMetadata->type = drwav_metadata_type_inst; - pMetadata->data.inst.midiUnityNote = (drwav_int8)instData[0]; - pMetadata->data.inst.fineTuneCents = (drwav_int8)instData[1]; - pMetadata->data.inst.gainDecibels = (drwav_int8)instData[2]; - pMetadata->data.inst.lowNote = (drwav_int8)instData[3]; - pMetadata->data.inst.highNote = (drwav_int8)instData[4]; - pMetadata->data.inst.lowVelocity = (drwav_int8)instData[5]; - pMetadata->data.inst.highVelocity = (drwav_int8)instData[6]; + pMetadata->type = ma_dr_wav_metadata_type_inst; + pMetadata->data.inst.midiUnityNote = (ma_int8)instData[0]; + pMetadata->data.inst.fineTuneCents = (ma_int8)instData[1]; + pMetadata->data.inst.gainDecibels = (ma_int8)instData[2]; + pMetadata->data.inst.lowNote = (ma_int8)instData[3]; + pMetadata->data.inst.highNote = (ma_int8)instData[4]; + pMetadata->data.inst.lowVelocity = (ma_int8)instData[5]; + pMetadata->data.inst.highVelocity = (ma_int8)instData[6]; } return bytesRead; } -DRWAV_PRIVATE drwav_uint64 drwav__read_acid_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) +MA_PRIVATE ma_uint64 ma_dr_wav__read_acid_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata) { - drwav_uint8 acidData[DRWAV_ACID_BYTES]; - drwav_uint64 bytesRead; + ma_uint8 acidData[MA_DR_WAV_ACID_BYTES]; + ma_uint64 bytesRead; if (pMetadata == NULL) { return 0; } - bytesRead = drwav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); - DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); + bytesRead = ma_dr_wav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); if (bytesRead == sizeof(acidData)) { - pMetadata->type = drwav_metadata_type_acid; - pMetadata->data.acid.flags = drwav_bytes_to_u32(acidData + 0); - pMetadata->data.acid.midiUnityNote = drwav_bytes_to_u16(acidData + 4); - pMetadata->data.acid.reserved1 = drwav_bytes_to_u16(acidData + 6); - pMetadata->data.acid.reserved2 = drwav_bytes_to_f32(acidData + 8); - pMetadata->data.acid.numBeats = drwav_bytes_to_u32(acidData + 12); - pMetadata->data.acid.meterDenominator = drwav_bytes_to_u16(acidData + 16); - pMetadata->data.acid.meterNumerator = drwav_bytes_to_u16(acidData + 18); - pMetadata->data.acid.tempo = drwav_bytes_to_f32(acidData + 20); + pMetadata->type = ma_dr_wav_metadata_type_acid; + pMetadata->data.acid.flags = ma_dr_wav_bytes_to_u32(acidData + 0); + pMetadata->data.acid.midiUnityNote = ma_dr_wav_bytes_to_u16(acidData + 4); + pMetadata->data.acid.reserved1 = ma_dr_wav_bytes_to_u16(acidData + 6); + pMetadata->data.acid.reserved2 = ma_dr_wav_bytes_to_f32(acidData + 8); + pMetadata->data.acid.numBeats = ma_dr_wav_bytes_to_u32(acidData + 12); + pMetadata->data.acid.meterDenominator = ma_dr_wav_bytes_to_u16(acidData + 16); + pMetadata->data.acid.meterNumerator = ma_dr_wav_bytes_to_u16(acidData + 18); + pMetadata->data.acid.tempo = ma_dr_wav_bytes_to_f32(acidData + 20); } return bytesRead; } -DRWAV_PRIVATE size_t drwav__strlen(const char* str) +MA_PRIVATE size_t ma_dr_wav__strlen(const char* str) { size_t result = 0; while (*str++) { @@ -77428,7 +77894,7 @@ DRWAV_PRIVATE size_t drwav__strlen(const char* str) } return result; } -DRWAV_PRIVATE size_t drwav__strlen_clamped(const char* str, size_t maxToRead) +MA_PRIVATE size_t ma_dr_wav__strlen_clamped(const char* str, size_t maxToRead) { size_t result = 0; while (*str++ && result < maxToRead) { @@ -77436,13 +77902,13 @@ DRWAV_PRIVATE size_t drwav__strlen_clamped(const char* str, size_t maxToRead) } return result; } -DRWAV_PRIVATE char* drwav__metadata_copy_string(drwav__metadata_parser* pParser, const char* str, size_t maxToRead) +MA_PRIVATE char* ma_dr_wav__metadata_copy_string(ma_dr_wav__metadata_parser* pParser, const char* str, size_t maxToRead) { - size_t len = drwav__strlen_clamped(str, maxToRead); + size_t len = ma_dr_wav__strlen_clamped(str, maxToRead); if (len) { - char* result = (char*)drwav__metadata_get_memory(pParser, len + 1, 1); - DRWAV_ASSERT(result != NULL); - DRWAV_COPY_MEMORY(result, str, len); + char* result = (char*)ma_dr_wav__metadata_get_memory(pParser, len + 1, 1); + MA_DR_WAV_ASSERT(result != NULL); + MA_DR_WAV_COPY_MEMORY(result, str, len); result[len] = '\0'; return result; } else { @@ -77454,36 +77920,36 @@ typedef struct const void* pBuffer; size_t sizeInBytes; size_t cursor; -} drwav_buffer_reader; -DRWAV_PRIVATE drwav_result drwav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, drwav_buffer_reader* pReader) +} ma_dr_wav_buffer_reader; +MA_PRIVATE ma_result ma_dr_wav_buffer_reader_init(const void* pBuffer, size_t sizeInBytes, ma_dr_wav_buffer_reader* pReader) { - DRWAV_ASSERT(pBuffer != NULL); - DRWAV_ASSERT(pReader != NULL); - DRWAV_ZERO_OBJECT(pReader); + MA_DR_WAV_ASSERT(pBuffer != NULL); + MA_DR_WAV_ASSERT(pReader != NULL); + MA_DR_WAV_ZERO_OBJECT(pReader); pReader->pBuffer = pBuffer; pReader->sizeInBytes = sizeInBytes; pReader->cursor = 0; - return DRWAV_SUCCESS; + return MA_SUCCESS; } -DRWAV_PRIVATE const void* drwav_buffer_reader_ptr(const drwav_buffer_reader* pReader) +MA_PRIVATE const void* ma_dr_wav_buffer_reader_ptr(const ma_dr_wav_buffer_reader* pReader) { - DRWAV_ASSERT(pReader != NULL); - return drwav_offset_ptr(pReader->pBuffer, pReader->cursor); + MA_DR_WAV_ASSERT(pReader != NULL); + return ma_dr_wav_offset_ptr(pReader->pBuffer, pReader->cursor); } -DRWAV_PRIVATE drwav_result drwav_buffer_reader_seek(drwav_buffer_reader* pReader, size_t bytesToSeek) +MA_PRIVATE ma_result ma_dr_wav_buffer_reader_seek(ma_dr_wav_buffer_reader* pReader, size_t bytesToSeek) { - DRWAV_ASSERT(pReader != NULL); + MA_DR_WAV_ASSERT(pReader != NULL); if (pReader->cursor + bytesToSeek > pReader->sizeInBytes) { - return DRWAV_BAD_SEEK; + return MA_BAD_SEEK; } pReader->cursor += bytesToSeek; - return DRWAV_SUCCESS; + return MA_SUCCESS; } -DRWAV_PRIVATE drwav_result drwav_buffer_reader_read(drwav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead) +MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read(ma_dr_wav_buffer_reader* pReader, void* pDst, size_t bytesToRead, size_t* pBytesRead) { - drwav_result result = DRWAV_SUCCESS; + ma_result result = MA_SUCCESS; size_t bytesRemaining; - DRWAV_ASSERT(pReader != NULL); + MA_DR_WAV_ASSERT(pReader != NULL); if (pBytesRead != NULL) { *pBytesRead = 0; } @@ -77492,87 +77958,87 @@ DRWAV_PRIVATE drwav_result drwav_buffer_reader_read(drwav_buffer_reader* pReader bytesToRead = bytesRemaining; } if (pDst == NULL) { - result = drwav_buffer_reader_seek(pReader, bytesToRead); + result = ma_dr_wav_buffer_reader_seek(pReader, bytesToRead); } else { - DRWAV_COPY_MEMORY(pDst, drwav_buffer_reader_ptr(pReader), bytesToRead); + MA_DR_WAV_COPY_MEMORY(pDst, ma_dr_wav_buffer_reader_ptr(pReader), bytesToRead); pReader->cursor += bytesToRead; } - DRWAV_ASSERT(pReader->cursor <= pReader->sizeInBytes); - if (result == DRWAV_SUCCESS) { + MA_DR_WAV_ASSERT(pReader->cursor <= pReader->sizeInBytes); + if (result == MA_SUCCESS) { if (pBytesRead != NULL) { *pBytesRead = bytesToRead; } } - return DRWAV_SUCCESS; + return MA_SUCCESS; } -DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u16(drwav_buffer_reader* pReader, drwav_uint16* pDst) +MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u16(ma_dr_wav_buffer_reader* pReader, ma_uint16* pDst) { - drwav_result result; + ma_result result; size_t bytesRead; - drwav_uint8 data[2]; - DRWAV_ASSERT(pReader != NULL); - DRWAV_ASSERT(pDst != NULL); + ma_uint8 data[2]; + MA_DR_WAV_ASSERT(pReader != NULL); + MA_DR_WAV_ASSERT(pDst != NULL); *pDst = 0; - result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); - if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) { + result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); + if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) { return result; } - *pDst = drwav_bytes_to_u16(data); - return DRWAV_SUCCESS; + *pDst = ma_dr_wav_bytes_to_u16(data); + return MA_SUCCESS; } -DRWAV_PRIVATE drwav_result drwav_buffer_reader_read_u32(drwav_buffer_reader* pReader, drwav_uint32* pDst) +MA_PRIVATE ma_result ma_dr_wav_buffer_reader_read_u32(ma_dr_wav_buffer_reader* pReader, ma_uint32* pDst) { - drwav_result result; + ma_result result; size_t bytesRead; - drwav_uint8 data[4]; - DRWAV_ASSERT(pReader != NULL); - DRWAV_ASSERT(pDst != NULL); + ma_uint8 data[4]; + MA_DR_WAV_ASSERT(pReader != NULL); + MA_DR_WAV_ASSERT(pDst != NULL); *pDst = 0; - result = drwav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); - if (result != DRWAV_SUCCESS || bytesRead != sizeof(*pDst)) { + result = ma_dr_wav_buffer_reader_read(pReader, data, sizeof(*pDst), &bytesRead); + if (result != MA_SUCCESS || bytesRead != sizeof(*pDst)) { return result; } - *pDst = drwav_bytes_to_u32(data); - return DRWAV_SUCCESS; + *pDst = ma_dr_wav_bytes_to_u32(data); + return MA_SUCCESS; } -DRWAV_PRIVATE drwav_uint64 drwav__read_bext_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize) +MA_PRIVATE ma_uint64 ma_dr_wav__read_bext_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize) { - drwav_uint8 bextData[DRWAV_BEXT_BYTES]; - size_t bytesRead = drwav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); - DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); + ma_uint8 bextData[MA_DR_WAV_BEXT_BYTES]; + size_t bytesRead = ma_dr_wav__metadata_parser_read(pParser, bextData, sizeof(bextData), NULL); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); if (bytesRead == sizeof(bextData)) { - drwav_buffer_reader reader; - drwav_uint32 timeReferenceLow; - drwav_uint32 timeReferenceHigh; + ma_dr_wav_buffer_reader reader; + ma_uint32 timeReferenceLow; + ma_uint32 timeReferenceHigh; size_t extraBytes; - pMetadata->type = drwav_metadata_type_bext; - if (drwav_buffer_reader_init(bextData, bytesRead, &reader) == DRWAV_SUCCESS) { - pMetadata->data.bext.pDescription = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_DESCRIPTION_BYTES); - drwav_buffer_reader_seek(&reader, DRWAV_BEXT_DESCRIPTION_BYTES); - pMetadata->data.bext.pOriginatorName = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_NAME_BYTES); - drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_NAME_BYTES); - pMetadata->data.bext.pOriginatorReference = drwav__metadata_copy_string(pParser, (const char*)drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_ORIGINATOR_REF_BYTES); - drwav_buffer_reader_seek(&reader, DRWAV_BEXT_ORIGINATOR_REF_BYTES); - drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL); - drwav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL); - drwav_buffer_reader_read_u32(&reader, &timeReferenceLow); - drwav_buffer_reader_read_u32(&reader, &timeReferenceHigh); - pMetadata->data.bext.timeReference = ((drwav_uint64)timeReferenceHigh << 32) + timeReferenceLow; - drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version); - pMetadata->data.bext.pUMID = drwav__metadata_get_memory(pParser, DRWAV_BEXT_UMID_BYTES, 1); - drwav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES, NULL); - drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue); - drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange); - drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel); - drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness); - drwav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness); - DRWAV_ASSERT((drwav_offset_ptr(drwav_buffer_reader_ptr(&reader), DRWAV_BEXT_RESERVED_BYTES)) == (bextData + DRWAV_BEXT_BYTES)); - extraBytes = (size_t)(chunkSize - DRWAV_BEXT_BYTES); + pMetadata->type = ma_dr_wav_metadata_type_bext; + if (ma_dr_wav_buffer_reader_init(bextData, bytesRead, &reader) == MA_SUCCESS) { + pMetadata->data.bext.pDescription = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_DESCRIPTION_BYTES); + ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_DESCRIPTION_BYTES); + pMetadata->data.bext.pOriginatorName = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); + ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); + pMetadata->data.bext.pOriginatorReference = ma_dr_wav__metadata_copy_string(pParser, (const char*)ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); + ma_dr_wav_buffer_reader_seek(&reader, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); + ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate), NULL); + ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime), NULL); + ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceLow); + ma_dr_wav_buffer_reader_read_u32(&reader, &timeReferenceHigh); + pMetadata->data.bext.timeReference = ((ma_uint64)timeReferenceHigh << 32) + timeReferenceLow; + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.version); + pMetadata->data.bext.pUMID = ma_dr_wav__metadata_get_memory(pParser, MA_DR_WAV_BEXT_UMID_BYTES, 1); + ma_dr_wav_buffer_reader_read(&reader, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES, NULL); + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessValue); + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.loudnessRange); + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxTruePeakLevel); + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxMomentaryLoudness); + ma_dr_wav_buffer_reader_read_u16(&reader, &pMetadata->data.bext.maxShortTermLoudness); + MA_DR_WAV_ASSERT((ma_dr_wav_offset_ptr(ma_dr_wav_buffer_reader_ptr(&reader), MA_DR_WAV_BEXT_RESERVED_BYTES)) == (bextData + MA_DR_WAV_BEXT_BYTES)); + extraBytes = (size_t)(chunkSize - MA_DR_WAV_BEXT_BYTES); if (extraBytes > 0) { - pMetadata->data.bext.pCodingHistory = (char*)drwav__metadata_get_memory(pParser, extraBytes + 1, 1); - DRWAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL); - bytesRead += drwav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL); - pMetadata->data.bext.codingHistorySize = (drwav_uint32)drwav__strlen(pMetadata->data.bext.pCodingHistory); + pMetadata->data.bext.pCodingHistory = (char*)ma_dr_wav__metadata_get_memory(pParser, extraBytes + 1, 1); + MA_DR_WAV_ASSERT(pMetadata->data.bext.pCodingHistory != NULL); + bytesRead += ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.bext.pCodingHistory, extraBytes, NULL); + pMetadata->data.bext.codingHistorySize = (ma_uint32)ma_dr_wav__strlen(pMetadata->data.bext.pCodingHistory); } else { pMetadata->data.bext.pCodingHistory = NULL; pMetadata->data.bext.codingHistorySize = 0; @@ -77581,22 +78047,22 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_bext_to_metadata_obj(drwav__metadata_pars } return bytesRead; } -DRWAV_PRIVATE drwav_uint64 drwav__read_list_label_or_note_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize, drwav_metadata_type type) +MA_PRIVATE ma_uint64 ma_dr_wav__read_list_label_or_note_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize, ma_dr_wav_metadata_type type) { - drwav_uint8 cueIDBuffer[DRWAV_LIST_LABEL_OR_NOTE_BYTES]; - drwav_uint64 totalBytesRead = 0; - size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead); - DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); + ma_uint8 cueIDBuffer[MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES]; + ma_uint64 totalBytesRead = 0; + size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, cueIDBuffer, sizeof(cueIDBuffer), &totalBytesRead); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); if (bytesJustRead == sizeof(cueIDBuffer)) { - drwav_uint32 sizeIncludingNullTerminator; + ma_uint32 sizeIncludingNullTerminator; pMetadata->type = type; - pMetadata->data.labelOrNote.cuePointId = drwav_bytes_to_u32(cueIDBuffer); - sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES; + pMetadata->data.labelOrNote.cuePointId = ma_dr_wav_bytes_to_u32(cueIDBuffer); + sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; if (sizeIncludingNullTerminator > 0) { pMetadata->data.labelOrNote.stringLength = sizeIncludingNullTerminator - 1; - pMetadata->data.labelOrNote.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); - DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); - drwav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead); + pMetadata->data.labelOrNote.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); + MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); + ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelOrNote.pString, sizeIncludingNullTerminator, &totalBytesRead); } else { pMetadata->data.labelOrNote.stringLength = 0; pMetadata->data.labelOrNote.pString = NULL; @@ -77604,31 +78070,31 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_list_label_or_note_to_metadata_obj(drwav_ } return totalBytesRead; } -DRWAV_PRIVATE drwav_uint64 drwav__read_list_labelled_cue_region_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata, drwav_uint64 chunkSize) +MA_PRIVATE ma_uint64 ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(ma_dr_wav__metadata_parser* pParser, ma_dr_wav_metadata* pMetadata, ma_uint64 chunkSize) { - drwav_uint8 buffer[DRWAV_LIST_LABELLED_TEXT_BYTES]; - drwav_uint64 totalBytesRead = 0; - size_t bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead); - DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); + ma_uint8 buffer[MA_DR_WAV_LIST_LABELLED_TEXT_BYTES]; + ma_uint64 totalBytesRead = 0; + size_t bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &totalBytesRead); + MA_DR_WAV_ASSERT(pParser->stage == ma_dr_wav__metadata_parser_stage_read); if (bytesJustRead == sizeof(buffer)) { - drwav_uint32 sizeIncludingNullTerminator; - pMetadata->type = drwav_metadata_type_list_labelled_cue_region; - pMetadata->data.labelledCueRegion.cuePointId = drwav_bytes_to_u32(buffer + 0); - pMetadata->data.labelledCueRegion.sampleLength = drwav_bytes_to_u32(buffer + 4); + ma_uint32 sizeIncludingNullTerminator; + pMetadata->type = ma_dr_wav_metadata_type_list_labelled_cue_region; + pMetadata->data.labelledCueRegion.cuePointId = ma_dr_wav_bytes_to_u32(buffer + 0); + pMetadata->data.labelledCueRegion.sampleLength = ma_dr_wav_bytes_to_u32(buffer + 4); pMetadata->data.labelledCueRegion.purposeId[0] = buffer[8]; pMetadata->data.labelledCueRegion.purposeId[1] = buffer[9]; pMetadata->data.labelledCueRegion.purposeId[2] = buffer[10]; pMetadata->data.labelledCueRegion.purposeId[3] = buffer[11]; - pMetadata->data.labelledCueRegion.country = drwav_bytes_to_u16(buffer + 12); - pMetadata->data.labelledCueRegion.language = drwav_bytes_to_u16(buffer + 14); - pMetadata->data.labelledCueRegion.dialect = drwav_bytes_to_u16(buffer + 16); - pMetadata->data.labelledCueRegion.codePage = drwav_bytes_to_u16(buffer + 18); - sizeIncludingNullTerminator = (drwav_uint32)chunkSize - DRWAV_LIST_LABELLED_TEXT_BYTES; + pMetadata->data.labelledCueRegion.country = ma_dr_wav_bytes_to_u16(buffer + 12); + pMetadata->data.labelledCueRegion.language = ma_dr_wav_bytes_to_u16(buffer + 14); + pMetadata->data.labelledCueRegion.dialect = ma_dr_wav_bytes_to_u16(buffer + 16); + pMetadata->data.labelledCueRegion.codePage = ma_dr_wav_bytes_to_u16(buffer + 18); + sizeIncludingNullTerminator = (ma_uint32)chunkSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; if (sizeIncludingNullTerminator > 0) { pMetadata->data.labelledCueRegion.stringLength = sizeIncludingNullTerminator - 1; - pMetadata->data.labelledCueRegion.pString = (char*)drwav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); - DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); - drwav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead); + pMetadata->data.labelledCueRegion.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, sizeIncludingNullTerminator, 1); + MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); + ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.labelledCueRegion.pString, sizeIncludingNullTerminator, &totalBytesRead); } else { pMetadata->data.labelledCueRegion.stringLength = 0; pMetadata->data.labelledCueRegion.pString = NULL; @@ -77636,21 +78102,21 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_list_labelled_cue_region_to_metadata_obj( } return totalBytesRead; } -DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_info_text_chunk(drwav__metadata_parser* pParser, drwav_uint64 chunkSize, drwav_metadata_type type) +MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_info_text_chunk(ma_dr_wav__metadata_parser* pParser, ma_uint64 chunkSize, ma_dr_wav_metadata_type type) { - drwav_uint64 bytesRead = 0; - drwav_uint32 stringSizeWithNullTerminator = (drwav_uint32)chunkSize; - if (pParser->stage == drwav__metadata_parser_stage_count) { + ma_uint64 bytesRead = 0; + ma_uint32 stringSizeWithNullTerminator = (ma_uint32)chunkSize; + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { pParser->metadataCount += 1; - drwav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1); + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, stringSizeWithNullTerminator, 1); } else { - drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; + ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; pMetadata->type = type; if (stringSizeWithNullTerminator > 0) { pMetadata->data.infoText.stringLength = stringSizeWithNullTerminator - 1; - pMetadata->data.infoText.pString = (char*)drwav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1); - DRWAV_ASSERT(pMetadata->data.infoText.pString != NULL); - bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL); + pMetadata->data.infoText.pString = (char*)ma_dr_wav__metadata_get_memory(pParser, stringSizeWithNullTerminator, 1); + MA_DR_WAV_ASSERT(pMetadata->data.infoText.pString != NULL); + bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.infoText.pString, (size_t)stringSizeWithNullTerminator, NULL); if (bytesRead == chunkSize) { pParser->metadataCursor += 1; } else { @@ -77663,30 +78129,30 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_info_text_chunk(drwav__metada } return bytesRead; } -DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_unknown_chunk(drwav__metadata_parser* pParser, const drwav_uint8* pChunkId, drwav_uint64 chunkSize, drwav_metadata_location location) +MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_unknown_chunk(ma_dr_wav__metadata_parser* pParser, const ma_uint8* pChunkId, ma_uint64 chunkSize, ma_dr_wav_metadata_location location) { - drwav_uint64 bytesRead = 0; - if (location == drwav_metadata_location_invalid) { + ma_uint64 bytesRead = 0; + if (location == ma_dr_wav_metadata_location_invalid) { return 0; } - if (drwav_fourcc_equal(pChunkId, "data") || drwav_fourcc_equal(pChunkId, "fmt") || drwav_fourcc_equal(pChunkId, "fact")) { + if (ma_dr_wav_fourcc_equal(pChunkId, "data") || ma_dr_wav_fourcc_equal(pChunkId, "fmt ") || ma_dr_wav_fourcc_equal(pChunkId, "fact")) { return 0; } - if (pParser->stage == drwav__metadata_parser_stage_count) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { pParser->metadataCount += 1; - drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1); + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)chunkSize, 1); } else { - drwav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; - pMetadata->type = drwav_metadata_type_unknown; + ma_dr_wav_metadata* pMetadata = &pParser->pMetadata[pParser->metadataCursor]; + pMetadata->type = ma_dr_wav_metadata_type_unknown; pMetadata->data.unknown.chunkLocation = location; pMetadata->data.unknown.id[0] = pChunkId[0]; pMetadata->data.unknown.id[1] = pChunkId[1]; pMetadata->data.unknown.id[2] = pChunkId[2]; pMetadata->data.unknown.id[3] = pChunkId[3]; - pMetadata->data.unknown.dataSizeInBytes = (drwav_uint32)chunkSize; - pMetadata->data.unknown.pData = (drwav_uint8 *)drwav__metadata_get_memory(pParser, (size_t)chunkSize, 1); - DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL); - bytesRead = drwav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL); + pMetadata->data.unknown.dataSizeInBytes = (ma_uint32)chunkSize; + pMetadata->data.unknown.pData = (ma_uint8 *)ma_dr_wav__metadata_get_memory(pParser, (size_t)chunkSize, 1); + MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL); + bytesRead = ma_dr_wav__metadata_parser_read(pParser, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes, NULL); if (bytesRead == pMetadata->data.unknown.dataSizeInBytes) { pParser->metadataCursor += 1; } else { @@ -77694,41 +78160,41 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_unknown_chunk(drwav__metadata } return bytesRead; } -DRWAV_PRIVATE drwav_bool32 drwav__chunk_matches(drwav_metadata_type allowedMetadataTypes, const drwav_uint8* pChunkID, drwav_metadata_type type, const char* pID) +MA_PRIVATE ma_bool32 ma_dr_wav__chunk_matches(ma_dr_wav_metadata_type allowedMetadataTypes, const ma_uint8* pChunkID, ma_dr_wav_metadata_type type, const char* pID) { - return (allowedMetadataTypes & type) && drwav_fourcc_equal(pChunkID, pID); + return (allowedMetadataTypes & type) && ma_dr_wav_fourcc_equal(pChunkID, pID); } -DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* pParser, const drwav_chunk_header* pChunkHeader, drwav_metadata_type allowedMetadataTypes) +MA_PRIVATE ma_uint64 ma_dr_wav__metadata_process_chunk(ma_dr_wav__metadata_parser* pParser, const ma_dr_wav_chunk_header* pChunkHeader, ma_dr_wav_metadata_type allowedMetadataTypes) { - const drwav_uint8 *pChunkID = pChunkHeader->id.fourcc; - drwav_uint64 bytesRead = 0; - if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_smpl, "smpl")) { - if (pChunkHeader->sizeInBytes >= DRWAV_SMPL_BYTES) { - if (pParser->stage == drwav__metadata_parser_stage_count) { - drwav_uint8 buffer[4]; + const ma_uint8 *pChunkID = pChunkHeader->id.fourcc; + ma_uint64 bytesRead = 0; + if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_smpl, "smpl")) { + if (pChunkHeader->sizeInBytes >= MA_DR_WAV_SMPL_BYTES) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + ma_uint8 buffer[4]; size_t bytesJustRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, 28, drwav_seek_origin_current)) { + if (!pParser->onSeek(pParser->pReadSeekUserData, 28, ma_dr_wav_seek_origin_current)) { return bytesRead; } bytesRead += 28; - bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); if (bytesJustRead == sizeof(buffer)) { - drwav_uint32 loopCount = drwav_bytes_to_u32(buffer); - drwav_uint64 calculatedLoopCount; - calculatedLoopCount = (pChunkHeader->sizeInBytes - DRWAV_SMPL_BYTES) / DRWAV_SMPL_LOOP_BYTES; + ma_uint32 loopCount = ma_dr_wav_bytes_to_u32(buffer); + ma_uint64 calculatedLoopCount; + calculatedLoopCount = (pChunkHeader->sizeInBytes - MA_DR_WAV_SMPL_BYTES) / MA_DR_WAV_SMPL_LOOP_BYTES; if (calculatedLoopCount == loopCount) { - bytesJustRead = drwav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, sizeof(buffer), &bytesRead); if (bytesJustRead == sizeof(buffer)) { - drwav_uint32 samplerSpecificDataSizeInBytes = drwav_bytes_to_u32(buffer); + ma_uint32 samplerSpecificDataSizeInBytes = ma_dr_wav_bytes_to_u32(buffer); pParser->metadataCount += 1; - drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_smpl_loop) * loopCount, DRWAV_METADATA_ALIGNMENT); - drwav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1); + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_smpl_loop) * loopCount, MA_DR_WAV_METADATA_ALIGNMENT); + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, samplerSpecificDataSizeInBytes, 1); } } else { } } } else { - bytesRead = drwav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); + bytesRead = ma_dr_wav__read_smpl_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { @@ -77736,12 +78202,12 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* } } else { } - } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_inst, "inst")) { - if (pChunkHeader->sizeInBytes == DRWAV_INST_BYTES) { - if (pParser->stage == drwav__metadata_parser_stage_count) { + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_inst, "inst")) { + if (pChunkHeader->sizeInBytes == MA_DR_WAV_INST_BYTES) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { pParser->metadataCount += 1; } else { - bytesRead = drwav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); + bytesRead = ma_dr_wav__read_inst_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { @@ -77749,12 +78215,12 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* } } else { } - } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_acid, "acid")) { - if (pChunkHeader->sizeInBytes == DRWAV_ACID_BYTES) { - if (pParser->stage == drwav__metadata_parser_stage_count) { + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_acid, "acid")) { + if (pChunkHeader->sizeInBytes == MA_DR_WAV_ACID_BYTES) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { pParser->metadataCount += 1; } else { - bytesRead = drwav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); + bytesRead = ma_dr_wav__read_acid_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor]); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { @@ -77762,15 +78228,15 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* } } else { } - } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_cue, "cue ")) { - if (pChunkHeader->sizeInBytes >= DRWAV_CUE_BYTES) { - if (pParser->stage == drwav__metadata_parser_stage_count) { + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_cue, "cue ")) { + if (pChunkHeader->sizeInBytes >= MA_DR_WAV_CUE_BYTES) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { size_t cueCount; pParser->metadataCount += 1; - cueCount = (size_t)(pChunkHeader->sizeInBytes - DRWAV_CUE_BYTES) / DRWAV_CUE_POINT_BYTES; - drwav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(drwav_cue_point) * cueCount, DRWAV_METADATA_ALIGNMENT); + cueCount = (size_t)(pChunkHeader->sizeInBytes - MA_DR_WAV_CUE_BYTES) / MA_DR_WAV_CUE_POINT_BYTES; + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, sizeof(ma_dr_wav_cue_point) * cueCount, MA_DR_WAV_METADATA_ALIGNMENT); } else { - bytesRead = drwav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); + bytesRead = ma_dr_wav__read_cue_to_metadata_obj(pParser, pChunkHeader, &pParser->pMetadata[pParser->metadataCursor]); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { @@ -77778,35 +78244,35 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* } } else { } - } else if (drwav__chunk_matches(allowedMetadataTypes, pChunkID, drwav_metadata_type_bext, "bext")) { - if (pChunkHeader->sizeInBytes >= DRWAV_BEXT_BYTES) { - if (pParser->stage == drwav__metadata_parser_stage_count) { - char buffer[DRWAV_BEXT_DESCRIPTION_BYTES + 1]; - size_t allocSizeNeeded = DRWAV_BEXT_UMID_BYTES; + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, pChunkID, ma_dr_wav_metadata_type_bext, "bext")) { + if (pChunkHeader->sizeInBytes >= MA_DR_WAV_BEXT_BYTES) { + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { + char buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES + 1]; + size_t allocSizeNeeded = MA_DR_WAV_BEXT_UMID_BYTES; size_t bytesJustRead; - buffer[DRWAV_BEXT_DESCRIPTION_BYTES] = '\0'; - bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_DESCRIPTION_BYTES, &bytesRead); - if (bytesJustRead != DRWAV_BEXT_DESCRIPTION_BYTES) { + buffer[MA_DR_WAV_BEXT_DESCRIPTION_BYTES] = '\0'; + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_DESCRIPTION_BYTES, &bytesRead); + if (bytesJustRead != MA_DR_WAV_BEXT_DESCRIPTION_BYTES) { return bytesRead; } - allocSizeNeeded += drwav__strlen(buffer) + 1; - buffer[DRWAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0'; - bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead); - if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_NAME_BYTES) { + allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; + buffer[MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES] = '\0'; + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES, &bytesRead); + if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES) { return bytesRead; } - allocSizeNeeded += drwav__strlen(buffer) + 1; - buffer[DRWAV_BEXT_ORIGINATOR_REF_BYTES] = '\0'; - bytesJustRead = drwav__metadata_parser_read(pParser, buffer, DRWAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead); - if (bytesJustRead != DRWAV_BEXT_ORIGINATOR_REF_BYTES) { + allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; + buffer[MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES] = '\0'; + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, buffer, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES, &bytesRead); + if (bytesJustRead != MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES) { return bytesRead; } - allocSizeNeeded += drwav__strlen(buffer) + 1; - allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - DRWAV_BEXT_BYTES; - drwav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); + allocSizeNeeded += ma_dr_wav__strlen(buffer) + 1; + allocSizeNeeded += (size_t)pChunkHeader->sizeInBytes - MA_DR_WAV_BEXT_BYTES; + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, allocSizeNeeded, 1); pParser->metadataCount += 1; } else { - bytesRead = drwav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes); + bytesRead = ma_dr_wav__read_bext_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], pChunkHeader->sizeInBytes); if (bytesRead == pChunkHeader->sizeInBytes) { pParser->metadataCursor += 1; } else { @@ -77814,37 +78280,37 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* } } else { } - } else if (drwav_fourcc_equal(pChunkID, "LIST") || drwav_fourcc_equal(pChunkID, "list")) { - drwav_metadata_location listType = drwav_metadata_location_invalid; + } else if (ma_dr_wav_fourcc_equal(pChunkID, "LIST") || ma_dr_wav_fourcc_equal(pChunkID, "list")) { + ma_dr_wav_metadata_location listType = ma_dr_wav_metadata_location_invalid; while (bytesRead < pChunkHeader->sizeInBytes) { - drwav_uint8 subchunkId[4]; - drwav_uint8 subchunkSizeBuffer[4]; - drwav_uint64 subchunkDataSize; - drwav_uint64 subchunkBytesRead = 0; - drwav_uint64 bytesJustRead = drwav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead); + ma_uint8 subchunkId[4]; + ma_uint8 subchunkSizeBuffer[4]; + ma_uint64 subchunkDataSize; + ma_uint64 subchunkBytesRead = 0; + ma_uint64 bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkId, sizeof(subchunkId), &bytesRead); if (bytesJustRead != sizeof(subchunkId)) { break; } - if (drwav_fourcc_equal(subchunkId, "adtl")) { - listType = drwav_metadata_location_inside_adtl_list; + if (ma_dr_wav_fourcc_equal(subchunkId, "adtl")) { + listType = ma_dr_wav_metadata_location_inside_adtl_list; continue; - } else if (drwav_fourcc_equal(subchunkId, "INFO")) { - listType = drwav_metadata_location_inside_info_list; + } else if (ma_dr_wav_fourcc_equal(subchunkId, "INFO")) { + listType = ma_dr_wav_metadata_location_inside_info_list; continue; } - bytesJustRead = drwav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead); + bytesJustRead = ma_dr_wav__metadata_parser_read(pParser, subchunkSizeBuffer, sizeof(subchunkSizeBuffer), &bytesRead); if (bytesJustRead != sizeof(subchunkSizeBuffer)) { break; } - subchunkDataSize = drwav_bytes_to_u32(subchunkSizeBuffer); - if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_label, "labl") || drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_note, "note")) { - if (subchunkDataSize >= DRWAV_LIST_LABEL_OR_NOTE_BYTES) { - drwav_uint64 stringSizeWithNullTerm = subchunkDataSize - DRWAV_LIST_LABEL_OR_NOTE_BYTES; - if (pParser->stage == drwav__metadata_parser_stage_count) { + subchunkDataSize = ma_dr_wav_bytes_to_u32(subchunkSizeBuffer); + if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_label, "labl") || ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_note, "note")) { + if (subchunkDataSize >= MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES) { + ma_uint64 stringSizeWithNullTerm = subchunkDataSize - MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { pParser->metadataCount += 1; - drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1); + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerm, 1); } else { - subchunkBytesRead = drwav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, drwav_fourcc_equal(subchunkId, "labl") ? drwav_metadata_type_list_label : drwav_metadata_type_list_note); + subchunkBytesRead = ma_dr_wav__read_list_label_or_note_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize, ma_dr_wav_fourcc_equal(subchunkId, "labl") ? ma_dr_wav_metadata_type_list_label : ma_dr_wav_metadata_type_list_note); if (subchunkBytesRead == subchunkDataSize) { pParser->metadataCursor += 1; } else { @@ -77852,14 +78318,14 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* } } else { } - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_labelled_cue_region, "ltxt")) { - if (subchunkDataSize >= DRWAV_LIST_LABELLED_TEXT_BYTES) { - drwav_uint64 stringSizeWithNullTerminator = subchunkDataSize - DRWAV_LIST_LABELLED_TEXT_BYTES; - if (pParser->stage == drwav__metadata_parser_stage_count) { + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_labelled_cue_region, "ltxt")) { + if (subchunkDataSize >= MA_DR_WAV_LIST_LABELLED_TEXT_BYTES) { + ma_uint64 stringSizeWithNullTerminator = subchunkDataSize - MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; + if (pParser->stage == ma_dr_wav__metadata_parser_stage_count) { pParser->metadataCount += 1; - drwav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1); + ma_dr_wav__metadata_request_extra_memory_for_stage_2(pParser, (size_t)stringSizeWithNullTerminator, 1); } else { - subchunkBytesRead = drwav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize); + subchunkBytesRead = ma_dr_wav__read_list_labelled_cue_region_to_metadata_obj(pParser, &pParser->pMetadata[pParser->metadataCursor], subchunkDataSize); if (subchunkBytesRead == subchunkDataSize) { pParser->metadataCursor += 1; } else { @@ -77867,332 +78333,542 @@ DRWAV_PRIVATE drwav_uint64 drwav__metadata_process_chunk(drwav__metadata_parser* } } else { } - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_software, "ISFT")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_software); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_copyright, "ICOP")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_copyright); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_title, "INAM")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_title); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_artist, "IART")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_artist); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_comment, "ICMT")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_comment); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_date, "ICRD")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_date); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_genre, "IGNR")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_genre); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_album, "IPRD")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_album); - } else if (drwav__chunk_matches(allowedMetadataTypes, subchunkId, drwav_metadata_type_list_info_tracknumber, "ITRK")) { - subchunkBytesRead = drwav__metadata_process_info_text_chunk(pParser, subchunkDataSize, drwav_metadata_type_list_info_tracknumber); - } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) { - subchunkBytesRead = drwav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_software, "ISFT")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_software); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_copyright, "ICOP")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_copyright); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_title, "INAM")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_title); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_artist, "IART")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_artist); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_comment, "ICMT")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_comment); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_date, "ICRD")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_date); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_genre, "IGNR")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_genre); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_album, "IPRD")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_album); + } else if (ma_dr_wav__chunk_matches(allowedMetadataTypes, subchunkId, ma_dr_wav_metadata_type_list_info_tracknumber, "ITRK")) { + subchunkBytesRead = ma_dr_wav__metadata_process_info_text_chunk(pParser, subchunkDataSize, ma_dr_wav_metadata_type_list_info_tracknumber); + } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { + subchunkBytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, subchunkId, subchunkDataSize, listType); } bytesRead += subchunkBytesRead; - DRWAV_ASSERT(subchunkBytesRead <= subchunkDataSize); + MA_DR_WAV_ASSERT(subchunkBytesRead <= subchunkDataSize); if (subchunkBytesRead < subchunkDataSize) { - drwav_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead; - if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, drwav_seek_origin_current)) { + ma_uint64 bytesToSeek = subchunkDataSize - subchunkBytesRead; + if (!pParser->onSeek(pParser->pReadSeekUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current)) { break; } bytesRead += bytesToSeek; } if ((subchunkDataSize % 2) == 1) { - if (!pParser->onSeek(pParser->pReadSeekUserData, 1, drwav_seek_origin_current)) { + if (!pParser->onSeek(pParser->pReadSeekUserData, 1, ma_dr_wav_seek_origin_current)) { break; } bytesRead += 1; } } - } else if ((allowedMetadataTypes & drwav_metadata_type_unknown) != 0) { - bytesRead = drwav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, drwav_metadata_location_top_level); + } else if ((allowedMetadataTypes & ma_dr_wav_metadata_type_unknown) != 0) { + bytesRead = ma_dr_wav__metadata_process_unknown_chunk(pParser, pChunkID, pChunkHeader->sizeInBytes, ma_dr_wav_metadata_location_top_level); } return bytesRead; } -DRWAV_PRIVATE drwav_uint32 drwav_get_bytes_per_pcm_frame(drwav* pWav) +MA_PRIVATE ma_uint32 ma_dr_wav_get_bytes_per_pcm_frame(ma_dr_wav* pWav) { - drwav_uint32 bytesPerFrame; + ma_uint32 bytesPerFrame; if ((pWav->bitsPerSample & 0x7) == 0) { bytesPerFrame = (pWav->bitsPerSample * pWav->fmt.channels) >> 3; } else { bytesPerFrame = pWav->fmt.blockAlign; } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { if (bytesPerFrame != pWav->fmt.channels) { return 0; } } return bytesPerFrame; } -DRWAV_API drwav_uint16 drwav_fmt_get_format(const drwav_fmt* pFMT) +MA_API ma_uint16 ma_dr_wav_fmt_get_format(const ma_dr_wav_fmt* pFMT) { if (pFMT == NULL) { return 0; } - if (pFMT->formatTag != DR_WAVE_FORMAT_EXTENSIBLE) { + if (pFMT->formatTag != MA_DR_WAVE_FORMAT_EXTENSIBLE) { return pFMT->formatTag; } else { - return drwav_bytes_to_u16(pFMT->subFormat); + return ma_dr_wav_bytes_to_u16(pFMT->subFormat); } } -DRWAV_PRIVATE drwav_bool32 drwav_preinit(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pReadSeekUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE ma_bool32 ma_dr_wav_preinit(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pReadSeekUserData, const ma_allocation_callbacks* pAllocationCallbacks) { if (pWav == NULL || onRead == NULL || onSeek == NULL) { - return DRWAV_FALSE; + return MA_FALSE; } - DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav)); + MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); pWav->onRead = onRead; pWav->onSeek = onSeek; pWav->pUserData = pReadSeekUserData; - pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { - return DRWAV_FALSE; + return MA_FALSE; } - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags) +MA_PRIVATE ma_bool32 ma_dr_wav_init__internal(ma_dr_wav* pWav, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags) { - drwav_uint64 cursor; - drwav_bool32 sequential; - drwav_uint8 riff[4]; - drwav_fmt fmt; + ma_result result; + ma_uint64 cursor; + ma_bool32 sequential; + ma_uint8 riff[4]; + ma_dr_wav_fmt fmt; unsigned short translatedFormatTag; - drwav_bool32 foundDataChunk; - drwav_uint64 dataChunkSize = 0; - drwav_uint64 sampleCountFromFactChunk = 0; - drwav_uint64 chunkSize; - drwav__metadata_parser metadataParser; + ma_uint64 dataChunkSize = 0; + ma_uint64 sampleCountFromFactChunk = 0; + ma_uint64 metadataStartPos; + ma_dr_wav__metadata_parser metadataParser; + ma_bool8 isProcessingMetadata = MA_FALSE; + ma_bool8 foundChunk_fmt = MA_FALSE; + ma_bool8 foundChunk_data = MA_FALSE; + ma_bool8 isAIFCFormType = MA_FALSE; + ma_uint64 aiffFrameCount = 0; cursor = 0; - sequential = (flags & DRWAV_SEQUENTIAL) != 0; - if (drwav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) { - return DRWAV_FALSE; + sequential = (flags & MA_DR_WAV_SEQUENTIAL) != 0; + MA_DR_WAV_ZERO_OBJECT(&fmt); + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff, sizeof(riff), &cursor) != sizeof(riff)) { + return MA_FALSE; } - if (drwav_fourcc_equal(riff, "RIFF")) { - pWav->container = drwav_container_riff; - } else if (drwav_fourcc_equal(riff, "riff")) { + if (ma_dr_wav_fourcc_equal(riff, "RIFF")) { + pWav->container = ma_dr_wav_container_riff; + } else if (ma_dr_wav_fourcc_equal(riff, "RIFX")) { + pWav->container = ma_dr_wav_container_rifx; + } else if (ma_dr_wav_fourcc_equal(riff, "riff")) { int i; - drwav_uint8 riff2[12]; - pWav->container = drwav_container_w64; - if (drwav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) { - return DRWAV_FALSE; + ma_uint8 riff2[12]; + pWav->container = ma_dr_wav_container_w64; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, riff2, sizeof(riff2), &cursor) != sizeof(riff2)) { + return MA_FALSE; } for (i = 0; i < 12; ++i) { - if (riff2[i] != drwavGUID_W64_RIFF[i+4]) { - return DRWAV_FALSE; + if (riff2[i] != ma_dr_wavGUID_W64_RIFF[i+4]) { + return MA_FALSE; } } - } else if (drwav_fourcc_equal(riff, "RF64")) { - pWav->container = drwav_container_rf64; + } else if (ma_dr_wav_fourcc_equal(riff, "RF64")) { + pWav->container = ma_dr_wav_container_rf64; + } else if (ma_dr_wav_fourcc_equal(riff, "FORM")) { + pWav->container = ma_dr_wav_container_aiff; } else { - return DRWAV_FALSE; + return MA_FALSE; } - if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { - drwav_uint8 chunkSizeBytes[4]; - drwav_uint8 wave[4]; - if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return DRWAV_FALSE; + if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) { + ma_uint8 chunkSizeBytes[4]; + ma_uint8 wave[4]; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { + return MA_FALSE; } - if (pWav->container == drwav_container_riff) { - if (drwav_bytes_to_u32(chunkSizeBytes) < 36) { - return DRWAV_FALSE; + if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) { + if (ma_dr_wav_bytes_to_u32_ex(chunkSizeBytes, pWav->container) < 36) { + return MA_FALSE; + } + } else if (pWav->container == ma_dr_wav_container_rf64) { + if (ma_dr_wav_bytes_to_u32_le(chunkSizeBytes) != 0xFFFFFFFF) { + return MA_FALSE; } } else { - if (drwav_bytes_to_u32(chunkSizeBytes) != 0xFFFFFFFF) { - return DRWAV_FALSE; - } + return MA_FALSE; } - if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { - return DRWAV_FALSE; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { + return MA_FALSE; } - if (!drwav_fourcc_equal(wave, "WAVE")) { - return DRWAV_FALSE; + if (!ma_dr_wav_fourcc_equal(wave, "WAVE")) { + return MA_FALSE; + } + } else if (pWav->container == ma_dr_wav_container_w64) { + ma_uint8 chunkSizeBytes[8]; + ma_uint8 wave[16]; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { + return MA_FALSE; + } + if (ma_dr_wav_bytes_to_u64(chunkSizeBytes) < 80) { + return MA_FALSE; + } + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { + return MA_FALSE; + } + if (!ma_dr_wav_guid_equal(wave, ma_dr_wavGUID_W64_WAVE)) { + return MA_FALSE; + } + } else if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint8 chunkSizeBytes[4]; + ma_uint8 aiff[4]; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { + return MA_FALSE; + } + if (ma_dr_wav_bytes_to_u32_be(chunkSizeBytes) < 18) { + return MA_FALSE; + } + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, aiff, sizeof(aiff), &cursor) != sizeof(aiff)) { + return MA_FALSE; + } + if (ma_dr_wav_fourcc_equal(aiff, "AIFF")) { + isAIFCFormType = MA_FALSE; + } else if (ma_dr_wav_fourcc_equal(aiff, "AIFC")) { + isAIFCFormType = MA_TRUE; + } else { + return MA_FALSE; } } else { - drwav_uint8 chunkSizeBytes[8]; - drwav_uint8 wave[16]; - if (drwav__on_read(pWav->onRead, pWav->pUserData, chunkSizeBytes, sizeof(chunkSizeBytes), &cursor) != sizeof(chunkSizeBytes)) { - return DRWAV_FALSE; - } - if (drwav_bytes_to_u64(chunkSizeBytes) < 80) { - return DRWAV_FALSE; - } - if (drwav__on_read(pWav->onRead, pWav->pUserData, wave, sizeof(wave), &cursor) != sizeof(wave)) { - return DRWAV_FALSE; - } - if (!drwav_guid_equal(wave, drwavGUID_W64_WAVE)) { - return DRWAV_FALSE; - } + return MA_FALSE; } - if (pWav->container == drwav_container_rf64) { - drwav_uint8 sizeBytes[8]; - drwav_uint64 bytesRemainingInChunk; - drwav_chunk_header header; - drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != DRWAV_SUCCESS) { - return DRWAV_FALSE; + if (pWav->container == ma_dr_wav_container_rf64) { + ma_uint8 sizeBytes[8]; + ma_uint64 bytesRemainingInChunk; + ma_dr_wav_chunk_header header; + result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); + if (result != MA_SUCCESS) { + return MA_FALSE; } - if (!drwav_fourcc_equal(header.id.fourcc, "ds64")) { - return DRWAV_FALSE; + if (!ma_dr_wav_fourcc_equal(header.id.fourcc, "ds64")) { + return MA_FALSE; } bytesRemainingInChunk = header.sizeInBytes + header.paddingSize; - if (!drwav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) { - return DRWAV_FALSE; + if (!ma_dr_wav__seek_forward(pWav->onSeek, 8, pWav->pUserData)) { + return MA_FALSE; } bytesRemainingInChunk -= 8; cursor += 8; - if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { - return DRWAV_FALSE; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { + return MA_FALSE; } bytesRemainingInChunk -= 8; - dataChunkSize = drwav_bytes_to_u64(sizeBytes); - if (drwav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { - return DRWAV_FALSE; + dataChunkSize = ma_dr_wav_bytes_to_u64(sizeBytes); + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, sizeBytes, sizeof(sizeBytes), &cursor) != sizeof(sizeBytes)) { + return MA_FALSE; } bytesRemainingInChunk -= 8; - sampleCountFromFactChunk = drwav_bytes_to_u64(sizeBytes); - if (!drwav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) { - return DRWAV_FALSE; + sampleCountFromFactChunk = ma_dr_wav_bytes_to_u64(sizeBytes); + if (!ma_dr_wav__seek_forward(pWav->onSeek, bytesRemainingInChunk, pWav->pUserData)) { + return MA_FALSE; } cursor += bytesRemainingInChunk; } - if (!drwav__read_fmt(pWav->onRead, pWav->onSeek, pWav->pUserData, pWav->container, &cursor, &fmt)) { - return DRWAV_FALSE; + metadataStartPos = cursor; + isProcessingMetadata = !sequential && ((flags & MA_DR_WAV_WITH_METADATA) != 0); + if (pWav->container != ma_dr_wav_container_riff && pWav->container != ma_dr_wav_container_rf64) { + isProcessingMetadata = MA_FALSE; } - if ((fmt.sampleRate == 0 || fmt.sampleRate > DRWAV_MAX_SAMPLE_RATE) || - (fmt.channels == 0 || fmt.channels > DRWAV_MAX_CHANNELS) || - (fmt.bitsPerSample == 0 || fmt.bitsPerSample > DRWAV_MAX_BITS_PER_SAMPLE) || - fmt.blockAlign == 0) { - return DRWAV_FALSE; - } - translatedFormatTag = fmt.formatTag; - if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) { - translatedFormatTag = drwav_bytes_to_u16(fmt.subFormat + 0); - } - DRWAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser)); - if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) { - drwav_uint64 cursorForMetadata = cursor; + MA_DR_WAV_ZERO_MEMORY(&metadataParser, sizeof(metadataParser)); + if (isProcessingMetadata) { metadataParser.onRead = pWav->onRead; metadataParser.onSeek = pWav->onSeek; metadataParser.pReadSeekUserData = pWav->pUserData; - metadataParser.stage = drwav__metadata_parser_stage_count; - for (;;) { - drwav_result result; - drwav_uint64 bytesRead; - drwav_uint64 remainingBytes; - drwav_chunk_header header; - result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursorForMetadata, &header); - if (result != DRWAV_SUCCESS) { - break; - } - bytesRead = drwav__metadata_process_chunk(&metadataParser, &header, pWav->allowedMetadataTypes); - DRWAV_ASSERT(bytesRead <= header.sizeInBytes); - remainingBytes = header.sizeInBytes - bytesRead + header.paddingSize; - if (!drwav__seek_forward(pWav->onSeek, remainingBytes, pWav->pUserData)) { - break; - } - cursorForMetadata += remainingBytes; - } - if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) { - return DRWAV_FALSE; - } - drwav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks); - metadataParser.stage = drwav__metadata_parser_stage_read; + metadataParser.stage = ma_dr_wav__metadata_parser_stage_count; } - foundDataChunk = DRWAV_FALSE; for (;;) { - drwav_chunk_header header; - drwav_result result = drwav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); - if (result != DRWAV_SUCCESS) { - if (!foundDataChunk) { - return DRWAV_FALSE; - } else { - break; - } - } - if (!sequential && onChunk != NULL) { - drwav_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt); - if (callbackBytesRead > 0) { - if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) { - return DRWAV_FALSE; - } - } - } - if (!sequential && pWav->allowedMetadataTypes != drwav_metadata_type_none && (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64)) { - drwav_uint64 bytesRead = drwav__metadata_process_chunk(&metadataParser, &header, pWav->allowedMetadataTypes); - if (bytesRead > 0) { - if (!drwav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData)) { - return DRWAV_FALSE; - } - } - } - if (!foundDataChunk) { - pWav->dataChunkDataPos = cursor; - } - chunkSize = header.sizeInBytes; - if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { - if (drwav_fourcc_equal(header.id.fourcc, "data")) { - foundDataChunk = DRWAV_TRUE; - if (pWav->container != drwav_container_rf64) { - dataChunkSize = chunkSize; - } - } - } else { - if (drwav_guid_equal(header.id.guid, drwavGUID_W64_DATA)) { - foundDataChunk = DRWAV_TRUE; - dataChunkSize = chunkSize; - } - } - if (foundDataChunk && sequential) { + ma_dr_wav_chunk_header header; + ma_uint64 chunkSize; + result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); + if (result != MA_SUCCESS) { break; } - if (pWav->container == drwav_container_riff) { - if (drwav_fourcc_equal(header.id.fourcc, "fact")) { - drwav_uint32 sampleCount; - if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) { - return DRWAV_FALSE; + chunkSize = header.sizeInBytes; + if (!sequential && onChunk != NULL) { + ma_uint64 callbackBytesRead = onChunk(pChunkUserData, pWav->onRead, pWav->onSeek, pWav->pUserData, &header, pWav->container, &fmt); + if (callbackBytesRead > 0) { + if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) { + return MA_FALSE; + } + } + } + if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fmt ")) || + ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FMT))) { + ma_uint8 fmtData[16]; + foundChunk_fmt = MA_TRUE; + if (pWav->onRead(pWav->pUserData, fmtData, sizeof(fmtData)) != sizeof(fmtData)) { + return MA_FALSE; + } + cursor += sizeof(fmtData); + fmt.formatTag = ma_dr_wav_bytes_to_u16_ex(fmtData + 0, pWav->container); + fmt.channels = ma_dr_wav_bytes_to_u16_ex(fmtData + 2, pWav->container); + fmt.sampleRate = ma_dr_wav_bytes_to_u32_ex(fmtData + 4, pWav->container); + fmt.avgBytesPerSec = ma_dr_wav_bytes_to_u32_ex(fmtData + 8, pWav->container); + fmt.blockAlign = ma_dr_wav_bytes_to_u16_ex(fmtData + 12, pWav->container); + fmt.bitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtData + 14, pWav->container); + fmt.extendedSize = 0; + fmt.validBitsPerSample = 0; + fmt.channelMask = 0; + MA_DR_WAV_ZERO_MEMORY(fmt.subFormat, sizeof(fmt.subFormat)); + if (header.sizeInBytes > 16) { + ma_uint8 fmt_cbSize[2]; + int bytesReadSoFar = 0; + if (pWav->onRead(pWav->pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) { + return MA_FALSE; + } + cursor += sizeof(fmt_cbSize); + bytesReadSoFar = 18; + fmt.extendedSize = ma_dr_wav_bytes_to_u16_ex(fmt_cbSize, pWav->container); + if (fmt.extendedSize > 0) { + if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { + if (fmt.extendedSize != 22) { + return MA_FALSE; + } + } + if (fmt.formatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { + ma_uint8 fmtext[22]; + if (pWav->onRead(pWav->pUserData, fmtext, fmt.extendedSize) != fmt.extendedSize) { + return MA_FALSE; + } + fmt.validBitsPerSample = ma_dr_wav_bytes_to_u16_ex(fmtext + 0, pWav->container); + fmt.channelMask = ma_dr_wav_bytes_to_u32_ex(fmtext + 2, pWav->container); + ma_dr_wav_bytes_to_guid(fmtext + 6, fmt.subFormat); + } else { + if (pWav->onSeek(pWav->pUserData, fmt.extendedSize, ma_dr_wav_seek_origin_current) == MA_FALSE) { + return MA_FALSE; + } + } + cursor += fmt.extendedSize; + bytesReadSoFar += fmt.extendedSize; + } + if (pWav->onSeek(pWav->pUserData, (int)(header.sizeInBytes - bytesReadSoFar), ma_dr_wav_seek_origin_current) == MA_FALSE) { + return MA_FALSE; + } + cursor += (header.sizeInBytes - bytesReadSoFar); + } + if (header.paddingSize > 0) { + if (ma_dr_wav__seek_forward(pWav->onSeek, header.paddingSize, pWav->pUserData) == MA_FALSE) { + break; + } + cursor += header.paddingSize; + } + continue; + } + if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "data")) || + ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_DATA))) { + foundChunk_data = MA_TRUE; + pWav->dataChunkDataPos = cursor; + if (pWav->container != ma_dr_wav_container_rf64) { + dataChunkSize = chunkSize; + } + if (sequential || !isProcessingMetadata) { + break; + } else { + chunkSize += header.paddingSize; + if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { + break; + } + cursor += chunkSize; + continue; + } + } + if (((pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx || pWav->container == ma_dr_wav_container_rf64) && ma_dr_wav_fourcc_equal(header.id.fourcc, "fact")) || + ((pWav->container == ma_dr_wav_container_w64) && ma_dr_wav_guid_equal(header.id.guid, ma_dr_wavGUID_W64_FACT))) { + if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) { + ma_uint8 sampleCount[4]; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCount, 4, &cursor) != 4) { + return MA_FALSE; } chunkSize -= 4; - if (!foundDataChunk) { - pWav->dataChunkDataPos = cursor; - } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - sampleCountFromFactChunk = sampleCount; + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + sampleCountFromFactChunk = ma_dr_wav_bytes_to_u32_ex(sampleCount, pWav->container); } else { sampleCountFromFactChunk = 0; } - } - } else if (pWav->container == drwav_container_w64) { - if (drwav_guid_equal(header.id.guid, drwavGUID_W64_FACT)) { - if (drwav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) { - return DRWAV_FALSE; + } else if (pWav->container == ma_dr_wav_container_w64) { + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, &sampleCountFromFactChunk, 8, &cursor) != 8) { + return MA_FALSE; } chunkSize -= 8; - if (!foundDataChunk) { - pWav->dataChunkDataPos = cursor; + } else if (pWav->container == ma_dr_wav_container_rf64) { + } + chunkSize += header.paddingSize; + if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { + break; + } + cursor += chunkSize; + continue; + } + if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "COMM")) { + ma_uint8 commData[24]; + ma_uint32 commDataBytesToRead; + ma_uint16 channels; + ma_uint32 frameCount; + ma_uint16 sampleSizeInBits; + ma_int64 sampleRate; + ma_uint16 compressionFormat; + foundChunk_fmt = MA_TRUE; + if (isAIFCFormType) { + commDataBytesToRead = 24; + if (header.sizeInBytes < commDataBytesToRead) { + return MA_FALSE; + } + } else { + commDataBytesToRead = 18; + if (header.sizeInBytes != commDataBytesToRead) { + return MA_FALSE; } } - } else if (pWav->container == drwav_container_rf64) { + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, commData, commDataBytesToRead, &cursor) != commDataBytesToRead) { + return MA_FALSE; + } + channels = ma_dr_wav_bytes_to_u16_ex (commData + 0, pWav->container); + frameCount = ma_dr_wav_bytes_to_u32_ex (commData + 2, pWav->container); + sampleSizeInBits = ma_dr_wav_bytes_to_u16_ex (commData + 6, pWav->container); + sampleRate = ma_dr_wav_aiff_extented_to_s64(commData + 8); + if (sampleRate < 0 || sampleRate > 0xFFFFFFFF) { + return MA_FALSE; + } + if (isAIFCFormType) { + const ma_uint8* type = commData + 18; + if (ma_dr_wav_fourcc_equal(type, "NONE")) { + compressionFormat = MA_DR_WAVE_FORMAT_PCM; + } else if (ma_dr_wav_fourcc_equal(type, "raw ")) { + compressionFormat = MA_DR_WAVE_FORMAT_PCM; + if (sampleSizeInBits == 8) { + pWav->aiff.isUnsigned = MA_TRUE; + } + } else if (ma_dr_wav_fourcc_equal(type, "sowt")) { + compressionFormat = MA_DR_WAVE_FORMAT_PCM; + pWav->aiff.isLE = MA_TRUE; + } else if (ma_dr_wav_fourcc_equal(type, "fl32") || ma_dr_wav_fourcc_equal(type, "fl64") || ma_dr_wav_fourcc_equal(type, "FL32") || ma_dr_wav_fourcc_equal(type, "FL64")) { + compressionFormat = MA_DR_WAVE_FORMAT_IEEE_FLOAT; + } else if (ma_dr_wav_fourcc_equal(type, "alaw") || ma_dr_wav_fourcc_equal(type, "ALAW")) { + compressionFormat = MA_DR_WAVE_FORMAT_ALAW; + } else if (ma_dr_wav_fourcc_equal(type, "ulaw") || ma_dr_wav_fourcc_equal(type, "ULAW")) { + compressionFormat = MA_DR_WAVE_FORMAT_MULAW; + } else if (ma_dr_wav_fourcc_equal(type, "ima4")) { + compressionFormat = MA_DR_WAVE_FORMAT_DVI_ADPCM; + sampleSizeInBits = 4; + return MA_FALSE; + } else { + return MA_FALSE; + } + } else { + compressionFormat = MA_DR_WAVE_FORMAT_PCM; + } + aiffFrameCount = frameCount; + fmt.formatTag = compressionFormat; + fmt.channels = channels; + fmt.sampleRate = (ma_uint32)sampleRate; + fmt.bitsPerSample = sampleSizeInBits; + fmt.blockAlign = (ma_uint16)(fmt.channels * fmt.bitsPerSample / 8); + fmt.avgBytesPerSec = fmt.blockAlign * fmt.sampleRate; + if (fmt.blockAlign == 0 && compressionFormat == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + fmt.blockAlign = 34 * fmt.channels; + } + if (compressionFormat == MA_DR_WAVE_FORMAT_ALAW || compressionFormat == MA_DR_WAVE_FORMAT_MULAW) { + if (fmt.bitsPerSample > 8) { + fmt.bitsPerSample = 8; + fmt.blockAlign = fmt.channels; + } + } + fmt.bitsPerSample += (fmt.bitsPerSample & 7); + if (isAIFCFormType) { + if (ma_dr_wav__seek_forward(pWav->onSeek, (chunkSize - commDataBytesToRead), pWav->pUserData) == MA_FALSE) { + return MA_FALSE; + } + cursor += (chunkSize - commDataBytesToRead); + } + continue; + } + if (pWav->container == ma_dr_wav_container_aiff && ma_dr_wav_fourcc_equal(header.id.fourcc, "SSND")) { + ma_uint8 offsetAndBlockSizeData[8]; + ma_uint32 offset; + foundChunk_data = MA_TRUE; + if (ma_dr_wav__on_read(pWav->onRead, pWav->pUserData, offsetAndBlockSizeData, sizeof(offsetAndBlockSizeData), &cursor) != sizeof(offsetAndBlockSizeData)) { + return MA_FALSE; + } + offset = ma_dr_wav_bytes_to_u32_ex(offsetAndBlockSizeData + 0, pWav->container); + if (ma_dr_wav__seek_forward(pWav->onSeek, offset, pWav->pUserData) == MA_FALSE) { + return MA_FALSE; + } + cursor += offset; + pWav->dataChunkDataPos = cursor; + dataChunkSize = chunkSize; + if (sequential || !isProcessingMetadata) { + break; + } else { + if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { + break; + } + cursor += chunkSize; + continue; + } + } + if (isProcessingMetadata) { + ma_uint64 metadataBytesRead; + metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); + MA_DR_WAV_ASSERT(metadataBytesRead <= header.sizeInBytes); + if (ma_dr_wav__seek_from_start(pWav->onSeek, cursor, pWav->pUserData) == MA_FALSE) { + break; + } } chunkSize += header.paddingSize; - if (!drwav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData)) { + if (ma_dr_wav__seek_forward(pWav->onSeek, chunkSize, pWav->pUserData) == MA_FALSE) { break; } cursor += chunkSize; - if (!foundDataChunk) { - pWav->dataChunkDataPos = cursor; - } } - pWav->pMetadata = metadataParser.pMetadata; - pWav->metadataCount = metadataParser.metadataCount; - if (!foundDataChunk) { - return DRWAV_FALSE; + if (!foundChunk_fmt || !foundChunk_data) { + return MA_FALSE; + } + if ((fmt.sampleRate == 0 || fmt.sampleRate > MA_DR_WAV_MAX_SAMPLE_RATE ) || + (fmt.channels == 0 || fmt.channels > MA_DR_WAV_MAX_CHANNELS ) || + (fmt.bitsPerSample == 0 || fmt.bitsPerSample > MA_DR_WAV_MAX_BITS_PER_SAMPLE) || + fmt.blockAlign == 0) { + return MA_FALSE; + } + translatedFormatTag = fmt.formatTag; + if (translatedFormatTag == MA_DR_WAVE_FORMAT_EXTENSIBLE) { + translatedFormatTag = ma_dr_wav_bytes_to_u16_ex(fmt.subFormat + 0, pWav->container); } if (!sequential) { - if (!drwav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) { - return DRWAV_FALSE; + if (!ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData)) { + return MA_FALSE; } cursor = pWav->dataChunkDataPos; } + if (isProcessingMetadata && metadataParser.metadataCount > 0) { + if (ma_dr_wav__seek_from_start(pWav->onSeek, metadataStartPos, pWav->pUserData) == MA_FALSE) { + return MA_FALSE; + } + result = ma_dr_wav__metadata_alloc(&metadataParser, &pWav->allocationCallbacks); + if (result != MA_SUCCESS) { + return MA_FALSE; + } + metadataParser.stage = ma_dr_wav__metadata_parser_stage_read; + for (;;) { + ma_dr_wav_chunk_header header; + ma_uint64 metadataBytesRead; + result = ma_dr_wav__read_chunk_header(pWav->onRead, pWav->pUserData, pWav->container, &cursor, &header); + if (result != MA_SUCCESS) { + break; + } + metadataBytesRead = ma_dr_wav__metadata_process_chunk(&metadataParser, &header, ma_dr_wav_metadata_type_all_including_unknown); + if (ma_dr_wav__seek_forward(pWav->onSeek, (header.sizeInBytes + header.paddingSize) - metadataBytesRead, pWav->pUserData) == MA_FALSE) { + ma_dr_wav_free(metadataParser.pMetadata, &pWav->allocationCallbacks); + return MA_FALSE; + } + } + pWav->pMetadata = metadataParser.pMetadata; + pWav->metadataCount = metadataParser.metadataCount; + } + if (dataChunkSize == 0xFFFFFFFF && (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rifx) && pWav->isSequentialWrite == MA_FALSE) { + dataChunkSize = 0; + for (;;) { + ma_uint8 temp[4096]; + size_t bytesRead = pWav->onRead(pWav->pUserData, temp, sizeof(temp)); + dataChunkSize += bytesRead; + if (bytesRead < sizeof(temp)) { + break; + } + } + } + if (ma_dr_wav__seek_from_start(pWav->onSeek, pWav->dataChunkDataPos, pWav->pUserData) == MA_FALSE) { + ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); + return MA_FALSE; + } pWav->fmt = fmt; pWav->sampleRate = fmt.sampleRate; pWav->channels = fmt.channels; @@ -78202,24 +78878,27 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on pWav->dataChunkDataSize = dataChunkSize; if (sampleCountFromFactChunk != 0) { pWav->totalPCMFrameCount = sampleCountFromFactChunk; + } else if (aiffFrameCount != 0) { + pWav->totalPCMFrameCount = aiffFrameCount; } else { - drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { - return DRWAV_FALSE; + ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); + return MA_FALSE; } pWav->totalPCMFrameCount = dataChunkSize / bytesPerFrame; - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - drwav_uint64 totalBlockHeaderSizeInBytes; - drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + ma_uint64 totalBlockHeaderSizeInBytes; + ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; if ((blockCount * fmt.blockAlign) < dataChunkSize) { blockCount += 1; } totalBlockHeaderSizeInBytes = blockCount * (6*fmt.channels); pWav->totalPCMFrameCount = ((dataChunkSize - totalBlockHeaderSizeInBytes) * 2) / fmt.channels; } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - drwav_uint64 totalBlockHeaderSizeInBytes; - drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + ma_uint64 totalBlockHeaderSizeInBytes; + ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; if ((blockCount * fmt.blockAlign) < dataChunkSize) { blockCount += 1; } @@ -78228,307 +78907,308 @@ DRWAV_PRIVATE drwav_bool32 drwav_init__internal(drwav* pWav, drwav_chunk_proc on pWav->totalPCMFrameCount += blockCount; } } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { if (pWav->channels > 2) { - return DRWAV_FALSE; + ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); + return MA_FALSE; } } - if (drwav_get_bytes_per_pcm_frame(pWav) == 0) { - return DRWAV_FALSE; + if (ma_dr_wav_get_bytes_per_pcm_frame(pWav) == 0) { + ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); + return MA_FALSE; } -#ifdef DR_WAV_LIBSNDFILE_COMPAT - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; +#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (6*pWav->channels))) * 2)) / fmt.channels; } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - drwav_uint64 blockCount = dataChunkSize / fmt.blockAlign; + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + ma_uint64 blockCount = dataChunkSize / fmt.blockAlign; pWav->totalPCMFrameCount = (((blockCount * (fmt.blockAlign - (4*pWav->channels))) * 2) + (blockCount * pWav->channels)) / fmt.channels; } #endif - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_API drwav_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - return drwav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks); + return ma_dr_wav_init_ex(pWav, onRead, onSeek, NULL, pUserData, NULL, 0, pAllocationCallbacks); } -DRWAV_API drwav_bool32 drwav_init_ex(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, drwav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_ex(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, ma_dr_wav_chunk_proc onChunk, void* pReadSeekUserData, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { - if (!drwav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { - return DRWAV_FALSE; + if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pReadSeekUserData, pAllocationCallbacks)) { + return MA_FALSE; } - return drwav_init__internal(pWav, onChunk, pChunkUserData, flags); + return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); } -DRWAV_API drwav_bool32 drwav_init_with_metadata(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_with_metadata(ma_dr_wav* pWav, ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { - if (!drwav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) { - return DRWAV_FALSE; + if (!ma_dr_wav_preinit(pWav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + return MA_FALSE; } - pWav->allowedMetadataTypes = drwav_metadata_type_all_including_unknown; - return drwav_init__internal(pWav, NULL, NULL, flags); + return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); } -DRWAV_API drwav_metadata* drwav_take_ownership_of_metadata(drwav* pWav) +MA_API ma_dr_wav_metadata* ma_dr_wav_take_ownership_of_metadata(ma_dr_wav* pWav) { - drwav_metadata *result = pWav->pMetadata; + ma_dr_wav_metadata *result = pWav->pMetadata; pWav->pMetadata = NULL; pWav->metadataCount = 0; return result; } -DRWAV_PRIVATE size_t drwav__write(drwav* pWav, const void* pData, size_t dataSize) +MA_PRIVATE size_t ma_dr_wav__write(ma_dr_wav* pWav, const void* pData, size_t dataSize) { - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->onWrite != NULL); + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); return pWav->onWrite(pWav->pUserData, pData, dataSize); } -DRWAV_PRIVATE size_t drwav__write_byte(drwav* pWav, drwav_uint8 byte) +MA_PRIVATE size_t ma_dr_wav__write_byte(ma_dr_wav* pWav, ma_uint8 byte) { - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->onWrite != NULL); + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); return pWav->onWrite(pWav->pUserData, &byte, 1); } -DRWAV_PRIVATE size_t drwav__write_u16ne_to_le(drwav* pWav, drwav_uint16 value) +MA_PRIVATE size_t ma_dr_wav__write_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value) { - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->onWrite != NULL); - if (!drwav__is_little_endian()) { - value = drwav__bswap16(value); + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); + if (!ma_dr_wav__is_little_endian()) { + value = ma_dr_wav__bswap16(value); } - return drwav__write(pWav, &value, 2); + return ma_dr_wav__write(pWav, &value, 2); } -DRWAV_PRIVATE size_t drwav__write_u32ne_to_le(drwav* pWav, drwav_uint32 value) +MA_PRIVATE size_t ma_dr_wav__write_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value) { - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->onWrite != NULL); - if (!drwav__is_little_endian()) { - value = drwav__bswap32(value); + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); + if (!ma_dr_wav__is_little_endian()) { + value = ma_dr_wav__bswap32(value); } - return drwav__write(pWav, &value, 4); + return ma_dr_wav__write(pWav, &value, 4); } -DRWAV_PRIVATE size_t drwav__write_u64ne_to_le(drwav* pWav, drwav_uint64 value) +MA_PRIVATE size_t ma_dr_wav__write_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value) { - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->onWrite != NULL); - if (!drwav__is_little_endian()) { - value = drwav__bswap64(value); + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); + if (!ma_dr_wav__is_little_endian()) { + value = ma_dr_wav__bswap64(value); } - return drwav__write(pWav, &value, 8); + return ma_dr_wav__write(pWav, &value, 8); } -DRWAV_PRIVATE size_t drwav__write_f32ne_to_le(drwav* pWav, float value) +MA_PRIVATE size_t ma_dr_wav__write_f32ne_to_le(ma_dr_wav* pWav, float value) { union { - drwav_uint32 u32; + ma_uint32 u32; float f32; } u; - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->onWrite != NULL); + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->onWrite != NULL); u.f32 = value; - if (!drwav__is_little_endian()) { - u.u32 = drwav__bswap32(u.u32); + if (!ma_dr_wav__is_little_endian()) { + u.u32 = ma_dr_wav__bswap32(u.u32); } - return drwav__write(pWav, &u.u32, 4); + return ma_dr_wav__write(pWav, &u.u32, 4); } -DRWAV_PRIVATE size_t drwav__write_or_count(drwav* pWav, const void* pData, size_t dataSize) +MA_PRIVATE size_t ma_dr_wav__write_or_count(ma_dr_wav* pWav, const void* pData, size_t dataSize) { if (pWav == NULL) { return dataSize; } - return drwav__write(pWav, pData, dataSize); + return ma_dr_wav__write(pWav, pData, dataSize); } -DRWAV_PRIVATE size_t drwav__write_or_count_byte(drwav* pWav, drwav_uint8 byte) +MA_PRIVATE size_t ma_dr_wav__write_or_count_byte(ma_dr_wav* pWav, ma_uint8 byte) { if (pWav == NULL) { return 1; } - return drwav__write_byte(pWav, byte); + return ma_dr_wav__write_byte(pWav, byte); } -DRWAV_PRIVATE size_t drwav__write_or_count_u16ne_to_le(drwav* pWav, drwav_uint16 value) +MA_PRIVATE size_t ma_dr_wav__write_or_count_u16ne_to_le(ma_dr_wav* pWav, ma_uint16 value) { if (pWav == NULL) { return 2; } - return drwav__write_u16ne_to_le(pWav, value); + return ma_dr_wav__write_u16ne_to_le(pWav, value); } -DRWAV_PRIVATE size_t drwav__write_or_count_u32ne_to_le(drwav* pWav, drwav_uint32 value) +MA_PRIVATE size_t ma_dr_wav__write_or_count_u32ne_to_le(ma_dr_wav* pWav, ma_uint32 value) { if (pWav == NULL) { return 4; } - return drwav__write_u32ne_to_le(pWav, value); + return ma_dr_wav__write_u32ne_to_le(pWav, value); } #if 0 -DRWAV_PRIVATE size_t drwav__write_or_count_u64ne_to_le(drwav* pWav, drwav_uint64 value) +MA_PRIVATE size_t ma_dr_wav__write_or_count_u64ne_to_le(ma_dr_wav* pWav, ma_uint64 value) { if (pWav == NULL) { return 8; } - return drwav__write_u64ne_to_le(pWav, value); + return ma_dr_wav__write_u64ne_to_le(pWav, value); } #endif -DRWAV_PRIVATE size_t drwav__write_or_count_f32ne_to_le(drwav* pWav, float value) +MA_PRIVATE size_t ma_dr_wav__write_or_count_f32ne_to_le(ma_dr_wav* pWav, float value) { if (pWav == NULL) { return 4; } - return drwav__write_f32ne_to_le(pWav, value); + return ma_dr_wav__write_f32ne_to_le(pWav, value); } -DRWAV_PRIVATE size_t drwav__write_or_count_string_to_fixed_size_buf(drwav* pWav, char* str, size_t bufFixedSize) +MA_PRIVATE size_t ma_dr_wav__write_or_count_string_to_fixed_size_buf(ma_dr_wav* pWav, char* str, size_t bufFixedSize) { size_t len; if (pWav == NULL) { return bufFixedSize; } - len = drwav__strlen_clamped(str, bufFixedSize); - drwav__write_or_count(pWav, str, len); + len = ma_dr_wav__strlen_clamped(str, bufFixedSize); + ma_dr_wav__write_or_count(pWav, str, len); if (len < bufFixedSize) { size_t i; for (i = 0; i < bufFixedSize - len; ++i) { - drwav__write_byte(pWav, 0); + ma_dr_wav__write_byte(pWav, 0); } } return bufFixedSize; } -DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* pMetadatas, drwav_uint32 metadataCount) +MA_PRIVATE size_t ma_dr_wav__write_or_count_metadata(ma_dr_wav* pWav, ma_dr_wav_metadata* pMetadatas, ma_uint32 metadataCount) { size_t bytesWritten = 0; - drwav_bool32 hasListAdtl = DRWAV_FALSE; - drwav_bool32 hasListInfo = DRWAV_FALSE; - drwav_uint32 iMetadata; + ma_bool32 hasListAdtl = MA_FALSE; + ma_bool32 hasListInfo = MA_FALSE; + ma_uint32 iMetadata; if (pMetadatas == NULL || metadataCount == 0) { return 0; } for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - drwav_metadata* pMetadata = &pMetadatas[iMetadata]; - drwav_uint32 chunkSize = 0; - if ((pMetadata->type & drwav_metadata_type_list_all_info_strings) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list)) { - hasListInfo = DRWAV_TRUE; + ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; + ma_uint32 chunkSize = 0; + if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list)) { + hasListInfo = MA_TRUE; } - if ((pMetadata->type & drwav_metadata_type_list_all_adtl) || (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list)) { - hasListAdtl = DRWAV_TRUE; + if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_adtl) || (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list)) { + hasListAdtl = MA_TRUE; } switch (pMetadata->type) { - case drwav_metadata_type_smpl: + case ma_dr_wav_metadata_type_smpl: { - drwav_uint32 iLoop; - chunkSize = DRWAV_SMPL_BYTES + DRWAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes; - bytesWritten += drwav__write_or_count(pWav, "smpl", 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); + ma_uint32 iLoop; + chunkSize = MA_DR_WAV_SMPL_BYTES + MA_DR_WAV_SMPL_LOOP_BYTES * pMetadata->data.smpl.sampleLoopCount + pMetadata->data.smpl.samplerSpecificDataSizeInBytes; + bytesWritten += ma_dr_wav__write_or_count(pWav, "smpl", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.manufacturerId); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.productId); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplePeriodNanoseconds); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiUnityNote); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.midiPitchFraction); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteFormat); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.smpteOffset); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.sampleLoopCount); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); for (iLoop = 0; iLoop < pMetadata->data.smpl.sampleLoopCount; ++iLoop) { - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].cuePointId); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].type); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].firstSampleByteOffset); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].lastSampleByteOffset); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].sampleFraction); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.smpl.pLoops[iLoop].playCount); } if (pMetadata->data.smpl.samplerSpecificDataSizeInBytes > 0) { - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.smpl.pSamplerSpecificData, pMetadata->data.smpl.samplerSpecificDataSizeInBytes); } } break; - case drwav_metadata_type_inst: + case ma_dr_wav_metadata_type_inst: { - chunkSize = DRWAV_INST_BYTES; - bytesWritten += drwav__write_or_count(pWav, "inst", 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1); - bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1); - bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1); - bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1); - bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1); - bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1); - bytesWritten += drwav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1); + chunkSize = MA_DR_WAV_INST_BYTES; + bytesWritten += ma_dr_wav__write_or_count(pWav, "inst", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.midiUnityNote, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.fineTuneCents, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.gainDecibels, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowNote, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highNote, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.lowVelocity, 1); + bytesWritten += ma_dr_wav__write_or_count(pWav, &pMetadata->data.inst.highVelocity, 1); } break; - case drwav_metadata_type_cue: + case ma_dr_wav_metadata_type_cue: { - drwav_uint32 iCuePoint; - chunkSize = DRWAV_CUE_BYTES + DRWAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount; - bytesWritten += drwav__write_or_count(pWav, "cue ", 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount); + ma_uint32 iCuePoint; + chunkSize = MA_DR_WAV_CUE_BYTES + MA_DR_WAV_CUE_POINT_BYTES * pMetadata->data.cue.cuePointCount; + bytesWritten += ma_dr_wav__write_or_count(pWav, "cue ", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.cuePointCount); for (iCuePoint = 0; iCuePoint < pMetadata->data.cue.cuePointCount; ++iCuePoint) { - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].id); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].playOrderPosition); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].dataChunkId, 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].chunkStart); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].blockStart); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.cue.pCuePoints[iCuePoint].sampleByteOffset); } } break; - case drwav_metadata_type_acid: + case ma_dr_wav_metadata_type_acid: { - chunkSize = DRWAV_ACID_BYTES; - bytesWritten += drwav__write_or_count(pWav, "acid", 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1); - bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator); - bytesWritten += drwav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo); + chunkSize = MA_DR_WAV_ACID_BYTES; + bytesWritten += ma_dr_wav__write_or_count(pWav, "acid", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.flags); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.midiUnityNote); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.reserved1); + bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.reserved2); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.acid.numBeats); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterDenominator); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.acid.meterNumerator); + bytesWritten += ma_dr_wav__write_or_count_f32ne_to_le(pWav, pMetadata->data.acid.tempo); } break; - case drwav_metadata_type_bext: + case ma_dr_wav_metadata_type_bext: { - char reservedBuf[DRWAV_BEXT_RESERVED_BYTES]; - drwav_uint32 timeReferenceLow; - drwav_uint32 timeReferenceHigh; - chunkSize = DRWAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize; - bytesWritten += drwav__write_or_count(pWav, "bext", 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, DRWAV_BEXT_DESCRIPTION_BYTES); - bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, DRWAV_BEXT_ORIGINATOR_NAME_BYTES); - bytesWritten += drwav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, DRWAV_BEXT_ORIGINATOR_REF_BYTES); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate)); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime)); - timeReferenceLow = (drwav_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF); - timeReferenceHigh = (drwav_uint32)(pMetadata->data.bext.timeReference >> 32); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceLow); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pUMID, DRWAV_BEXT_UMID_BYTES); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness); - DRWAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf)); - bytesWritten += drwav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf)); + char reservedBuf[MA_DR_WAV_BEXT_RESERVED_BYTES]; + ma_uint32 timeReferenceLow; + ma_uint32 timeReferenceHigh; + chunkSize = MA_DR_WAV_BEXT_BYTES + pMetadata->data.bext.codingHistorySize; + bytesWritten += ma_dr_wav__write_or_count(pWav, "bext", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pDescription, MA_DR_WAV_BEXT_DESCRIPTION_BYTES); + bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorName, MA_DR_WAV_BEXT_ORIGINATOR_NAME_BYTES); + bytesWritten += ma_dr_wav__write_or_count_string_to_fixed_size_buf(pWav, pMetadata->data.bext.pOriginatorReference, MA_DR_WAV_BEXT_ORIGINATOR_REF_BYTES); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationDate, sizeof(pMetadata->data.bext.pOriginationDate)); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pOriginationTime, sizeof(pMetadata->data.bext.pOriginationTime)); + timeReferenceLow = (ma_uint32)(pMetadata->data.bext.timeReference & 0xFFFFFFFF); + timeReferenceHigh = (ma_uint32)(pMetadata->data.bext.timeReference >> 32); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceLow); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, timeReferenceHigh); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.version); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pUMID, MA_DR_WAV_BEXT_UMID_BYTES); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessValue); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.loudnessRange); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxTruePeakLevel); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxMomentaryLoudness); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.bext.maxShortTermLoudness); + MA_DR_WAV_ZERO_MEMORY(reservedBuf, sizeof(reservedBuf)); + bytesWritten += ma_dr_wav__write_or_count(pWav, reservedBuf, sizeof(reservedBuf)); if (pMetadata->data.bext.codingHistorySize > 0) { - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.bext.pCodingHistory, pMetadata->data.bext.codingHistorySize); } } break; - case drwav_metadata_type_unknown: + case ma_dr_wav_metadata_type_unknown: { - if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_top_level) { + if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_top_level) { chunkSize = pMetadata->data.unknown.dataSizeInBytes; - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, pMetadata->data.unknown.dataSizeInBytes); } } break; default: break; } if ((chunkSize % 2) != 0) { - bytesWritten += drwav__write_or_count_byte(pWav, 0); + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); } } if (hasListInfo) { - drwav_uint32 chunkSize = 4; + ma_uint32 chunkSize = 4; for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - drwav_metadata* pMetadata = &pMetadatas[iMetadata]; - if ((pMetadata->type & drwav_metadata_type_list_all_info_strings)) { + ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; + if ((pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings)) { chunkSize += 8; chunkSize += pMetadata->data.infoText.stringLength + 1; - } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) { + } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) { chunkSize += 8; chunkSize += pMetadata->data.unknown.dataSizeInBytes; } @@ -78536,73 +79216,73 @@ DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* chunkSize += 1; } } - bytesWritten += drwav__write_or_count(pWav, "LIST", 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count(pWav, "INFO", 4); + bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, "INFO", 4); for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - drwav_metadata* pMetadata = &pMetadatas[iMetadata]; - drwav_uint32 subchunkSize = 0; - if (pMetadata->type & drwav_metadata_type_list_all_info_strings) { + ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; + ma_uint32 subchunkSize = 0; + if (pMetadata->type & ma_dr_wav_metadata_type_list_all_info_strings) { const char* pID = NULL; switch (pMetadata->type) { - case drwav_metadata_type_list_info_software: pID = "ISFT"; break; - case drwav_metadata_type_list_info_copyright: pID = "ICOP"; break; - case drwav_metadata_type_list_info_title: pID = "INAM"; break; - case drwav_metadata_type_list_info_artist: pID = "IART"; break; - case drwav_metadata_type_list_info_comment: pID = "ICMT"; break; - case drwav_metadata_type_list_info_date: pID = "ICRD"; break; - case drwav_metadata_type_list_info_genre: pID = "IGNR"; break; - case drwav_metadata_type_list_info_album: pID = "IPRD"; break; - case drwav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; + case ma_dr_wav_metadata_type_list_info_software: pID = "ISFT"; break; + case ma_dr_wav_metadata_type_list_info_copyright: pID = "ICOP"; break; + case ma_dr_wav_metadata_type_list_info_title: pID = "INAM"; break; + case ma_dr_wav_metadata_type_list_info_artist: pID = "IART"; break; + case ma_dr_wav_metadata_type_list_info_comment: pID = "ICMT"; break; + case ma_dr_wav_metadata_type_list_info_date: pID = "ICRD"; break; + case ma_dr_wav_metadata_type_list_info_genre: pID = "IGNR"; break; + case ma_dr_wav_metadata_type_list_info_album: pID = "IPRD"; break; + case ma_dr_wav_metadata_type_list_info_tracknumber: pID = "ITRK"; break; default: break; } - DRWAV_ASSERT(pID != NULL); + MA_DR_WAV_ASSERT(pID != NULL); if (pMetadata->data.infoText.stringLength) { subchunkSize = pMetadata->data.infoText.stringLength + 1; - bytesWritten += drwav__write_or_count(pWav, pID, 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength); - bytesWritten += drwav__write_or_count_byte(pWav, '\0'); + bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.infoText.pString, pMetadata->data.infoText.stringLength); + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); } - } else if (pMetadata->type == drwav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_info_list) { + } else if (pMetadata->type == ma_dr_wav_metadata_type_unknown && pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_info_list) { if (pMetadata->data.unknown.dataSizeInBytes) { subchunkSize = pMetadata->data.unknown.dataSizeInBytes; - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.unknown.dataSizeInBytes); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); } } if ((subchunkSize % 2) != 0) { - bytesWritten += drwav__write_or_count_byte(pWav, 0); + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); } } } if (hasListAdtl) { - drwav_uint32 chunkSize = 4; + ma_uint32 chunkSize = 4; for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - drwav_metadata* pMetadata = &pMetadatas[iMetadata]; + ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; switch (pMetadata->type) { - case drwav_metadata_type_list_label: - case drwav_metadata_type_list_note: + case ma_dr_wav_metadata_type_list_label: + case ma_dr_wav_metadata_type_list_note: { chunkSize += 8; - chunkSize += DRWAV_LIST_LABEL_OR_NOTE_BYTES; + chunkSize += MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; if (pMetadata->data.labelOrNote.stringLength > 0) { chunkSize += pMetadata->data.labelOrNote.stringLength + 1; } } break; - case drwav_metadata_type_list_labelled_cue_region: + case ma_dr_wav_metadata_type_list_labelled_cue_region: { chunkSize += 8; - chunkSize += DRWAV_LIST_LABELLED_TEXT_BYTES; + chunkSize += MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; if (pMetadata->data.labelledCueRegion.stringLength > 0) { chunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; } } break; - case drwav_metadata_type_unknown: + case ma_dr_wav_metadata_type_unknown: { - if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) { + if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) { chunkSize += 8; chunkSize += pMetadata->data.unknown.dataSizeInBytes; } @@ -78613,968 +79293,457 @@ DRWAV_PRIVATE size_t drwav__write_or_count_metadata(drwav* pWav, drwav_metadata* chunkSize += 1; } } - bytesWritten += drwav__write_or_count(pWav, "LIST", 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, chunkSize); - bytesWritten += drwav__write_or_count(pWav, "adtl", 4); + bytesWritten += ma_dr_wav__write_or_count(pWav, "LIST", 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, chunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, "adtl", 4); for (iMetadata = 0; iMetadata < metadataCount; ++iMetadata) { - drwav_metadata* pMetadata = &pMetadatas[iMetadata]; - drwav_uint32 subchunkSize = 0; + ma_dr_wav_metadata* pMetadata = &pMetadatas[iMetadata]; + ma_uint32 subchunkSize = 0; switch (pMetadata->type) { - case drwav_metadata_type_list_label: - case drwav_metadata_type_list_note: + case ma_dr_wav_metadata_type_list_label: + case ma_dr_wav_metadata_type_list_note: { if (pMetadata->data.labelOrNote.stringLength > 0) { const char *pID = NULL; - if (pMetadata->type == drwav_metadata_type_list_label) { + if (pMetadata->type == ma_dr_wav_metadata_type_list_label) { pID = "labl"; } - else if (pMetadata->type == drwav_metadata_type_list_note) { + else if (pMetadata->type == ma_dr_wav_metadata_type_list_note) { pID = "note"; } - DRWAV_ASSERT(pID != NULL); - DRWAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); - subchunkSize = DRWAV_LIST_LABEL_OR_NOTE_BYTES; - bytesWritten += drwav__write_or_count(pWav, pID, 4); + MA_DR_WAV_ASSERT(pID != NULL); + MA_DR_WAV_ASSERT(pMetadata->data.labelOrNote.pString != NULL); + subchunkSize = MA_DR_WAV_LIST_LABEL_OR_NOTE_BYTES; + bytesWritten += ma_dr_wav__write_or_count(pWav, pID, 4); subchunkSize += pMetadata->data.labelOrNote.stringLength + 1; - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength); - bytesWritten += drwav__write_or_count_byte(pWav, '\0'); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelOrNote.cuePointId); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelOrNote.pString, pMetadata->data.labelOrNote.stringLength); + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); } } break; - case drwav_metadata_type_list_labelled_cue_region: + case ma_dr_wav_metadata_type_list_labelled_cue_region: { - subchunkSize = DRWAV_LIST_LABELLED_TEXT_BYTES; - bytesWritten += drwav__write_or_count(pWav, "ltxt", 4); + subchunkSize = MA_DR_WAV_LIST_LABELLED_TEXT_BYTES; + bytesWritten += ma_dr_wav__write_or_count(pWav, "ltxt", 4); if (pMetadata->data.labelledCueRegion.stringLength > 0) { subchunkSize += pMetadata->data.labelledCueRegion.stringLength + 1; } - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect); - bytesWritten += drwav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.cuePointId); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, pMetadata->data.labelledCueRegion.sampleLength); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.purposeId, 4); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.country); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.language); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.dialect); + bytesWritten += ma_dr_wav__write_or_count_u16ne_to_le(pWav, pMetadata->data.labelledCueRegion.codePage); if (pMetadata->data.labelledCueRegion.stringLength > 0) { - DRWAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength); - bytesWritten += drwav__write_or_count_byte(pWav, '\0'); + MA_DR_WAV_ASSERT(pMetadata->data.labelledCueRegion.pString != NULL); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.labelledCueRegion.pString, pMetadata->data.labelledCueRegion.stringLength); + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, '\0'); } } break; - case drwav_metadata_type_unknown: + case ma_dr_wav_metadata_type_unknown: { - if (pMetadata->data.unknown.chunkLocation == drwav_metadata_location_inside_adtl_list) { + if (pMetadata->data.unknown.chunkLocation == ma_dr_wav_metadata_location_inside_adtl_list) { subchunkSize = pMetadata->data.unknown.dataSizeInBytes; - DRWAV_ASSERT(pMetadata->data.unknown.pData != NULL); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.id, 4); - bytesWritten += drwav__write_or_count_u32ne_to_le(pWav, subchunkSize); - bytesWritten += drwav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); + MA_DR_WAV_ASSERT(pMetadata->data.unknown.pData != NULL); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.id, 4); + bytesWritten += ma_dr_wav__write_or_count_u32ne_to_le(pWav, subchunkSize); + bytesWritten += ma_dr_wav__write_or_count(pWav, pMetadata->data.unknown.pData, subchunkSize); } } break; default: break; } if ((subchunkSize % 2) != 0) { - bytesWritten += drwav__write_or_count_byte(pWav, 0); + bytesWritten += ma_dr_wav__write_or_count_byte(pWav, 0); } } } - DRWAV_ASSERT((bytesWritten % 2) == 0); + MA_DR_WAV_ASSERT((bytesWritten % 2) == 0); return bytesWritten; } -DRWAV_PRIVATE drwav_uint32 drwav__riff_chunk_size_riff(drwav_uint64 dataChunkSize, drwav_metadata* pMetadata, drwav_uint32 metadataCount) +MA_PRIVATE ma_uint32 ma_dr_wav__riff_chunk_size_riff(ma_uint64 dataChunkSize, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) { - drwav_uint64 chunkSize = 4 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); + ma_uint64 chunkSize = 4 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, pMetadata, metadataCount) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize); if (chunkSize > 0xFFFFFFFFUL) { chunkSize = 0xFFFFFFFFUL; } - return (drwav_uint32)chunkSize; + return (ma_uint32)chunkSize; } -DRWAV_PRIVATE drwav_uint32 drwav__data_chunk_size_riff(drwav_uint64 dataChunkSize) +MA_PRIVATE ma_uint32 ma_dr_wav__data_chunk_size_riff(ma_uint64 dataChunkSize) { if (dataChunkSize <= 0xFFFFFFFFUL) { - return (drwav_uint32)dataChunkSize; + return (ma_uint32)dataChunkSize; } else { return 0xFFFFFFFFUL; } } -DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_w64(drwav_uint64 dataChunkSize) +MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_w64(ma_uint64 dataChunkSize) { - drwav_uint64 dataSubchunkPaddingSize = drwav__chunk_padding_size_w64(dataChunkSize); + ma_uint64 dataSubchunkPaddingSize = ma_dr_wav__chunk_padding_size_w64(dataChunkSize); return 80 + 24 + dataChunkSize + dataSubchunkPaddingSize; } -DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_w64(drwav_uint64 dataChunkSize) +MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_w64(ma_uint64 dataChunkSize) { return 24 + dataChunkSize; } -DRWAV_PRIVATE drwav_uint64 drwav__riff_chunk_size_rf64(drwav_uint64 dataChunkSize, drwav_metadata *metadata, drwav_uint32 numMetadata) +MA_PRIVATE ma_uint64 ma_dr_wav__riff_chunk_size_rf64(ma_uint64 dataChunkSize, ma_dr_wav_metadata *metadata, ma_uint32 numMetadata) { - drwav_uint64 chunkSize = 4 + 36 + 24 + (drwav_uint64)drwav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + drwav__chunk_padding_size_riff(dataChunkSize); + ma_uint64 chunkSize = 4 + 36 + 24 + (ma_uint64)ma_dr_wav__write_or_count_metadata(NULL, metadata, numMetadata) + 8 + dataChunkSize + ma_dr_wav__chunk_padding_size_riff(dataChunkSize); if (chunkSize > 0xFFFFFFFFUL) { chunkSize = 0xFFFFFFFFUL; } return chunkSize; } -DRWAV_PRIVATE drwav_uint64 drwav__data_chunk_size_rf64(drwav_uint64 dataChunkSize) +MA_PRIVATE ma_uint64 ma_dr_wav__data_chunk_size_rf64(ma_uint64 dataChunkSize) { return dataChunkSize; } -DRWAV_PRIVATE drwav_bool32 drwav_preinit_write(drwav* pWav, const drwav_data_format* pFormat, drwav_bool32 isSequential, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE ma_bool32 ma_dr_wav_preinit_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_bool32 isSequential, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { if (pWav == NULL || onWrite == NULL) { - return DRWAV_FALSE; + return MA_FALSE; } if (!isSequential && onSeek == NULL) { - return DRWAV_FALSE; + return MA_FALSE; } - if (pFormat->format == DR_WAVE_FORMAT_EXTENSIBLE) { - return DRWAV_FALSE; + if (pFormat->format == MA_DR_WAVE_FORMAT_EXTENSIBLE) { + return MA_FALSE; } - if (pFormat->format == DR_WAVE_FORMAT_ADPCM || pFormat->format == DR_WAVE_FORMAT_DVI_ADPCM) { - return DRWAV_FALSE; + if (pFormat->format == MA_DR_WAVE_FORMAT_ADPCM || pFormat->format == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + return MA_FALSE; } - DRWAV_ZERO_MEMORY(pWav, sizeof(*pWav)); + MA_DR_WAV_ZERO_MEMORY(pWav, sizeof(*pWav)); pWav->onWrite = onWrite; pWav->onSeek = onSeek; pWav->pUserData = pUserData; - pWav->allocationCallbacks = drwav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + pWav->allocationCallbacks = ma_dr_wav_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); if (pWav->allocationCallbacks.onFree == NULL || (pWav->allocationCallbacks.onMalloc == NULL && pWav->allocationCallbacks.onRealloc == NULL)) { - return DRWAV_FALSE; + return MA_FALSE; } - pWav->fmt.formatTag = (drwav_uint16)pFormat->format; - pWav->fmt.channels = (drwav_uint16)pFormat->channels; + pWav->fmt.formatTag = (ma_uint16)pFormat->format; + pWav->fmt.channels = (ma_uint16)pFormat->channels; pWav->fmt.sampleRate = pFormat->sampleRate; - pWav->fmt.avgBytesPerSec = (drwav_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8); - pWav->fmt.blockAlign = (drwav_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8); - pWav->fmt.bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; + pWav->fmt.avgBytesPerSec = (ma_uint32)((pFormat->bitsPerSample * pFormat->sampleRate * pFormat->channels) / 8); + pWav->fmt.blockAlign = (ma_uint16)((pFormat->channels * pFormat->bitsPerSample) / 8); + pWav->fmt.bitsPerSample = (ma_uint16)pFormat->bitsPerSample; pWav->fmt.extendedSize = 0; pWav->isSequentialWrite = isSequential; - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_PRIVATE drwav_bool32 drwav_init_write__internal(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount) +MA_PRIVATE ma_bool32 ma_dr_wav_init_write__internal(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount) { size_t runningPos = 0; - drwav_uint64 initialDataChunkSize = 0; - drwav_uint64 chunkSizeFMT; + ma_uint64 initialDataChunkSize = 0; + ma_uint64 chunkSizeFMT; if (pWav->isSequentialWrite) { initialDataChunkSize = (totalSampleCount * pWav->fmt.bitsPerSample) / 8; - if (pFormat->container == drwav_container_riff) { + if (pFormat->container == ma_dr_wav_container_riff) { if (initialDataChunkSize > (0xFFFFFFFFUL - 36)) { - return DRWAV_FALSE; + return MA_FALSE; } } } pWav->dataChunkDataSizeTargetWrite = initialDataChunkSize; - if (pFormat->container == drwav_container_riff) { - drwav_uint32 chunkSizeRIFF = 28 + (drwav_uint32)initialDataChunkSize; - runningPos += drwav__write(pWav, "RIFF", 4); - runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeRIFF); - runningPos += drwav__write(pWav, "WAVE", 4); - } else if (pFormat->container == drwav_container_w64) { - drwav_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; - runningPos += drwav__write(pWav, drwavGUID_W64_RIFF, 16); - runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeRIFF); - runningPos += drwav__write(pWav, drwavGUID_W64_WAVE, 16); - } else if (pFormat->container == drwav_container_rf64) { - runningPos += drwav__write(pWav, "RF64", 4); - runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF); - runningPos += drwav__write(pWav, "WAVE", 4); + if (pFormat->container == ma_dr_wav_container_riff) { + ma_uint32 chunkSizeRIFF = 28 + (ma_uint32)initialDataChunkSize; + runningPos += ma_dr_wav__write(pWav, "RIFF", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeRIFF); + runningPos += ma_dr_wav__write(pWav, "WAVE", 4); + } else if (pFormat->container == ma_dr_wav_container_w64) { + ma_uint64 chunkSizeRIFF = 80 + 24 + initialDataChunkSize; + runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_RIFF, 16); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeRIFF); + runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_WAVE, 16); + } else if (pFormat->container == ma_dr_wav_container_rf64) { + runningPos += ma_dr_wav__write(pWav, "RF64", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF); + runningPos += ma_dr_wav__write(pWav, "WAVE", 4); + } else { + return MA_FALSE; } - if (pFormat->container == drwav_container_rf64) { - drwav_uint32 initialds64ChunkSize = 28; - drwav_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize; - runningPos += drwav__write(pWav, "ds64", 4); - runningPos += drwav__write_u32ne_to_le(pWav, initialds64ChunkSize); - runningPos += drwav__write_u64ne_to_le(pWav, initialRiffChunkSize); - runningPos += drwav__write_u64ne_to_le(pWav, initialDataChunkSize); - runningPos += drwav__write_u64ne_to_le(pWav, totalSampleCount); - runningPos += drwav__write_u32ne_to_le(pWav, 0); + if (pFormat->container == ma_dr_wav_container_rf64) { + ma_uint32 initialds64ChunkSize = 28; + ma_uint64 initialRiffChunkSize = 8 + initialds64ChunkSize + initialDataChunkSize; + runningPos += ma_dr_wav__write(pWav, "ds64", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, initialds64ChunkSize); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialRiffChunkSize); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, initialDataChunkSize); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, totalSampleCount); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0); } - if (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64) { + if (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64) { chunkSizeFMT = 16; - runningPos += drwav__write(pWav, "fmt ", 4); - runningPos += drwav__write_u32ne_to_le(pWav, (drwav_uint32)chunkSizeFMT); - } else if (pFormat->container == drwav_container_w64) { + runningPos += ma_dr_wav__write(pWav, "fmt ", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, (ma_uint32)chunkSizeFMT); + } else if (pFormat->container == ma_dr_wav_container_w64) { chunkSizeFMT = 40; - runningPos += drwav__write(pWav, drwavGUID_W64_FMT, 16); - runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeFMT); + runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_FMT, 16); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeFMT); } - runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.formatTag); - runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.channels); - runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate); - runningPos += drwav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec); - runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign); - runningPos += drwav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample); - if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == drwav_container_riff || pFormat->container == drwav_container_rf64)) { - runningPos += drwav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount); + runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.formatTag); + runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.channels); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.sampleRate); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, pWav->fmt.avgBytesPerSec); + runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.blockAlign); + runningPos += ma_dr_wav__write_u16ne_to_le(pWav, pWav->fmt.bitsPerSample); + if (!pWav->isSequentialWrite && pWav->pMetadata != NULL && pWav->metadataCount > 0 && (pFormat->container == ma_dr_wav_container_riff || pFormat->container == ma_dr_wav_container_rf64)) { + runningPos += ma_dr_wav__write_or_count_metadata(pWav, pWav->pMetadata, pWav->metadataCount); } pWav->dataChunkDataPos = runningPos; - if (pFormat->container == drwav_container_riff) { - drwav_uint32 chunkSizeDATA = (drwav_uint32)initialDataChunkSize; - runningPos += drwav__write(pWav, "data", 4); - runningPos += drwav__write_u32ne_to_le(pWav, chunkSizeDATA); - } else if (pFormat->container == drwav_container_w64) { - drwav_uint64 chunkSizeDATA = 24 + initialDataChunkSize; - runningPos += drwav__write(pWav, drwavGUID_W64_DATA, 16); - runningPos += drwav__write_u64ne_to_le(pWav, chunkSizeDATA); - } else if (pFormat->container == drwav_container_rf64) { - runningPos += drwav__write(pWav, "data", 4); - runningPos += drwav__write_u32ne_to_le(pWav, 0xFFFFFFFF); + if (pFormat->container == ma_dr_wav_container_riff) { + ma_uint32 chunkSizeDATA = (ma_uint32)initialDataChunkSize; + runningPos += ma_dr_wav__write(pWav, "data", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, chunkSizeDATA); + } else if (pFormat->container == ma_dr_wav_container_w64) { + ma_uint64 chunkSizeDATA = 24 + initialDataChunkSize; + runningPos += ma_dr_wav__write(pWav, ma_dr_wavGUID_W64_DATA, 16); + runningPos += ma_dr_wav__write_u64ne_to_le(pWav, chunkSizeDATA); + } else if (pFormat->container == ma_dr_wav_container_rf64) { + runningPos += ma_dr_wav__write(pWav, "data", 4); + runningPos += ma_dr_wav__write_u32ne_to_le(pWav, 0xFFFFFFFF); } pWav->container = pFormat->container; - pWav->channels = (drwav_uint16)pFormat->channels; + pWav->channels = (ma_uint16)pFormat->channels; pWav->sampleRate = pFormat->sampleRate; - pWav->bitsPerSample = (drwav_uint16)pFormat->bitsPerSample; - pWav->translatedFormatTag = (drwav_uint16)pFormat->format; + pWav->bitsPerSample = (ma_uint16)pFormat->bitsPerSample; + pWav->translatedFormatTag = (ma_uint16)pFormat->format; pWav->dataChunkDataPos = runningPos; - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_API drwav_bool32 drwav_init_write(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_write(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { - return DRWAV_FALSE; + if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { + return MA_FALSE; } - return drwav_init_write__internal(pWav, pFormat, 0); + return ma_dr_wav_init_write__internal(pWav, pFormat, 0); } -DRWAV_API drwav_bool32 drwav_init_write_sequential(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_write_sequential(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - if (!drwav_preinit_write(pWav, pFormat, DRWAV_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) { - return DRWAV_FALSE; + if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_TRUE, onWrite, NULL, pUserData, pAllocationCallbacks)) { + return MA_FALSE; } - return drwav_init_write__internal(pWav, pFormat, totalSampleCount); + return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); } -DRWAV_API drwav_bool32 drwav_init_write_sequential_pcm_frames(drwav* pWav, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, drwav_write_proc onWrite, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_write_sequential_pcm_frames(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, ma_dr_wav_write_proc onWrite, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { if (pFormat == NULL) { - return DRWAV_FALSE; + return MA_FALSE; } - return drwav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks); + return ma_dr_wav_init_write_sequential(pWav, pFormat, totalPCMFrameCount*pFormat->channels, onWrite, pUserData, pAllocationCallbacks); } -DRWAV_API drwav_bool32 drwav_init_write_with_metadata(drwav* pWav, const drwav_data_format* pFormat, drwav_write_proc onWrite, drwav_seek_proc onSeek, void* pUserData, const drwav_allocation_callbacks* pAllocationCallbacks, drwav_metadata* pMetadata, drwav_uint32 metadataCount) +MA_API ma_bool32 ma_dr_wav_init_write_with_metadata(ma_dr_wav* pWav, const ma_dr_wav_data_format* pFormat, ma_dr_wav_write_proc onWrite, ma_dr_wav_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) { - if (!drwav_preinit_write(pWav, pFormat, DRWAV_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { - return DRWAV_FALSE; + if (!ma_dr_wav_preinit_write(pWav, pFormat, MA_FALSE, onWrite, onSeek, pUserData, pAllocationCallbacks)) { + return MA_FALSE; } pWav->pMetadata = pMetadata; pWav->metadataCount = metadataCount; - return drwav_init_write__internal(pWav, pFormat, 0); + return ma_dr_wav_init_write__internal(pWav, pFormat, 0); } -DRWAV_API drwav_uint64 drwav_target_write_size_bytes(const drwav_data_format* pFormat, drwav_uint64 totalFrameCount, drwav_metadata* pMetadata, drwav_uint32 metadataCount) +MA_API ma_uint64 ma_dr_wav_target_write_size_bytes(const ma_dr_wav_data_format* pFormat, ma_uint64 totalFrameCount, ma_dr_wav_metadata* pMetadata, ma_uint32 metadataCount) { - drwav_uint64 targetDataSizeBytes = (drwav_uint64)((drwav_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0); - drwav_uint64 riffChunkSizeBytes; - drwav_uint64 fileSizeBytes = 0; - if (pFormat->container == drwav_container_riff) { - riffChunkSizeBytes = drwav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount); + ma_uint64 targetDataSizeBytes = (ma_uint64)((ma_int64)totalFrameCount * pFormat->channels * pFormat->bitsPerSample/8.0); + ma_uint64 riffChunkSizeBytes; + ma_uint64 fileSizeBytes = 0; + if (pFormat->container == ma_dr_wav_container_riff) { + riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_riff(targetDataSizeBytes, pMetadata, metadataCount); fileSizeBytes = (8 + riffChunkSizeBytes); - } else if (pFormat->container == drwav_container_w64) { - riffChunkSizeBytes = drwav__riff_chunk_size_w64(targetDataSizeBytes); + } else if (pFormat->container == ma_dr_wav_container_w64) { + riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_w64(targetDataSizeBytes); fileSizeBytes = riffChunkSizeBytes; - } else if (pFormat->container == drwav_container_rf64) { - riffChunkSizeBytes = drwav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount); + } else if (pFormat->container == ma_dr_wav_container_rf64) { + riffChunkSizeBytes = ma_dr_wav__riff_chunk_size_rf64(targetDataSizeBytes, pMetadata, metadataCount); fileSizeBytes = (8 + riffChunkSizeBytes); } return fileSizeBytes; } -#ifndef DR_WAV_NO_STDIO -#include -DRWAV_PRIVATE drwav_result drwav_result_from_errno(int e) -{ - switch (e) - { - case 0: return DRWAV_SUCCESS; - #ifdef EPERM - case EPERM: return DRWAV_INVALID_OPERATION; - #endif - #ifdef ENOENT - case ENOENT: return DRWAV_DOES_NOT_EXIST; - #endif - #ifdef ESRCH - case ESRCH: return DRWAV_DOES_NOT_EXIST; - #endif - #ifdef EINTR - case EINTR: return DRWAV_INTERRUPT; - #endif - #ifdef EIO - case EIO: return DRWAV_IO_ERROR; - #endif - #ifdef ENXIO - case ENXIO: return DRWAV_DOES_NOT_EXIST; - #endif - #ifdef E2BIG - case E2BIG: return DRWAV_INVALID_ARGS; - #endif - #ifdef ENOEXEC - case ENOEXEC: return DRWAV_INVALID_FILE; - #endif - #ifdef EBADF - case EBADF: return DRWAV_INVALID_FILE; - #endif - #ifdef ECHILD - case ECHILD: return DRWAV_ERROR; - #endif - #ifdef EAGAIN - case EAGAIN: return DRWAV_UNAVAILABLE; - #endif - #ifdef ENOMEM - case ENOMEM: return DRWAV_OUT_OF_MEMORY; - #endif - #ifdef EACCES - case EACCES: return DRWAV_ACCESS_DENIED; - #endif - #ifdef EFAULT - case EFAULT: return DRWAV_BAD_ADDRESS; - #endif - #ifdef ENOTBLK - case ENOTBLK: return DRWAV_ERROR; - #endif - #ifdef EBUSY - case EBUSY: return DRWAV_BUSY; - #endif - #ifdef EEXIST - case EEXIST: return DRWAV_ALREADY_EXISTS; - #endif - #ifdef EXDEV - case EXDEV: return DRWAV_ERROR; - #endif - #ifdef ENODEV - case ENODEV: return DRWAV_DOES_NOT_EXIST; - #endif - #ifdef ENOTDIR - case ENOTDIR: return DRWAV_NOT_DIRECTORY; - #endif - #ifdef EISDIR - case EISDIR: return DRWAV_IS_DIRECTORY; - #endif - #ifdef EINVAL - case EINVAL: return DRWAV_INVALID_ARGS; - #endif - #ifdef ENFILE - case ENFILE: return DRWAV_TOO_MANY_OPEN_FILES; - #endif - #ifdef EMFILE - case EMFILE: return DRWAV_TOO_MANY_OPEN_FILES; - #endif - #ifdef ENOTTY - case ENOTTY: return DRWAV_INVALID_OPERATION; - #endif - #ifdef ETXTBSY - case ETXTBSY: return DRWAV_BUSY; - #endif - #ifdef EFBIG - case EFBIG: return DRWAV_TOO_BIG; - #endif - #ifdef ENOSPC - case ENOSPC: return DRWAV_NO_SPACE; - #endif - #ifdef ESPIPE - case ESPIPE: return DRWAV_BAD_SEEK; - #endif - #ifdef EROFS - case EROFS: return DRWAV_ACCESS_DENIED; - #endif - #ifdef EMLINK - case EMLINK: return DRWAV_TOO_MANY_LINKS; - #endif - #ifdef EPIPE - case EPIPE: return DRWAV_BAD_PIPE; - #endif - #ifdef EDOM - case EDOM: return DRWAV_OUT_OF_RANGE; - #endif - #ifdef ERANGE - case ERANGE: return DRWAV_OUT_OF_RANGE; - #endif - #ifdef EDEADLK - case EDEADLK: return DRWAV_DEADLOCK; - #endif - #ifdef ENAMETOOLONG - case ENAMETOOLONG: return DRWAV_PATH_TOO_LONG; - #endif - #ifdef ENOLCK - case ENOLCK: return DRWAV_ERROR; - #endif - #ifdef ENOSYS - case ENOSYS: return DRWAV_NOT_IMPLEMENTED; - #endif - #ifdef ENOTEMPTY - case ENOTEMPTY: return DRWAV_DIRECTORY_NOT_EMPTY; - #endif - #ifdef ELOOP - case ELOOP: return DRWAV_TOO_MANY_LINKS; - #endif - #ifdef ENOMSG - case ENOMSG: return DRWAV_NO_MESSAGE; - #endif - #ifdef EIDRM - case EIDRM: return DRWAV_ERROR; - #endif - #ifdef ECHRNG - case ECHRNG: return DRWAV_ERROR; - #endif - #ifdef EL2NSYNC - case EL2NSYNC: return DRWAV_ERROR; - #endif - #ifdef EL3HLT - case EL3HLT: return DRWAV_ERROR; - #endif - #ifdef EL3RST - case EL3RST: return DRWAV_ERROR; - #endif - #ifdef ELNRNG - case ELNRNG: return DRWAV_OUT_OF_RANGE; - #endif - #ifdef EUNATCH - case EUNATCH: return DRWAV_ERROR; - #endif - #ifdef ENOCSI - case ENOCSI: return DRWAV_ERROR; - #endif - #ifdef EL2HLT - case EL2HLT: return DRWAV_ERROR; - #endif - #ifdef EBADE - case EBADE: return DRWAV_ERROR; - #endif - #ifdef EBADR - case EBADR: return DRWAV_ERROR; - #endif - #ifdef EXFULL - case EXFULL: return DRWAV_ERROR; - #endif - #ifdef ENOANO - case ENOANO: return DRWAV_ERROR; - #endif - #ifdef EBADRQC - case EBADRQC: return DRWAV_ERROR; - #endif - #ifdef EBADSLT - case EBADSLT: return DRWAV_ERROR; - #endif - #ifdef EBFONT - case EBFONT: return DRWAV_INVALID_FILE; - #endif - #ifdef ENOSTR - case ENOSTR: return DRWAV_ERROR; - #endif - #ifdef ENODATA - case ENODATA: return DRWAV_NO_DATA_AVAILABLE; - #endif - #ifdef ETIME - case ETIME: return DRWAV_TIMEOUT; - #endif - #ifdef ENOSR - case ENOSR: return DRWAV_NO_DATA_AVAILABLE; - #endif - #ifdef ENONET - case ENONET: return DRWAV_NO_NETWORK; - #endif - #ifdef ENOPKG - case ENOPKG: return DRWAV_ERROR; - #endif - #ifdef EREMOTE - case EREMOTE: return DRWAV_ERROR; - #endif - #ifdef ENOLINK - case ENOLINK: return DRWAV_ERROR; - #endif - #ifdef EADV - case EADV: return DRWAV_ERROR; - #endif - #ifdef ESRMNT - case ESRMNT: return DRWAV_ERROR; - #endif - #ifdef ECOMM - case ECOMM: return DRWAV_ERROR; - #endif - #ifdef EPROTO - case EPROTO: return DRWAV_ERROR; - #endif - #ifdef EMULTIHOP - case EMULTIHOP: return DRWAV_ERROR; - #endif - #ifdef EDOTDOT - case EDOTDOT: return DRWAV_ERROR; - #endif - #ifdef EBADMSG - case EBADMSG: return DRWAV_BAD_MESSAGE; - #endif - #ifdef EOVERFLOW - case EOVERFLOW: return DRWAV_TOO_BIG; - #endif - #ifdef ENOTUNIQ - case ENOTUNIQ: return DRWAV_NOT_UNIQUE; - #endif - #ifdef EBADFD - case EBADFD: return DRWAV_ERROR; - #endif - #ifdef EREMCHG - case EREMCHG: return DRWAV_ERROR; - #endif - #ifdef ELIBACC - case ELIBACC: return DRWAV_ACCESS_DENIED; - #endif - #ifdef ELIBBAD - case ELIBBAD: return DRWAV_INVALID_FILE; - #endif - #ifdef ELIBSCN - case ELIBSCN: return DRWAV_INVALID_FILE; - #endif - #ifdef ELIBMAX - case ELIBMAX: return DRWAV_ERROR; - #endif - #ifdef ELIBEXEC - case ELIBEXEC: return DRWAV_ERROR; - #endif - #ifdef EILSEQ - case EILSEQ: return DRWAV_INVALID_DATA; - #endif - #ifdef ERESTART - case ERESTART: return DRWAV_ERROR; - #endif - #ifdef ESTRPIPE - case ESTRPIPE: return DRWAV_ERROR; - #endif - #ifdef EUSERS - case EUSERS: return DRWAV_ERROR; - #endif - #ifdef ENOTSOCK - case ENOTSOCK: return DRWAV_NOT_SOCKET; - #endif - #ifdef EDESTADDRREQ - case EDESTADDRREQ: return DRWAV_NO_ADDRESS; - #endif - #ifdef EMSGSIZE - case EMSGSIZE: return DRWAV_TOO_BIG; - #endif - #ifdef EPROTOTYPE - case EPROTOTYPE: return DRWAV_BAD_PROTOCOL; - #endif - #ifdef ENOPROTOOPT - case ENOPROTOOPT: return DRWAV_PROTOCOL_UNAVAILABLE; - #endif - #ifdef EPROTONOSUPPORT - case EPROTONOSUPPORT: return DRWAV_PROTOCOL_NOT_SUPPORTED; - #endif - #ifdef ESOCKTNOSUPPORT - case ESOCKTNOSUPPORT: return DRWAV_SOCKET_NOT_SUPPORTED; - #endif - #ifdef EOPNOTSUPP - case EOPNOTSUPP: return DRWAV_INVALID_OPERATION; - #endif - #ifdef EPFNOSUPPORT - case EPFNOSUPPORT: return DRWAV_PROTOCOL_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EAFNOSUPPORT - case EAFNOSUPPORT: return DRWAV_ADDRESS_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EADDRINUSE - case EADDRINUSE: return DRWAV_ALREADY_IN_USE; - #endif - #ifdef EADDRNOTAVAIL - case EADDRNOTAVAIL: return DRWAV_ERROR; - #endif - #ifdef ENETDOWN - case ENETDOWN: return DRWAV_NO_NETWORK; - #endif - #ifdef ENETUNREACH - case ENETUNREACH: return DRWAV_NO_NETWORK; - #endif - #ifdef ENETRESET - case ENETRESET: return DRWAV_NO_NETWORK; - #endif - #ifdef ECONNABORTED - case ECONNABORTED: return DRWAV_NO_NETWORK; - #endif - #ifdef ECONNRESET - case ECONNRESET: return DRWAV_CONNECTION_RESET; - #endif - #ifdef ENOBUFS - case ENOBUFS: return DRWAV_NO_SPACE; - #endif - #ifdef EISCONN - case EISCONN: return DRWAV_ALREADY_CONNECTED; - #endif - #ifdef ENOTCONN - case ENOTCONN: return DRWAV_NOT_CONNECTED; - #endif - #ifdef ESHUTDOWN - case ESHUTDOWN: return DRWAV_ERROR; - #endif - #ifdef ETOOMANYREFS - case ETOOMANYREFS: return DRWAV_ERROR; - #endif - #ifdef ETIMEDOUT - case ETIMEDOUT: return DRWAV_TIMEOUT; - #endif - #ifdef ECONNREFUSED - case ECONNREFUSED: return DRWAV_CONNECTION_REFUSED; - #endif - #ifdef EHOSTDOWN - case EHOSTDOWN: return DRWAV_NO_HOST; - #endif - #ifdef EHOSTUNREACH - case EHOSTUNREACH: return DRWAV_NO_HOST; - #endif - #ifdef EALREADY - case EALREADY: return DRWAV_IN_PROGRESS; - #endif - #ifdef EINPROGRESS - case EINPROGRESS: return DRWAV_IN_PROGRESS; - #endif - #ifdef ESTALE - case ESTALE: return DRWAV_INVALID_FILE; - #endif - #ifdef EUCLEAN - case EUCLEAN: return DRWAV_ERROR; - #endif - #ifdef ENOTNAM - case ENOTNAM: return DRWAV_ERROR; - #endif - #ifdef ENAVAIL - case ENAVAIL: return DRWAV_ERROR; - #endif - #ifdef EISNAM - case EISNAM: return DRWAV_ERROR; - #endif - #ifdef EREMOTEIO - case EREMOTEIO: return DRWAV_IO_ERROR; - #endif - #ifdef EDQUOT - case EDQUOT: return DRWAV_NO_SPACE; - #endif - #ifdef ENOMEDIUM - case ENOMEDIUM: return DRWAV_DOES_NOT_EXIST; - #endif - #ifdef EMEDIUMTYPE - case EMEDIUMTYPE: return DRWAV_ERROR; - #endif - #ifdef ECANCELED - case ECANCELED: return DRWAV_CANCELLED; - #endif - #ifdef ENOKEY - case ENOKEY: return DRWAV_ERROR; - #endif - #ifdef EKEYEXPIRED - case EKEYEXPIRED: return DRWAV_ERROR; - #endif - #ifdef EKEYREVOKED - case EKEYREVOKED: return DRWAV_ERROR; - #endif - #ifdef EKEYREJECTED - case EKEYREJECTED: return DRWAV_ERROR; - #endif - #ifdef EOWNERDEAD - case EOWNERDEAD: return DRWAV_ERROR; - #endif - #ifdef ENOTRECOVERABLE - case ENOTRECOVERABLE: return DRWAV_ERROR; - #endif - #ifdef ERFKILL - case ERFKILL: return DRWAV_ERROR; - #endif - #ifdef EHWPOISON - case EHWPOISON: return DRWAV_ERROR; - #endif - default: return DRWAV_ERROR; - } -} -DRWAV_PRIVATE drwav_result drwav_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) -{ -#if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err; -#endif - if (ppFile != NULL) { - *ppFile = NULL; - } - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return DRWAV_INVALID_ARGS; - } -#if defined(_MSC_VER) && _MSC_VER >= 1400 - err = fopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return drwav_result_from_errno(err); - } -#else -#if defined(_WIN32) || defined(__APPLE__) - *ppFile = fopen(pFilePath, pOpenMode); -#else - #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) - *ppFile = fopen64(pFilePath, pOpenMode); - #else - *ppFile = fopen(pFilePath, pOpenMode); - #endif -#endif - if (*ppFile == NULL) { - drwav_result result = drwav_result_from_errno(errno); - if (result == DRWAV_SUCCESS) { - result = DRWAV_ERROR; - } - return result; - } -#endif - return DRWAV_SUCCESS; -} -#if defined(_WIN32) - #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) - #define DRWAV_HAS_WFOPEN - #endif -#endif -#ifndef DR_WAV_NO_WCHAR -DRWAV_PRIVATE drwav_result drwav_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drwav_allocation_callbacks* pAllocationCallbacks) -{ - if (ppFile != NULL) { - *ppFile = NULL; - } - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return DRWAV_INVALID_ARGS; - } -#if defined(DRWAV_HAS_WFOPEN) - { - #if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return drwav_result_from_errno(err); - } - #else - *ppFile = _wfopen(pFilePath, pOpenMode); - if (*ppFile == NULL) { - return drwav_result_from_errno(errno); - } - #endif - (void)pAllocationCallbacks; - } -#else - #if defined(__DJGPP__) - { - } - #else - { - mbstate_t mbs; - size_t lenMB; - const wchar_t* pFilePathTemp = pFilePath; - char* pFilePathMB = NULL; - char pOpenModeMB[32] = {0}; - DRWAV_ZERO_OBJECT(&mbs); - lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); - if (lenMB == (size_t)-1) { - return drwav_result_from_errno(errno); - } - pFilePathMB = (char*)drwav__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); - if (pFilePathMB == NULL) { - return DRWAV_OUT_OF_MEMORY; - } - pFilePathTemp = pFilePath; - DRWAV_ZERO_OBJECT(&mbs); - wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); - { - size_t i = 0; - for (;;) { - if (pOpenMode[i] == 0) { - pOpenModeMB[i] = '\0'; - break; - } - pOpenModeMB[i] = (char)pOpenMode[i]; - i += 1; - } - } - *ppFile = fopen(pFilePathMB, pOpenModeMB); - drwav__free_from_callbacks(pFilePathMB, pAllocationCallbacks); - } - #endif - if (*ppFile == NULL) { - return DRWAV_ERROR; - } -#endif - return DRWAV_SUCCESS; -} -#endif -DRWAV_PRIVATE size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) +#ifndef MA_DR_WAV_NO_STDIO +MA_PRIVATE size_t ma_dr_wav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) { return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); } -DRWAV_PRIVATE size_t drwav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite) +MA_PRIVATE size_t ma_dr_wav__on_write_stdio(void* pUserData, const void* pData, size_t bytesToWrite) { return fwrite(pData, 1, bytesToWrite, (FILE*)pUserData); } -DRWAV_PRIVATE drwav_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin) +MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_stdio(void* pUserData, int offset, ma_dr_wav_seek_origin origin) { - return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; + return fseek((FILE*)pUserData, offset, (origin == ma_dr_wav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; } -DRWAV_API drwav_bool32 drwav_init_file(drwav* pWav, const char* filename, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_file(ma_dr_wav* pWav, const char* filename, const ma_allocation_callbacks* pAllocationCallbacks) { - return drwav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); + return ma_dr_wav_init_file_ex(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); } -DRWAV_PRIVATE drwav_bool32 drwav_init_file__internal_FILE(drwav* pWav, FILE* pFile, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, drwav_metadata_type allowedMetadataTypes, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE ma_bool32 ma_dr_wav_init_file__internal_FILE(ma_dr_wav* pWav, FILE* pFile, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav_bool32 result; - result = drwav_preinit(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != DRWAV_TRUE) { + ma_bool32 result; + result = ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != MA_TRUE) { fclose(pFile); return result; } - pWav->allowedMetadataTypes = allowedMetadataTypes; - result = drwav_init__internal(pWav, onChunk, pChunkUserData, flags); - if (result != DRWAV_TRUE) { + result = ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); + if (result != MA_TRUE) { fclose(pFile); return result; } - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_API drwav_bool32 drwav_init_file_ex(drwav* pWav, const char* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_file_ex(ma_dr_wav* pWav, const char* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; - if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) { - return DRWAV_FALSE; + if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) { + return MA_FALSE; } - return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks); + return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); } -#ifndef DR_WAV_NO_WCHAR -DRWAV_API drwav_bool32 drwav_init_file_w(drwav* pWav, const wchar_t* filename, const drwav_allocation_callbacks* pAllocationCallbacks) +#ifndef MA_DR_WAV_NO_WCHAR +MA_API ma_bool32 ma_dr_wav_init_file_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_allocation_callbacks* pAllocationCallbacks) { - return drwav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); + return ma_dr_wav_init_file_ex_w(pWav, filename, NULL, NULL, 0, pAllocationCallbacks); } -DRWAV_API drwav_bool32 drwav_init_file_ex_w(drwav* pWav, const wchar_t* filename, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_file_ex_w(ma_dr_wav* pWav, const wchar_t* filename, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; - if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) { - return DRWAV_FALSE; + if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) { + return MA_FALSE; } - return drwav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, drwav_metadata_type_none, pAllocationCallbacks); + return ma_dr_wav_init_file__internal_FILE(pWav, pFile, onChunk, pChunkUserData, flags, pAllocationCallbacks); } #endif -DRWAV_API drwav_bool32 drwav_init_file_with_metadata(drwav* pWav, const char* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_file_with_metadata(ma_dr_wav* pWav, const char* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; - if (drwav_fopen(&pFile, filename, "rb") != DRWAV_SUCCESS) { - return DRWAV_FALSE; + if (ma_fopen(&pFile, filename, "rb") != MA_SUCCESS) { + return MA_FALSE; } - return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks); + return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks); } -#ifndef DR_WAV_NO_WCHAR -DRWAV_API drwav_bool32 drwav_init_file_with_metadata_w(drwav* pWav, const wchar_t* filename, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +#ifndef MA_DR_WAV_NO_WCHAR +MA_API ma_bool32 ma_dr_wav_init_file_with_metadata_w(ma_dr_wav* pWav, const wchar_t* filename, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; - if (drwav_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != DRWAV_SUCCESS) { - return DRWAV_FALSE; + if (ma_wfopen(&pFile, filename, L"rb", pAllocationCallbacks) != MA_SUCCESS) { + return MA_FALSE; } - return drwav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags, drwav_metadata_type_all_including_unknown, pAllocationCallbacks); + return ma_dr_wav_init_file__internal_FILE(pWav, pFile, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA, pAllocationCallbacks); } #endif -DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal_FILE(drwav* pWav, FILE* pFile, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal_FILE(ma_dr_wav* pWav, FILE* pFile, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav_bool32 result; - result = drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_stdio, drwav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != DRWAV_TRUE) { + ma_bool32 result; + result = ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_stdio, ma_dr_wav__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != MA_TRUE) { fclose(pFile); return result; } - result = drwav_init_write__internal(pWav, pFormat, totalSampleCount); - if (result != DRWAV_TRUE) { + result = ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); + if (result != MA_TRUE) { fclose(pFile); return result; } - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_PRIVATE drwav_bool32 drwav_init_file_write__internal(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write__internal(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; - if (drwav_fopen(&pFile, filename, "wb") != DRWAV_SUCCESS) { - return DRWAV_FALSE; + if (ma_fopen(&pFile, filename, "wb") != MA_SUCCESS) { + return MA_FALSE; } - return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); + return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); } -#ifndef DR_WAV_NO_WCHAR -DRWAV_PRIVATE drwav_bool32 drwav_init_file_write_w__internal(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) +#ifndef MA_DR_WAV_NO_WCHAR +MA_PRIVATE ma_bool32 ma_dr_wav_init_file_write_w__internal(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) { FILE* pFile; - if (drwav_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != DRWAV_SUCCESS) { - return DRWAV_FALSE; + if (ma_wfopen(&pFile, filename, L"wb", pAllocationCallbacks) != MA_SUCCESS) { + return MA_FALSE; } - return drwav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); + return ma_dr_wav_init_file_write__internal_FILE(pWav, pFile, pFormat, totalSampleCount, isSequential, pAllocationCallbacks); } #endif -DRWAV_API drwav_bool32 drwav_init_file_write(drwav* pWav, const char* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_file_write(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) { - return drwav_init_file_write__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); + return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks); } -DRWAV_API drwav_bool32 drwav_init_file_write_sequential(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) { - return drwav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); + return ma_dr_wav_init_file_write__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); } -DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames(drwav* pWav, const char* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames(ma_dr_wav* pWav, const char* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { if (pFormat == NULL) { - return DRWAV_FALSE; + return MA_FALSE; } - return drwav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); + return ma_dr_wav_init_file_write_sequential(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); } -#ifndef DR_WAV_NO_WCHAR -DRWAV_API drwav_bool32 drwav_init_file_write_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) +#ifndef MA_DR_WAV_NO_WCHAR +MA_API ma_bool32 ma_dr_wav_init_file_write_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) { - return drwav_init_file_write_w__internal(pWav, filename, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); + return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, 0, MA_FALSE, pAllocationCallbacks); } -DRWAV_API drwav_bool32 drwav_init_file_write_sequential_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) { - return drwav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); + return ma_dr_wav_init_file_write_w__internal(pWav, filename, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); } -DRWAV_API drwav_bool32 drwav_init_file_write_sequential_pcm_frames_w(drwav* pWav, const wchar_t* filename, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_file_write_sequential_pcm_frames_w(ma_dr_wav* pWav, const wchar_t* filename, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { if (pFormat == NULL) { - return DRWAV_FALSE; + return MA_FALSE; } - return drwav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); + return ma_dr_wav_init_file_write_sequential_w(pWav, filename, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); } #endif #endif -DRWAV_PRIVATE size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) +MA_PRIVATE size_t ma_dr_wav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) { - drwav* pWav = (drwav*)pUserData; + ma_dr_wav* pWav = (ma_dr_wav*)pUserData; size_t bytesRemaining; - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos); + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->memoryStream.dataSize >= pWav->memoryStream.currentReadPos); bytesRemaining = pWav->memoryStream.dataSize - pWav->memoryStream.currentReadPos; if (bytesToRead > bytesRemaining) { bytesToRead = bytesRemaining; } if (bytesToRead > 0) { - DRWAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead); + MA_DR_WAV_COPY_MEMORY(pBufferOut, pWav->memoryStream.data + pWav->memoryStream.currentReadPos, bytesToRead); pWav->memoryStream.currentReadPos += bytesToRead; } return bytesToRead; } -DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin) +MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory(void* pUserData, int offset, ma_dr_wav_seek_origin origin) { - drwav* pWav = (drwav*)pUserData; - DRWAV_ASSERT(pWav != NULL); - if (origin == drwav_seek_origin_current) { + ma_dr_wav* pWav = (ma_dr_wav*)pUserData; + MA_DR_WAV_ASSERT(pWav != NULL); + if (origin == ma_dr_wav_seek_origin_current) { if (offset > 0) { if (pWav->memoryStream.currentReadPos + offset > pWav->memoryStream.dataSize) { - return DRWAV_FALSE; + return MA_FALSE; } } else { if (pWav->memoryStream.currentReadPos < (size_t)-offset) { - return DRWAV_FALSE; + return MA_FALSE; } } pWav->memoryStream.currentReadPos += offset; } else { - if ((drwav_uint32)offset <= pWav->memoryStream.dataSize) { + if ((ma_uint32)offset <= pWav->memoryStream.dataSize) { pWav->memoryStream.currentReadPos = offset; } else { - return DRWAV_FALSE; + return MA_FALSE; } } - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_PRIVATE size_t drwav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) +MA_PRIVATE size_t ma_dr_wav__on_write_memory(void* pUserData, const void* pDataIn, size_t bytesToWrite) { - drwav* pWav = (drwav*)pUserData; + ma_dr_wav* pWav = (ma_dr_wav*)pUserData; size_t bytesRemaining; - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos); + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(pWav->memoryStreamWrite.dataCapacity >= pWav->memoryStreamWrite.currentWritePos); bytesRemaining = pWav->memoryStreamWrite.dataCapacity - pWav->memoryStreamWrite.currentWritePos; if (bytesRemaining < bytesToWrite) { void* pNewData; @@ -79582,14 +79751,14 @@ DRWAV_PRIVATE size_t drwav__on_write_memory(void* pUserData, const void* pDataIn if ((newDataCapacity - pWav->memoryStreamWrite.currentWritePos) < bytesToWrite) { newDataCapacity = pWav->memoryStreamWrite.currentWritePos + bytesToWrite; } - pNewData = drwav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks); + pNewData = ma_dr_wav__realloc_from_callbacks(*pWav->memoryStreamWrite.ppData, newDataCapacity, pWav->memoryStreamWrite.dataCapacity, &pWav->allocationCallbacks); if (pNewData == NULL) { return 0; } *pWav->memoryStreamWrite.ppData = pNewData; pWav->memoryStreamWrite.dataCapacity = newDataCapacity; } - DRWAV_COPY_MEMORY(((drwav_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite); + MA_DR_WAV_COPY_MEMORY(((ma_uint8*)(*pWav->memoryStreamWrite.ppData)) + pWav->memoryStreamWrite.currentWritePos, pDataIn, bytesToWrite); pWav->memoryStreamWrite.currentWritePos += bytesToWrite; if (pWav->memoryStreamWrite.dataSize < pWav->memoryStreamWrite.currentWritePos) { pWav->memoryStreamWrite.dataSize = pWav->memoryStreamWrite.currentWritePos; @@ -79597,11 +79766,11 @@ DRWAV_PRIVATE size_t drwav__on_write_memory(void* pUserData, const void* pDataIn *pWav->memoryStreamWrite.pDataSize = pWav->memoryStreamWrite.dataSize; return bytesToWrite; } -DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offset, drwav_seek_origin origin) +MA_PRIVATE ma_bool32 ma_dr_wav__on_seek_memory_write(void* pUserData, int offset, ma_dr_wav_seek_origin origin) { - drwav* pWav = (drwav*)pUserData; - DRWAV_ASSERT(pWav != NULL); - if (origin == drwav_seek_origin_current) { + ma_dr_wav* pWav = (ma_dr_wav*)pUserData; + MA_DR_WAV_ASSERT(pWav != NULL); + if (origin == ma_dr_wav_seek_origin_current) { if (offset > 0) { if (pWav->memoryStreamWrite.currentWritePos + offset > pWav->memoryStreamWrite.dataSize) { offset = (int)(pWav->memoryStreamWrite.dataSize - pWav->memoryStreamWrite.currentWritePos); @@ -79613,146 +79782,143 @@ DRWAV_PRIVATE drwav_bool32 drwav__on_seek_memory_write(void* pUserData, int offs } pWav->memoryStreamWrite.currentWritePos += offset; } else { - if ((drwav_uint32)offset <= pWav->memoryStreamWrite.dataSize) { + if ((ma_uint32)offset <= pWav->memoryStreamWrite.dataSize) { pWav->memoryStreamWrite.currentWritePos = offset; } else { pWav->memoryStreamWrite.currentWritePos = pWav->memoryStreamWrite.dataSize; } } - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_API drwav_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_memory(ma_dr_wav* pWav, const void* data, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) { - return drwav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks); + return ma_dr_wav_init_memory_ex(pWav, data, dataSize, NULL, NULL, 0, pAllocationCallbacks); } -DRWAV_API drwav_bool32 drwav_init_memory_ex(drwav* pWav, const void* data, size_t dataSize, drwav_chunk_proc onChunk, void* pChunkUserData, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_memory_ex(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_dr_wav_chunk_proc onChunk, void* pChunkUserData, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { if (data == NULL || dataSize == 0) { - return DRWAV_FALSE; + return MA_FALSE; } - if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) { - return DRWAV_FALSE; + if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { + return MA_FALSE; } - pWav->memoryStream.data = (const drwav_uint8*)data; + pWav->memoryStream.data = (const ma_uint8*)data; pWav->memoryStream.dataSize = dataSize; pWav->memoryStream.currentReadPos = 0; - return drwav_init__internal(pWav, onChunk, pChunkUserData, flags); + return ma_dr_wav_init__internal(pWav, onChunk, pChunkUserData, flags); } -DRWAV_API drwav_bool32 drwav_init_memory_with_metadata(drwav* pWav, const void* data, size_t dataSize, drwav_uint32 flags, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_memory_with_metadata(ma_dr_wav* pWav, const void* data, size_t dataSize, ma_uint32 flags, const ma_allocation_callbacks* pAllocationCallbacks) { if (data == NULL || dataSize == 0) { - return DRWAV_FALSE; + return MA_FALSE; } - if (!drwav_preinit(pWav, drwav__on_read_memory, drwav__on_seek_memory, pWav, pAllocationCallbacks)) { - return DRWAV_FALSE; + if (!ma_dr_wav_preinit(pWav, ma_dr_wav__on_read_memory, ma_dr_wav__on_seek_memory, pWav, pAllocationCallbacks)) { + return MA_FALSE; } - pWav->memoryStream.data = (const drwav_uint8*)data; + pWav->memoryStream.data = (const ma_uint8*)data; pWav->memoryStream.dataSize = dataSize; pWav->memoryStream.currentReadPos = 0; - pWav->allowedMetadataTypes = drwav_metadata_type_all_including_unknown; - return drwav_init__internal(pWav, NULL, NULL, flags); + return ma_dr_wav_init__internal(pWav, NULL, NULL, flags | MA_DR_WAV_WITH_METADATA); } -DRWAV_PRIVATE drwav_bool32 drwav_init_memory_write__internal(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, drwav_bool32 isSequential, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_PRIVATE ma_bool32 ma_dr_wav_init_memory_write__internal(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, ma_bool32 isSequential, const ma_allocation_callbacks* pAllocationCallbacks) { if (ppData == NULL || pDataSize == NULL) { - return DRWAV_FALSE; + return MA_FALSE; } *ppData = NULL; *pDataSize = 0; - if (!drwav_preinit_write(pWav, pFormat, isSequential, drwav__on_write_memory, drwav__on_seek_memory_write, pWav, pAllocationCallbacks)) { - return DRWAV_FALSE; + if (!ma_dr_wav_preinit_write(pWav, pFormat, isSequential, ma_dr_wav__on_write_memory, ma_dr_wav__on_seek_memory_write, pWav, pAllocationCallbacks)) { + return MA_FALSE; } pWav->memoryStreamWrite.ppData = ppData; pWav->memoryStreamWrite.pDataSize = pDataSize; pWav->memoryStreamWrite.dataSize = 0; pWav->memoryStreamWrite.dataCapacity = 0; pWav->memoryStreamWrite.currentWritePos = 0; - return drwav_init_write__internal(pWav, pFormat, totalSampleCount); + return ma_dr_wav_init_write__internal(pWav, pFormat, totalSampleCount); } -DRWAV_API drwav_bool32 drwav_init_memory_write(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_memory_write(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, const ma_allocation_callbacks* pAllocationCallbacks) { - return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, DRWAV_FALSE, pAllocationCallbacks); + return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, 0, MA_FALSE, pAllocationCallbacks); } -DRWAV_API drwav_bool32 drwav_init_memory_write_sequential(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalSampleCount, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalSampleCount, const ma_allocation_callbacks* pAllocationCallbacks) { - return drwav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, DRWAV_TRUE, pAllocationCallbacks); + return ma_dr_wav_init_memory_write__internal(pWav, ppData, pDataSize, pFormat, totalSampleCount, MA_TRUE, pAllocationCallbacks); } -DRWAV_API drwav_bool32 drwav_init_memory_write_sequential_pcm_frames(drwav* pWav, void** ppData, size_t* pDataSize, const drwav_data_format* pFormat, drwav_uint64 totalPCMFrameCount, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_wav_init_memory_write_sequential_pcm_frames(ma_dr_wav* pWav, void** ppData, size_t* pDataSize, const ma_dr_wav_data_format* pFormat, ma_uint64 totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { if (pFormat == NULL) { - return DRWAV_FALSE; + return MA_FALSE; } - return drwav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); + return ma_dr_wav_init_memory_write_sequential(pWav, ppData, pDataSize, pFormat, totalPCMFrameCount*pFormat->channels, pAllocationCallbacks); } -DRWAV_API drwav_result drwav_uninit(drwav* pWav) +MA_API ma_result ma_dr_wav_uninit(ma_dr_wav* pWav) { - drwav_result result = DRWAV_SUCCESS; + ma_result result = MA_SUCCESS; if (pWav == NULL) { - return DRWAV_INVALID_ARGS; + return MA_INVALID_ARGS; } if (pWav->onWrite != NULL) { - drwav_uint32 paddingSize = 0; - if (pWav->container == drwav_container_riff || pWav->container == drwav_container_rf64) { - paddingSize = drwav__chunk_padding_size_riff(pWav->dataChunkDataSize); + ma_uint32 paddingSize = 0; + if (pWav->container == ma_dr_wav_container_riff || pWav->container == ma_dr_wav_container_rf64) { + paddingSize = ma_dr_wav__chunk_padding_size_riff(pWav->dataChunkDataSize); } else { - paddingSize = drwav__chunk_padding_size_w64(pWav->dataChunkDataSize); + paddingSize = ma_dr_wav__chunk_padding_size_w64(pWav->dataChunkDataSize); } if (paddingSize > 0) { - drwav_uint64 paddingData = 0; - drwav__write(pWav, &paddingData, paddingSize); + ma_uint64 paddingData = 0; + ma_dr_wav__write(pWav, &paddingData, paddingSize); } if (pWav->onSeek && !pWav->isSequentialWrite) { - if (pWav->container == drwav_container_riff) { - if (pWav->onSeek(pWav->pUserData, 4, drwav_seek_origin_start)) { - drwav_uint32 riffChunkSize = drwav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); - drwav__write_u32ne_to_le(pWav, riffChunkSize); + if (pWav->container == ma_dr_wav_container_riff) { + if (pWav->onSeek(pWav->pUserData, 4, ma_dr_wav_seek_origin_start)) { + ma_uint32 riffChunkSize = ma_dr_wav__riff_chunk_size_riff(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); + ma_dr_wav__write_u32ne_to_le(pWav, riffChunkSize); } - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, drwav_seek_origin_start)) { - drwav_uint32 dataChunkSize = drwav__data_chunk_size_riff(pWav->dataChunkDataSize); - drwav__write_u32ne_to_le(pWav, dataChunkSize); + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 4, ma_dr_wav_seek_origin_start)) { + ma_uint32 dataChunkSize = ma_dr_wav__data_chunk_size_riff(pWav->dataChunkDataSize); + ma_dr_wav__write_u32ne_to_le(pWav, dataChunkSize); } - } else if (pWav->container == drwav_container_w64) { - if (pWav->onSeek(pWav->pUserData, 16, drwav_seek_origin_start)) { - drwav_uint64 riffChunkSize = drwav__riff_chunk_size_w64(pWav->dataChunkDataSize); - drwav__write_u64ne_to_le(pWav, riffChunkSize); + } else if (pWav->container == ma_dr_wav_container_w64) { + if (pWav->onSeek(pWav->pUserData, 16, ma_dr_wav_seek_origin_start)) { + ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_w64(pWav->dataChunkDataSize); + ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); } - if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, drwav_seek_origin_start)) { - drwav_uint64 dataChunkSize = drwav__data_chunk_size_w64(pWav->dataChunkDataSize); - drwav__write_u64ne_to_le(pWav, dataChunkSize); + if (pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos - 8, ma_dr_wav_seek_origin_start)) { + ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_w64(pWav->dataChunkDataSize); + ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); } - } else if (pWav->container == drwav_container_rf64) { + } else if (pWav->container == ma_dr_wav_container_rf64) { int ds64BodyPos = 12 + 8; - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, drwav_seek_origin_start)) { - drwav_uint64 riffChunkSize = drwav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); - drwav__write_u64ne_to_le(pWav, riffChunkSize); + if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 0, ma_dr_wav_seek_origin_start)) { + ma_uint64 riffChunkSize = ma_dr_wav__riff_chunk_size_rf64(pWav->dataChunkDataSize, pWav->pMetadata, pWav->metadataCount); + ma_dr_wav__write_u64ne_to_le(pWav, riffChunkSize); } - if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, drwav_seek_origin_start)) { - drwav_uint64 dataChunkSize = drwav__data_chunk_size_rf64(pWav->dataChunkDataSize); - drwav__write_u64ne_to_le(pWav, dataChunkSize); + if (pWav->onSeek(pWav->pUserData, ds64BodyPos + 8, ma_dr_wav_seek_origin_start)) { + ma_uint64 dataChunkSize = ma_dr_wav__data_chunk_size_rf64(pWav->dataChunkDataSize); + ma_dr_wav__write_u64ne_to_le(pWav, dataChunkSize); } } } if (pWav->isSequentialWrite) { if (pWav->dataChunkDataSize != pWav->dataChunkDataSizeTargetWrite) { - result = DRWAV_INVALID_FILE; + result = MA_INVALID_FILE; } } } else { - if (pWav->pMetadata != NULL) { - pWav->allocationCallbacks.onFree(pWav->pMetadata, pWav->allocationCallbacks.pUserData); - } + ma_dr_wav_free(pWav->pMetadata, &pWav->allocationCallbacks); } -#ifndef DR_WAV_NO_STDIO - if (pWav->onRead == drwav__on_read_stdio || pWav->onWrite == drwav__on_write_stdio) { +#ifndef MA_DR_WAV_NO_STDIO + if (pWav->onRead == ma_dr_wav__on_read_stdio || pWav->onWrite == ma_dr_wav__on_write_stdio) { fclose((FILE*)pWav->pUserData); } #endif return result; } -DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut) +MA_API size_t ma_dr_wav_read_raw(ma_dr_wav* pWav, size_t bytesToRead, void* pBufferOut) { size_t bytesRead; - drwav_uint32 bytesPerFrame; + ma_uint32 bytesPerFrame; if (pWav == NULL || bytesToRead == 0) { return 0; } @@ -79762,7 +79928,7 @@ DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOu if (bytesToRead == 0) { return 0; } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -79775,13 +79941,13 @@ DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOu if (bytesToSeek > 0x7FFFFFFF) { bytesToSeek = 0x7FFFFFFF; } - if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, drwav_seek_origin_current) == DRWAV_FALSE) { + if (pWav->onSeek(pWav->pUserData, (int)bytesToSeek, ma_dr_wav_seek_origin_current) == MA_FALSE) { break; } bytesRead += bytesToSeek; } while (bytesRead < bytesToRead) { - drwav_uint8 buffer[4096]; + ma_uint8 buffer[4096]; size_t bytesSeeked; size_t bytesToSeek = (bytesToRead - bytesRead); if (bytesToSeek > sizeof(buffer)) { @@ -79798,171 +79964,198 @@ DRWAV_API size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOu pWav->bytesRemaining -= bytesRead; return bytesRead; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_le(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) { - drwav_uint32 bytesPerFrame; - drwav_uint64 bytesToRead; + ma_uint32 bytesPerFrame; + ma_uint64 bytesToRead; + ma_uint64 framesRemainingInFile; if (pWav == NULL || framesToRead == 0) { return 0; } - if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { + if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { return 0; } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + framesRemainingInFile = pWav->totalPCMFrameCount - pWav->readCursorInPCMFrames; + if (framesToRead > framesRemainingInFile) { + framesToRead = framesRemainingInFile; + } + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } bytesToRead = framesToRead * bytesPerFrame; - if (bytesToRead > DRWAV_SIZE_MAX) { - bytesToRead = (DRWAV_SIZE_MAX / bytesPerFrame) * bytesPerFrame; + if (bytesToRead > MA_SIZE_MAX) { + bytesToRead = (MA_SIZE_MAX / bytesPerFrame) * bytesPerFrame; } if (bytesToRead == 0) { return 0; } - return drwav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame; + return ma_dr_wav_read_raw(pWav, (size_t)bytesToRead, pBufferOut) / bytesPerFrame; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_be(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) { - drwav_uint64 framesRead = drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); if (pBufferOut != NULL) { - drwav_uint32 bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + ma_uint32 bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } - drwav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels, pWav->translatedFormatTag); + ma_dr_wav__bswap_samples(pBufferOut, framesRead*pWav->channels, bytesPerFrame/pWav->channels); } return framesRead; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames(drwav* pWav, drwav_uint64 framesToRead, void* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToRead, void* pBufferOut) { - if (drwav__is_little_endian()) { - return drwav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); - } else { - return drwav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); + ma_uint64 framesRead = 0; + if (ma_dr_wav_is_container_be(pWav->container)) { + if (pWav->container != ma_dr_wav_container_aiff || pWav->aiff.isLE == MA_FALSE) { + if (ma_dr_wav__is_little_endian()) { + framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); + } else { + framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); + } + goto post_process; + } } + if (ma_dr_wav__is_little_endian()) { + framesRead = ma_dr_wav_read_pcm_frames_le(pWav, framesToRead, pBufferOut); + } else { + framesRead = ma_dr_wav_read_pcm_frames_be(pWav, framesToRead, pBufferOut); + } + post_process: + { + if (pWav->container == ma_dr_wav_container_aiff && pWav->bitsPerSample == 8 && pWav->aiff.isUnsigned == MA_FALSE) { + if (pBufferOut != NULL) { + ma_uint64 iSample; + for (iSample = 0; iSample < framesRead * pWav->channels; iSample += 1) { + ((ma_uint8*)pBufferOut)[iSample] += 128; + } + } + } + } + return framesRead; } -DRWAV_PRIVATE drwav_bool32 drwav_seek_to_first_pcm_frame(drwav* pWav) +MA_PRIVATE ma_bool32 ma_dr_wav_seek_to_first_pcm_frame(ma_dr_wav* pWav) { if (pWav->onWrite != NULL) { - return DRWAV_FALSE; + return MA_FALSE; } - if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, drwav_seek_origin_start)) { - return DRWAV_FALSE; + if (!pWav->onSeek(pWav->pUserData, (int)pWav->dataChunkDataPos, ma_dr_wav_seek_origin_start)) { + return MA_FALSE; } - if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - DRWAV_ZERO_OBJECT(&pWav->msadpcm); - } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - DRWAV_ZERO_OBJECT(&pWav->ima); + if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + MA_DR_WAV_ZERO_OBJECT(&pWav->msadpcm); + } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + MA_DR_WAV_ZERO_OBJECT(&pWav->ima); } else { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); } } pWav->readCursorInPCMFrames = 0; pWav->bytesRemaining = pWav->dataChunkDataSize; - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_API drwav_bool32 drwav_seek_to_pcm_frame(drwav* pWav, drwav_uint64 targetFrameIndex) +MA_API ma_bool32 ma_dr_wav_seek_to_pcm_frame(ma_dr_wav* pWav, ma_uint64 targetFrameIndex) { if (pWav == NULL || pWav->onSeek == NULL) { - return DRWAV_FALSE; + return MA_FALSE; } if (pWav->onWrite != NULL) { - return DRWAV_FALSE; + return MA_FALSE; } if (pWav->totalPCMFrameCount == 0) { - return DRWAV_TRUE; + return MA_TRUE; } if (targetFrameIndex > pWav->totalPCMFrameCount) { targetFrameIndex = pWav->totalPCMFrameCount; } - if (drwav__is_compressed_format_tag(pWav->translatedFormatTag)) { + if (ma_dr_wav__is_compressed_format_tag(pWav->translatedFormatTag)) { if (targetFrameIndex < pWav->readCursorInPCMFrames) { - if (!drwav_seek_to_first_pcm_frame(pWav)) { - return DRWAV_FALSE; + if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) { + return MA_FALSE; } } if (targetFrameIndex > pWav->readCursorInPCMFrames) { - drwav_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames; - drwav_int16 devnull[2048]; + ma_uint64 offsetInFrames = targetFrameIndex - pWav->readCursorInPCMFrames; + ma_int16 devnull[2048]; while (offsetInFrames > 0) { - drwav_uint64 framesRead = 0; - drwav_uint64 framesToRead = offsetInFrames; - if (framesToRead > drwav_countof(devnull)/pWav->channels) { - framesToRead = drwav_countof(devnull)/pWav->channels; + ma_uint64 framesRead = 0; + ma_uint64 framesToRead = offsetInFrames; + if (framesToRead > ma_dr_wav_countof(devnull)/pWav->channels) { + framesToRead = ma_dr_wav_countof(devnull)/pWav->channels; } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - framesRead = drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull); - } else if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - framesRead = drwav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + framesRead = ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, devnull); + } else if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + framesRead = ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, devnull); } else { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); } if (framesRead != framesToRead) { - return DRWAV_FALSE; + return MA_FALSE; } offsetInFrames -= framesRead; } } } else { - drwav_uint64 totalSizeInBytes; - drwav_uint64 currentBytePos; - drwav_uint64 targetBytePos; - drwav_uint64 offset; - drwav_uint32 bytesPerFrame; - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + ma_uint64 totalSizeInBytes; + ma_uint64 currentBytePos; + ma_uint64 targetBytePos; + ma_uint64 offset; + ma_uint32 bytesPerFrame; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { - return DRWAV_FALSE; + return MA_FALSE; } totalSizeInBytes = pWav->totalPCMFrameCount * bytesPerFrame; - DRWAV_ASSERT(totalSizeInBytes >= pWav->bytesRemaining); currentBytePos = totalSizeInBytes - pWav->bytesRemaining; targetBytePos = targetFrameIndex * bytesPerFrame; if (currentBytePos < targetBytePos) { offset = (targetBytePos - currentBytePos); } else { - if (!drwav_seek_to_first_pcm_frame(pWav)) { - return DRWAV_FALSE; + if (!ma_dr_wav_seek_to_first_pcm_frame(pWav)) { + return MA_FALSE; } offset = targetBytePos; } while (offset > 0) { int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset); - if (!pWav->onSeek(pWav->pUserData, offset32, drwav_seek_origin_current)) { - return DRWAV_FALSE; + if (!pWav->onSeek(pWav->pUserData, offset32, ma_dr_wav_seek_origin_current)) { + return MA_FALSE; } pWav->readCursorInPCMFrames += offset32 / bytesPerFrame; pWav->bytesRemaining -= offset32; offset -= offset32; } } - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_API drwav_result drwav_get_cursor_in_pcm_frames(drwav* pWav, drwav_uint64* pCursor) +MA_API ma_result ma_dr_wav_get_cursor_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pCursor) { if (pCursor == NULL) { - return DRWAV_INVALID_ARGS; + return MA_INVALID_ARGS; } *pCursor = 0; if (pWav == NULL) { - return DRWAV_INVALID_ARGS; + return MA_INVALID_ARGS; } *pCursor = pWav->readCursorInPCMFrames; - return DRWAV_SUCCESS; + return MA_SUCCESS; } -DRWAV_API drwav_result drwav_get_length_in_pcm_frames(drwav* pWav, drwav_uint64* pLength) +MA_API ma_result ma_dr_wav_get_length_in_pcm_frames(ma_dr_wav* pWav, ma_uint64* pLength) { if (pLength == NULL) { - return DRWAV_INVALID_ARGS; + return MA_INVALID_ARGS; } *pLength = 0; if (pWav == NULL) { - return DRWAV_INVALID_ARGS; + return MA_INVALID_ARGS; } *pLength = pWav->totalPCMFrameCount; - return DRWAV_SUCCESS; + return MA_SUCCESS; } -DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* pData) +MA_API size_t ma_dr_wav_write_raw(ma_dr_wav* pWav, size_t bytesToWrite, const void* pData) { size_t bytesWritten; if (pWav == NULL || bytesToWrite == 0 || pData == NULL) { @@ -79972,26 +80165,26 @@ DRWAV_API size_t drwav_write_raw(drwav* pWav, size_t bytesToWrite, const void* p pWav->dataChunkDataSize += bytesWritten; return bytesWritten; } -DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) +MA_API ma_uint64 ma_dr_wav_write_pcm_frames_le(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) { - drwav_uint64 bytesToWrite; - drwav_uint64 bytesWritten; - const drwav_uint8* pRunningData; + ma_uint64 bytesToWrite; + ma_uint64 bytesWritten; + const ma_uint8* pRunningData; if (pWav == NULL || framesToWrite == 0 || pData == NULL) { return 0; } bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); - if (bytesToWrite > DRWAV_SIZE_MAX) { + if (bytesToWrite > MA_SIZE_MAX) { return 0; } bytesWritten = 0; - pRunningData = (const drwav_uint8*)pData; + pRunningData = (const ma_uint8*)pData; while (bytesToWrite > 0) { size_t bytesJustWritten; - drwav_uint64 bytesToWriteThisIteration; + ma_uint64 bytesToWriteThisIteration; bytesToWriteThisIteration = bytesToWrite; - DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); - bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData); + MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX); + bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, pRunningData); if (bytesJustWritten == 0) { break; } @@ -80001,39 +80194,39 @@ DRWAV_API drwav_uint64 drwav_write_pcm_frames_le(drwav* pWav, drwav_uint64 frame } return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; } -DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) +MA_API ma_uint64 ma_dr_wav_write_pcm_frames_be(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) { - drwav_uint64 bytesToWrite; - drwav_uint64 bytesWritten; - drwav_uint32 bytesPerSample; - const drwav_uint8* pRunningData; + ma_uint64 bytesToWrite; + ma_uint64 bytesWritten; + ma_uint32 bytesPerSample; + const ma_uint8* pRunningData; if (pWav == NULL || framesToWrite == 0 || pData == NULL) { return 0; } bytesToWrite = ((framesToWrite * pWav->channels * pWav->bitsPerSample) / 8); - if (bytesToWrite > DRWAV_SIZE_MAX) { + if (bytesToWrite > MA_SIZE_MAX) { return 0; } bytesWritten = 0; - pRunningData = (const drwav_uint8*)pData; - bytesPerSample = drwav_get_bytes_per_pcm_frame(pWav) / pWav->channels; + pRunningData = (const ma_uint8*)pData; + bytesPerSample = ma_dr_wav_get_bytes_per_pcm_frame(pWav) / pWav->channels; if (bytesPerSample == 0) { return 0; } while (bytesToWrite > 0) { - drwav_uint8 temp[4096]; - drwav_uint32 sampleCount; + ma_uint8 temp[4096]; + ma_uint32 sampleCount; size_t bytesJustWritten; - drwav_uint64 bytesToWriteThisIteration; + ma_uint64 bytesToWriteThisIteration; bytesToWriteThisIteration = bytesToWrite; - DRWAV_ASSERT(bytesToWriteThisIteration <= DRWAV_SIZE_MAX); + MA_DR_WAV_ASSERT(bytesToWriteThisIteration <= MA_SIZE_MAX); sampleCount = sizeof(temp)/bytesPerSample; - if (bytesToWriteThisIteration > ((drwav_uint64)sampleCount)*bytesPerSample) { - bytesToWriteThisIteration = ((drwav_uint64)sampleCount)*bytesPerSample; + if (bytesToWriteThisIteration > ((ma_uint64)sampleCount)*bytesPerSample) { + bytesToWriteThisIteration = ((ma_uint64)sampleCount)*bytesPerSample; } - DRWAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration); - drwav__bswap_samples(temp, sampleCount, bytesPerSample, pWav->translatedFormatTag); - bytesJustWritten = drwav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp); + MA_DR_WAV_COPY_MEMORY(temp, pRunningData, (size_t)bytesToWriteThisIteration); + ma_dr_wav__bswap_samples(temp, sampleCount, bytesPerSample); + bytesJustWritten = ma_dr_wav_write_raw(pWav, (size_t)bytesToWriteThisIteration, temp); if (bytesJustWritten == 0) { break; } @@ -80043,49 +80236,49 @@ DRWAV_API drwav_uint64 drwav_write_pcm_frames_be(drwav* pWav, drwav_uint64 frame } return (bytesWritten * 8) / pWav->bitsPerSample / pWav->channels; } -DRWAV_API drwav_uint64 drwav_write_pcm_frames(drwav* pWav, drwav_uint64 framesToWrite, const void* pData) +MA_API ma_uint64 ma_dr_wav_write_pcm_frames(ma_dr_wav* pWav, ma_uint64 framesToWrite, const void* pData) { - if (drwav__is_little_endian()) { - return drwav_write_pcm_frames_le(pWav, framesToWrite, pData); + if (ma_dr_wav__is_little_endian()) { + return ma_dr_wav_write_pcm_frames_le(pWav, framesToWrite, pData); } else { - return drwav_write_pcm_frames_be(pWav, framesToWrite, pData); + return ma_dr_wav_write_pcm_frames_be(pWav, framesToWrite, pData); } } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__msadpcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) { - drwav_uint64 totalFramesRead = 0; - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(framesToRead > 0); + ma_uint64 totalFramesRead = 0; + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(framesToRead > 0); while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - DRWAV_ASSERT(framesToRead > 0); + MA_DR_WAV_ASSERT(framesToRead > 0); if (pWav->msadpcm.cachedFrameCount == 0 && pWav->msadpcm.bytesRemainingInBlock == 0) { if (pWav->channels == 1) { - drwav_uint8 header[7]; + ma_uint8 header[7]; if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { return totalFramesRead; } pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); pWav->msadpcm.predictor[0] = header[0]; - pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 1); - pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 3); - pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 5); + pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 1); + pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 3); + pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 5); pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][0]; pWav->msadpcm.cachedFrames[3] = pWav->msadpcm.prevFrames[0][1]; pWav->msadpcm.cachedFrameCount = 2; } else { - drwav_uint8 header[14]; + ma_uint8 header[14]; if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { return totalFramesRead; } pWav->msadpcm.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); pWav->msadpcm.predictor[0] = header[0]; pWav->msadpcm.predictor[1] = header[1]; - pWav->msadpcm.delta[0] = drwav_bytes_to_s16(header + 2); - pWav->msadpcm.delta[1] = drwav_bytes_to_s16(header + 4); - pWav->msadpcm.prevFrames[0][1] = (drwav_int32)drwav_bytes_to_s16(header + 6); - pWav->msadpcm.prevFrames[1][1] = (drwav_int32)drwav_bytes_to_s16(header + 8); - pWav->msadpcm.prevFrames[0][0] = (drwav_int32)drwav_bytes_to_s16(header + 10); - pWav->msadpcm.prevFrames[1][0] = (drwav_int32)drwav_bytes_to_s16(header + 12); + pWav->msadpcm.delta[0] = ma_dr_wav_bytes_to_s16(header + 2); + pWav->msadpcm.delta[1] = ma_dr_wav_bytes_to_s16(header + 4); + pWav->msadpcm.prevFrames[0][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 6); + pWav->msadpcm.prevFrames[1][1] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 8); + pWav->msadpcm.prevFrames[0][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 10); + pWav->msadpcm.prevFrames[1][0] = (ma_int32)ma_dr_wav_bytes_to_s16(header + 12); pWav->msadpcm.cachedFrames[0] = pWav->msadpcm.prevFrames[0][0]; pWav->msadpcm.cachedFrames[1] = pWav->msadpcm.prevFrames[1][0]; pWav->msadpcm.cachedFrames[2] = pWav->msadpcm.prevFrames[0][1]; @@ -80095,9 +80288,9 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav } while (framesToRead > 0 && pWav->msadpcm.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { if (pBufferOut != NULL) { - drwav_uint32 iSample = 0; + ma_uint32 iSample = 0; for (iSample = 0; iSample < pWav->channels; iSample += 1) { - pBufferOut[iSample] = (drwav_int16)pWav->msadpcm.cachedFrames[(drwav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample]; + pBufferOut[iSample] = (ma_int16)pWav->msadpcm.cachedFrames[(ma_dr_wav_countof(pWav->msadpcm.cachedFrames) - (pWav->msadpcm.cachedFrameCount*pWav->channels)) + iSample]; } pBufferOut += pWav->channels; } @@ -80113,15 +80306,15 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav if (pWav->msadpcm.bytesRemainingInBlock == 0) { continue; } else { - static drwav_int32 adaptationTable[] = { + static ma_int32 adaptationTable[] = { 230, 230, 230, 230, 307, 409, 512, 614, 768, 614, 512, 409, 307, 230, 230, 230 }; - static drwav_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; - static drwav_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; - drwav_uint8 nibbles; - drwav_int32 nibble0; - drwav_int32 nibble1; + static ma_int32 coeff1Table[] = { 256, 512, 0, 192, 240, 460, 392 }; + static ma_int32 coeff2Table[] = { 0, -256, 0, 64, 0, -208, -232 }; + ma_uint8 nibbles; + ma_int32 nibble0; + ma_int32 nibble1; if (pWav->onRead(pWav->pUserData, &nibbles, 1) != 1) { return totalFramesRead; } @@ -80129,11 +80322,11 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav nibble0 = ((nibbles & 0xF0) >> 4); if ((nibbles & 0x80)) { nibble0 |= 0xFFFFFFF0UL; } nibble1 = ((nibbles & 0x0F) >> 0); if ((nibbles & 0x08)) { nibble1 |= 0xFFFFFFF0UL; } if (pWav->channels == 1) { - drwav_int32 newSample0; - drwav_int32 newSample1; + ma_int32 newSample0; + ma_int32 newSample1; newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; newSample0 += nibble0 * pWav->msadpcm.delta[0]; - newSample0 = drwav_clamp(newSample0, -32768, 32767); + newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767); pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; if (pWav->msadpcm.delta[0] < 16) { pWav->msadpcm.delta[0] = 16; @@ -80142,7 +80335,7 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav pWav->msadpcm.prevFrames[0][1] = newSample0; newSample1 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; newSample1 += nibble1 * pWav->msadpcm.delta[0]; - newSample1 = drwav_clamp(newSample1, -32768, 32767); + newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767); pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[0]) >> 8; if (pWav->msadpcm.delta[0] < 16) { pWav->msadpcm.delta[0] = 16; @@ -80153,11 +80346,11 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav pWav->msadpcm.cachedFrames[3] = newSample1; pWav->msadpcm.cachedFrameCount = 2; } else { - drwav_int32 newSample0; - drwav_int32 newSample1; + ma_int32 newSample0; + ma_int32 newSample1; newSample0 = ((pWav->msadpcm.prevFrames[0][1] * coeff1Table[pWav->msadpcm.predictor[0]]) + (pWav->msadpcm.prevFrames[0][0] * coeff2Table[pWav->msadpcm.predictor[0]])) >> 8; newSample0 += nibble0 * pWav->msadpcm.delta[0]; - newSample0 = drwav_clamp(newSample0, -32768, 32767); + newSample0 = ma_dr_wav_clamp(newSample0, -32768, 32767); pWav->msadpcm.delta[0] = (adaptationTable[((nibbles & 0xF0) >> 4)] * pWav->msadpcm.delta[0]) >> 8; if (pWav->msadpcm.delta[0] < 16) { pWav->msadpcm.delta[0] = 16; @@ -80166,7 +80359,7 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav pWav->msadpcm.prevFrames[0][1] = newSample0; newSample1 = ((pWav->msadpcm.prevFrames[1][1] * coeff1Table[pWav->msadpcm.predictor[1]]) + (pWav->msadpcm.prevFrames[1][0] * coeff2Table[pWav->msadpcm.predictor[1]])) >> 8; newSample1 += nibble1 * pWav->msadpcm.delta[1]; - newSample1 = drwav_clamp(newSample1, -32768, 32767); + newSample1 = ma_dr_wav_clamp(newSample1, -32768, 32767); pWav->msadpcm.delta[1] = (adaptationTable[((nibbles & 0x0F) >> 0)] * pWav->msadpcm.delta[1]) >> 8; if (pWav->msadpcm.delta[1] < 16) { pWav->msadpcm.delta[1] = 16; @@ -80182,15 +80375,15 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__msadpcm(drwav* pWav, drwav } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) { - drwav_uint64 totalFramesRead = 0; - drwav_uint32 iChannel; - static drwav_int32 indexTable[16] = { + ma_uint64 totalFramesRead = 0; + ma_uint32 iChannel; + static ma_int32 indexTable[16] = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8 }; - static drwav_int32 stepTable[89] = { + static ma_int32 stepTable[89] = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, @@ -80201,51 +80394,51 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 }; - DRWAV_ASSERT(pWav != NULL); - DRWAV_ASSERT(framesToRead > 0); + MA_DR_WAV_ASSERT(pWav != NULL); + MA_DR_WAV_ASSERT(framesToRead > 0); while (pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { - DRWAV_ASSERT(framesToRead > 0); + MA_DR_WAV_ASSERT(framesToRead > 0); if (pWav->ima.cachedFrameCount == 0 && pWav->ima.bytesRemainingInBlock == 0) { if (pWav->channels == 1) { - drwav_uint8 header[4]; + ma_uint8 header[4]; if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { return totalFramesRead; } pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - if (header[2] >= drwav_countof(stepTable)) { - pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current); + if (header[2] >= ma_dr_wav_countof(stepTable)) { + pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); pWav->ima.bytesRemainingInBlock = 0; return totalFramesRead; } - pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0); - pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1); - pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0]; + pWav->ima.predictor[0] = (ma_int16)ma_dr_wav_bytes_to_u16(header + 0); + pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); + pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[0]; pWav->ima.cachedFrameCount = 1; } else { - drwav_uint8 header[8]; + ma_uint8 header[8]; if (pWav->onRead(pWav->pUserData, header, sizeof(header)) != sizeof(header)) { return totalFramesRead; } pWav->ima.bytesRemainingInBlock = pWav->fmt.blockAlign - sizeof(header); - if (header[2] >= drwav_countof(stepTable) || header[6] >= drwav_countof(stepTable)) { - pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, drwav_seek_origin_current); + if (header[2] >= ma_dr_wav_countof(stepTable) || header[6] >= ma_dr_wav_countof(stepTable)) { + pWav->onSeek(pWav->pUserData, pWav->ima.bytesRemainingInBlock, ma_dr_wav_seek_origin_current); pWav->ima.bytesRemainingInBlock = 0; return totalFramesRead; } - pWav->ima.predictor[0] = drwav_bytes_to_s16(header + 0); - pWav->ima.stepIndex[0] = drwav_clamp(header[2], 0, (drwav_int32)drwav_countof(stepTable)-1); - pWav->ima.predictor[1] = drwav_bytes_to_s16(header + 4); - pWav->ima.stepIndex[1] = drwav_clamp(header[6], 0, (drwav_int32)drwav_countof(stepTable)-1); - pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0]; - pWav->ima.cachedFrames[drwav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1]; + pWav->ima.predictor[0] = ma_dr_wav_bytes_to_s16(header + 0); + pWav->ima.stepIndex[0] = ma_dr_wav_clamp(header[2], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); + pWav->ima.predictor[1] = ma_dr_wav_bytes_to_s16(header + 4); + pWav->ima.stepIndex[1] = ma_dr_wav_clamp(header[6], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); + pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 2] = pWav->ima.predictor[0]; + pWav->ima.cachedFrames[ma_dr_wav_countof(pWav->ima.cachedFrames) - 1] = pWav->ima.predictor[1]; pWav->ima.cachedFrameCount = 1; } } while (framesToRead > 0 && pWav->ima.cachedFrameCount > 0 && pWav->readCursorInPCMFrames < pWav->totalPCMFrameCount) { if (pBufferOut != NULL) { - drwav_uint32 iSample; + ma_uint32 iSample; for (iSample = 0; iSample < pWav->channels; iSample += 1) { - pBufferOut[iSample] = (drwav_int16)pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample]; + pBufferOut[iSample] = (ma_int16)pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + iSample]; } pBufferOut += pWav->channels; } @@ -80263,27 +80456,27 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin } else { pWav->ima.cachedFrameCount = 8; for (iChannel = 0; iChannel < pWav->channels; ++iChannel) { - drwav_uint32 iByte; - drwav_uint8 nibbles[4]; + ma_uint32 iByte; + ma_uint8 nibbles[4]; if (pWav->onRead(pWav->pUserData, &nibbles, 4) != 4) { pWav->ima.cachedFrameCount = 0; return totalFramesRead; } pWav->ima.bytesRemainingInBlock -= 4; for (iByte = 0; iByte < 4; ++iByte) { - drwav_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0); - drwav_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4); - drwav_int32 step = stepTable[pWav->ima.stepIndex[iChannel]]; - drwav_int32 predictor = pWav->ima.predictor[iChannel]; - drwav_int32 diff = step >> 3; + ma_uint8 nibble0 = ((nibbles[iByte] & 0x0F) >> 0); + ma_uint8 nibble1 = ((nibbles[iByte] & 0xF0) >> 4); + ma_int32 step = stepTable[pWav->ima.stepIndex[iChannel]]; + ma_int32 predictor = pWav->ima.predictor[iChannel]; + ma_int32 diff = step >> 3; if (nibble0 & 1) diff += step >> 2; if (nibble0 & 2) diff += step >> 1; if (nibble0 & 4) diff += step; if (nibble0 & 8) diff = -diff; - predictor = drwav_clamp(predictor + diff, -32768, 32767); + predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767); pWav->ima.predictor[iChannel] = predictor; - pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (drwav_int32)drwav_countof(stepTable)-1); - pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor; + pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble0], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); + pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+0)*pWav->channels + iChannel] = predictor; step = stepTable[pWav->ima.stepIndex[iChannel]]; predictor = pWav->ima.predictor[iChannel]; diff = step >> 3; @@ -80291,10 +80484,10 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin if (nibble1 & 2) diff += step >> 1; if (nibble1 & 4) diff += step; if (nibble1 & 8) diff = -diff; - predictor = drwav_clamp(predictor + diff, -32768, 32767); + predictor = ma_dr_wav_clamp(predictor + diff, -32768, 32767); pWav->ima.predictor[iChannel] = predictor; - pWav->ima.stepIndex[iChannel] = drwav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (drwav_int32)drwav_countof(stepTable)-1); - pWav->ima.cachedFrames[(drwav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor; + pWav->ima.stepIndex[iChannel] = ma_dr_wav_clamp(pWav->ima.stepIndex[iChannel] + indexTable[nibble1], 0, (ma_int32)ma_dr_wav_countof(stepTable)-1); + pWav->ima.cachedFrames[(ma_dr_wav_countof(pWav->ima.cachedFrames) - (pWav->ima.cachedFrameCount*pWav->channels)) + (iByte*2+1)*pWav->channels + iChannel] = predictor; } } } @@ -80302,8 +80495,8 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ima(drwav* pWav, drwav_uin } return totalFramesRead; } -#ifndef DR_WAV_NO_CONVERSION_API -static unsigned short g_drwavAlawTable[256] = { +#ifndef MA_DR_WAV_NO_CONVERSION_API +static unsigned short g_ma_dr_wavAlawTable[256] = { 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, @@ -80321,7 +80514,7 @@ static unsigned short g_drwavAlawTable[256] = { 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 }; -static unsigned short g_drwavMulawTable[256] = { +static unsigned short g_ma_dr_wavMulawTable[256] = { 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, @@ -80339,76 +80532,76 @@ static unsigned short g_drwavMulawTable[256] = { 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 }; -static DRWAV_INLINE drwav_int16 drwav__alaw_to_s16(drwav_uint8 sampleIn) +static MA_INLINE ma_int16 ma_dr_wav__alaw_to_s16(ma_uint8 sampleIn) { - return (short)g_drwavAlawTable[sampleIn]; + return (short)g_ma_dr_wavAlawTable[sampleIn]; } -static DRWAV_INLINE drwav_int16 drwav__mulaw_to_s16(drwav_uint8 sampleIn) +static MA_INLINE ma_int16 ma_dr_wav__mulaw_to_s16(ma_uint8 sampleIn) { - return (short)g_drwavMulawTable[sampleIn]; + return (short)g_ma_dr_wavMulawTable[sampleIn]; } -DRWAV_PRIVATE void drwav__pcm_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +MA_PRIVATE void ma_dr_wav__pcm_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) { size_t i; if (bytesPerSample == 1) { - drwav_u8_to_s16(pOut, pIn, totalSampleCount); + ma_dr_wav_u8_to_s16(pOut, pIn, totalSampleCount); return; } if (bytesPerSample == 2) { for (i = 0; i < totalSampleCount; ++i) { - *pOut++ = ((const drwav_int16*)pIn)[i]; + *pOut++ = ((const ma_int16*)pIn)[i]; } return; } if (bytesPerSample == 3) { - drwav_s24_to_s16(pOut, pIn, totalSampleCount); + ma_dr_wav_s24_to_s16(pOut, pIn, totalSampleCount); return; } if (bytesPerSample == 4) { - drwav_s32_to_s16(pOut, (const drwav_int32*)pIn, totalSampleCount); + ma_dr_wav_s32_to_s16(pOut, (const ma_int32*)pIn, totalSampleCount); return; } if (bytesPerSample > 8) { - DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); return; } for (i = 0; i < totalSampleCount; ++i) { - drwav_uint64 sample = 0; + ma_uint64 sample = 0; unsigned int shift = (8 - bytesPerSample) * 8; unsigned int j; for (j = 0; j < bytesPerSample; j += 1) { - DRWAV_ASSERT(j < 8); - sample |= (drwav_uint64)(pIn[j]) << shift; + MA_DR_WAV_ASSERT(j < 8); + sample |= (ma_uint64)(pIn[j]) << shift; shift += 8; } pIn += j; - *pOut++ = (drwav_int16)((drwav_int64)sample >> 48); + *pOut++ = (ma_int16)((ma_int64)sample >> 48); } } -DRWAV_PRIVATE void drwav__ieee_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +MA_PRIVATE void ma_dr_wav__ieee_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) { if (bytesPerSample == 4) { - drwav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount); + ma_dr_wav_f32_to_s16(pOut, (const float*)pIn, totalSampleCount); return; } else if (bytesPerSample == 8) { - drwav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount); + ma_dr_wav_f64_to_s16(pOut, (const double*)pIn, totalSampleCount); return; } else { - DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); return; } } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - if ((pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) { - return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + if ((pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 16) || pBufferOut == NULL) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -80418,35 +80611,35 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__pcm(drwav* pWav, drwav_uin } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + ma_dr_wav__pcm_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; if (pBufferOut == NULL) { - return drwav_read_pcm_frames(pWav, framesToRead, NULL); + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -80456,35 +80649,35 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__ieee(drwav* pWav, drwav_ui } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + ma_dr_wav__ieee_to_s16(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; if (pBufferOut == NULL) { - return drwav_read_pcm_frames(pWav, framesToRead, NULL); + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -80494,35 +80687,45 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__alaw(drwav* pWav, drwav_ui } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); + ma_dr_wav_alaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s16__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; if (pBufferOut == NULL) { - return drwav_read_pcm_frames(pWav, framesToRead, NULL); + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -80532,72 +80735,82 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s16__mulaw(drwav* pWav, drwav_u } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); + ma_dr_wav_mulaw_to_s16(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) { if (pWav == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { - return drwav_read_pcm_frames(pWav, framesToRead, NULL); + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); } - if (framesToRead * pWav->channels * sizeof(drwav_int16) > DRWAV_SIZE_MAX) { - framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int16) / pWav->channels; + if (framesToRead * pWav->channels * sizeof(ma_int16) > MA_SIZE_MAX) { + framesToRead = MA_SIZE_MAX / sizeof(ma_int16) / pWav->channels; } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { - return drwav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { + return ma_dr_wav_read_pcm_frames_s16__pcm(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { - return drwav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { + return ma_dr_wav_read_pcm_frames_s16__ieee(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { - return drwav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { + return ma_dr_wav_read_pcm_frames_s16__alaw(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { - return drwav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { + return ma_dr_wav_read_pcm_frames_s16__mulaw(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM) { - return drwav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM) { + return ma_dr_wav_read_pcm_frames_s16__msadpcm(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - return drwav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + return ma_dr_wav_read_pcm_frames_s16__ima(pWav, framesToRead, pBufferOut); } return 0; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16le(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) { - drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { - drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { + ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); } return framesRead; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s16be(drwav* pWav, drwav_uint64 framesToRead, drwav_int16* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s16be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int16* pBufferOut) { - drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { - drwav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { + ma_dr_wav__bswap_samples_s16(pBufferOut, framesRead*pWav->channels); } return framesRead; } -DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_u8_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) { int r; size_t i; @@ -80608,17 +80821,17 @@ DRWAV_API void drwav_u8_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t pOut[i] = (short)r; } } -DRWAV_API void drwav_s24_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_s24_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) { int r; size_t i; for (i = 0; i < sampleCount; ++i) { - int x = ((int)(((unsigned int)(((const drwav_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const drwav_uint8*)pIn)[i*3+2])) << 24)) >> 8; + int x = ((int)(((unsigned int)(((const ma_uint8*)pIn)[i*3+0]) << 8) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+1]) << 16) | ((unsigned int)(((const ma_uint8*)pIn)[i*3+2])) << 24)) >> 8; r = x >> 8; pOut[i] = (short)r; } } -DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_t sampleCount) +MA_API void ma_dr_wav_s32_to_s16(ma_int16* pOut, const ma_int32* pIn, size_t sampleCount) { int r; size_t i; @@ -80628,7 +80841,7 @@ DRWAV_API void drwav_s32_to_s16(drwav_int16* pOut, const drwav_int32* pIn, size_ pOut[i] = (short)r; } } -DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t sampleCount) +MA_API void ma_dr_wav_f32_to_s16(ma_int16* pOut, const float* pIn, size_t sampleCount) { int r; size_t i; @@ -80642,7 +80855,7 @@ DRWAV_API void drwav_f32_to_s16(drwav_int16* pOut, const float* pIn, size_t samp pOut[i] = (short)r; } } -DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sampleCount) +MA_API void ma_dr_wav_f64_to_s16(ma_int16* pOut, const double* pIn, size_t sampleCount) { int r; size_t i; @@ -80656,57 +80869,57 @@ DRWAV_API void drwav_f64_to_s16(drwav_int16* pOut, const double* pIn, size_t sam pOut[i] = (short)r; } } -DRWAV_API void drwav_alaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_alaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) { size_t i; for (i = 0; i < sampleCount; ++i) { - pOut[i] = drwav__alaw_to_s16(pIn[i]); + pOut[i] = ma_dr_wav__alaw_to_s16(pIn[i]); } } -DRWAV_API void drwav_mulaw_to_s16(drwav_int16* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_mulaw_to_s16(ma_int16* pOut, const ma_uint8* pIn, size_t sampleCount) { size_t i; for (i = 0; i < sampleCount; ++i) { - pOut[i] = drwav__mulaw_to_s16(pIn[i]); + pOut[i] = ma_dr_wav__mulaw_to_s16(pIn[i]); } } -DRWAV_PRIVATE void drwav__pcm_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) +MA_PRIVATE void ma_dr_wav__pcm_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) { unsigned int i; if (bytesPerSample == 1) { - drwav_u8_to_f32(pOut, pIn, sampleCount); + ma_dr_wav_u8_to_f32(pOut, pIn, sampleCount); return; } if (bytesPerSample == 2) { - drwav_s16_to_f32(pOut, (const drwav_int16*)pIn, sampleCount); + ma_dr_wav_s16_to_f32(pOut, (const ma_int16*)pIn, sampleCount); return; } if (bytesPerSample == 3) { - drwav_s24_to_f32(pOut, pIn, sampleCount); + ma_dr_wav_s24_to_f32(pOut, pIn, sampleCount); return; } if (bytesPerSample == 4) { - drwav_s32_to_f32(pOut, (const drwav_int32*)pIn, sampleCount); + ma_dr_wav_s32_to_f32(pOut, (const ma_int32*)pIn, sampleCount); return; } if (bytesPerSample > 8) { - DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); + MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); return; } for (i = 0; i < sampleCount; ++i) { - drwav_uint64 sample = 0; + ma_uint64 sample = 0; unsigned int shift = (8 - bytesPerSample) * 8; unsigned int j; for (j = 0; j < bytesPerSample; j += 1) { - DRWAV_ASSERT(j < 8); - sample |= (drwav_uint64)(pIn[j]) << shift; + MA_DR_WAV_ASSERT(j < 8); + sample |= (ma_uint64)(pIn[j]) << shift; shift += 8; } pIn += j; - *pOut++ = (float)((drwav_int64)sample / 9223372036854775807.0); + *pOut++ = (float)((ma_int64)sample / 9223372036854775807.0); } } -DRWAV_PRIVATE void drwav__ieee_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) +MA_PRIVATE void ma_dr_wav__ieee_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount, unsigned int bytesPerSample) { if (bytesPerSample == 4) { unsigned int i; @@ -80715,21 +80928,21 @@ DRWAV_PRIVATE void drwav__ieee_to_f32(float* pOut, const drwav_uint8* pIn, size_ } return; } else if (bytesPerSample == 8) { - drwav_f64_to_f32(pOut, (const double*)pIn, sampleCount); + ma_dr_wav_f64_to_f32(pOut, (const double*)pIn, sampleCount); return; } else { - DRWAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); + MA_DR_WAV_ZERO_MEMORY(pOut, sampleCount * sizeof(*pOut)); return; } } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__pcm(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -80739,54 +80952,54 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__pcm(drwav* pWav, drwav_uin } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + ma_dr_wav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_int16 samples16[2048]; + ma_uint64 totalFramesRead; + ma_int16 samples16[2048]; totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels); - drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); - drwav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + ma_dr_wav_s16_to_f32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); pBufferOut += framesRead*pWav->channels; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) { - return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bitsPerSample == 32) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -80796,32 +81009,32 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__ieee(drwav* pWav, drwav_ui } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + ma_dr_wav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__alaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -80831,32 +81044,42 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__alaw(drwav* pWav, drwav_ui } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); + ma_dr_wav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__mulaw(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_f32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -80866,75 +81089,85 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_f32__mulaw(drwav* pWav, drwav_u } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); + ma_dr_wav_mulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) { if (pWav == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { - return drwav_read_pcm_frames(pWav, framesToRead, NULL); + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); } - if (framesToRead * pWav->channels * sizeof(float) > DRWAV_SIZE_MAX) { - framesToRead = DRWAV_SIZE_MAX / sizeof(float) / pWav->channels; + if (framesToRead * pWav->channels * sizeof(float) > MA_SIZE_MAX) { + framesToRead = MA_SIZE_MAX / sizeof(float) / pWav->channels; } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { - return drwav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { + return ma_dr_wav_read_pcm_frames_f32__pcm(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - return drwav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + return ma_dr_wav_read_pcm_frames_f32__msadpcm_ima(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { - return drwav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { + return ma_dr_wav_read_pcm_frames_f32__ieee(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { - return drwav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { + return ma_dr_wav_read_pcm_frames_f32__alaw(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { - return drwav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { + return ma_dr_wav_read_pcm_frames_f32__mulaw(pWav, framesToRead, pBufferOut); } return 0; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32le(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32le(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) { - drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { - drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { + ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); } return framesRead; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_f32be(drwav* pWav, drwav_uint64 framesToRead, float* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_f32be(ma_dr_wav* pWav, ma_uint64 framesToRead, float* pBufferOut) { - drwav_uint64 framesRead = drwav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { - drwav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { + ma_dr_wav__bswap_samples_f32(pBufferOut, framesRead*pWav->channels); } return framesRead; } -DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_u8_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } -#ifdef DR_WAV_LIBSNDFILE_COMPAT +#ifdef MA_DR_WAV_LIBSNDFILE_COMPAT for (i = 0; i < sampleCount; ++i) { *pOut++ = (pIn[i] / 256.0f) * 2 - 1; } @@ -80947,7 +81180,7 @@ DRWAV_API void drwav_u8_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampl } #endif } -DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t sampleCount) +MA_API void ma_dr_wav_s16_to_f32(float* pOut, const ma_int16* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { @@ -80957,7 +81190,7 @@ DRWAV_API void drwav_s16_to_f32(float* pOut, const drwav_int16* pIn, size_t samp *pOut++ = pIn[i] * 0.000030517578125f; } } -DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_s24_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { @@ -80965,14 +81198,14 @@ DRWAV_API void drwav_s24_to_f32(float* pOut, const drwav_uint8* pIn, size_t samp } for (i = 0; i < sampleCount; ++i) { double x; - drwav_uint32 a = ((drwav_uint32)(pIn[i*3+0]) << 8); - drwav_uint32 b = ((drwav_uint32)(pIn[i*3+1]) << 16); - drwav_uint32 c = ((drwav_uint32)(pIn[i*3+2]) << 24); - x = (double)((drwav_int32)(a | b | c) >> 8); + ma_uint32 a = ((ma_uint32)(pIn[i*3+0]) << 8); + ma_uint32 b = ((ma_uint32)(pIn[i*3+1]) << 16); + ma_uint32 c = ((ma_uint32)(pIn[i*3+2]) << 24); + x = (double)((ma_int32)(a | b | c) >> 8); *pOut++ = (float)(x * 0.00000011920928955078125); } } -DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t sampleCount) +MA_API void ma_dr_wav_s32_to_f32(float* pOut, const ma_int32* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { @@ -80982,7 +81215,7 @@ DRWAV_API void drwav_s32_to_f32(float* pOut, const drwav_int32* pIn, size_t samp *pOut++ = (float)(pIn[i] / 2147483648.0); } } -DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount) +MA_API void ma_dr_wav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { @@ -80992,88 +81225,88 @@ DRWAV_API void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCou *pOut++ = (float)pIn[i]; } } -DRWAV_API void drwav_alaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_alaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { - *pOut++ = drwav__alaw_to_s16(pIn[i]) / 32768.0f; + *pOut++ = ma_dr_wav__alaw_to_s16(pIn[i]) / 32768.0f; } } -DRWAV_API void drwav_mulaw_to_f32(float* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_mulaw_to_f32(float* pOut, const ma_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { - *pOut++ = drwav__mulaw_to_s16(pIn[i]) / 32768.0f; + *pOut++ = ma_dr_wav__mulaw_to_s16(pIn[i]) / 32768.0f; } } -DRWAV_PRIVATE void drwav__pcm_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +MA_PRIVATE void ma_dr_wav__pcm_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) { unsigned int i; if (bytesPerSample == 1) { - drwav_u8_to_s32(pOut, pIn, totalSampleCount); + ma_dr_wav_u8_to_s32(pOut, pIn, totalSampleCount); return; } if (bytesPerSample == 2) { - drwav_s16_to_s32(pOut, (const drwav_int16*)pIn, totalSampleCount); + ma_dr_wav_s16_to_s32(pOut, (const ma_int16*)pIn, totalSampleCount); return; } if (bytesPerSample == 3) { - drwav_s24_to_s32(pOut, pIn, totalSampleCount); + ma_dr_wav_s24_to_s32(pOut, pIn, totalSampleCount); return; } if (bytesPerSample == 4) { for (i = 0; i < totalSampleCount; ++i) { - *pOut++ = ((const drwav_int32*)pIn)[i]; + *pOut++ = ((const ma_int32*)pIn)[i]; } return; } if (bytesPerSample > 8) { - DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); return; } for (i = 0; i < totalSampleCount; ++i) { - drwav_uint64 sample = 0; + ma_uint64 sample = 0; unsigned int shift = (8 - bytesPerSample) * 8; unsigned int j; for (j = 0; j < bytesPerSample; j += 1) { - DRWAV_ASSERT(j < 8); - sample |= (drwav_uint64)(pIn[j]) << shift; + MA_DR_WAV_ASSERT(j < 8); + sample |= (ma_uint64)(pIn[j]) << shift; shift += 8; } pIn += j; - *pOut++ = (drwav_int32)((drwav_int64)sample >> 32); + *pOut++ = (ma_int32)((ma_int64)sample >> 32); } } -DRWAV_PRIVATE void drwav__ieee_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) +MA_PRIVATE void ma_dr_wav__ieee_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t totalSampleCount, unsigned int bytesPerSample) { if (bytesPerSample == 4) { - drwav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount); + ma_dr_wav_f32_to_s32(pOut, (const float*)pIn, totalSampleCount); return; } else if (bytesPerSample == 8) { - drwav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount); + ma_dr_wav_f64_to_s32(pOut, (const double*)pIn, totalSampleCount); return; } else { - DRWAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); + MA_DR_WAV_ZERO_MEMORY(pOut, totalSampleCount * sizeof(*pOut)); return; } } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__pcm(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__pcm(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) { - return drwav_read_pcm_frames(pWav, framesToRead, pBufferOut); + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM && pWav->bitsPerSample == 32) { + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, pBufferOut); } - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -81083,50 +81316,50 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__pcm(drwav* pWav, drwav_uin } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + ma_dr_wav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__msadpcm_ima(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) { - drwav_uint64 totalFramesRead = 0; - drwav_int16 samples16[2048]; + ma_uint64 totalFramesRead = 0; + ma_int16 samples16[2048]; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, drwav_countof(samples16)/pWav->channels); - drwav_uint64 framesRead = drwav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, ma_dr_wav_countof(samples16)/pWav->channels); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, framesToReadThisIteration, samples16); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); - drwav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); + ma_dr_wav_s16_to_s32(pBufferOut, samples16, (size_t)(framesRead*pWav->channels)); pBufferOut += framesRead*pWav->channels; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ieee(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__ieee(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -81136,32 +81369,32 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__ieee(drwav* pWav, drwav_ui } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); + ma_dr_wav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, bytesPerSample); pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__alaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__alaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -81171,32 +81404,42 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__alaw(drwav* pWav, drwav_ui } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); + ma_dr_wav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__mulaw(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +MA_PRIVATE ma_uint64 ma_dr_wav_read_pcm_frames_s32__mulaw(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) { - drwav_uint64 totalFramesRead; - drwav_uint8 sampleData[4096] = {0}; - drwav_uint32 bytesPerFrame; - drwav_uint32 bytesPerSample; - drwav_uint64 samplesRead; - bytesPerFrame = drwav_get_bytes_per_pcm_frame(pWav); + ma_uint64 totalFramesRead; + ma_uint8 sampleData[4096] = {0}; + ma_uint32 bytesPerFrame; + ma_uint32 bytesPerSample; + ma_uint64 samplesRead; + bytesPerFrame = ma_dr_wav_get_bytes_per_pcm_frame(pWav); if (bytesPerFrame == 0) { return 0; } @@ -81206,69 +81449,79 @@ DRWAV_PRIVATE drwav_uint64 drwav_read_pcm_frames_s32__mulaw(drwav* pWav, drwav_u } totalFramesRead = 0; while (framesToRead > 0) { - drwav_uint64 framesToReadThisIteration = drwav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); - drwav_uint64 framesRead = drwav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); + ma_uint64 framesToReadThisIteration = ma_dr_wav_min(framesToRead, sizeof(sampleData)/bytesPerFrame); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames(pWav, framesToReadThisIteration, sampleData); if (framesRead == 0) { break; } - DRWAV_ASSERT(framesRead <= framesToReadThisIteration); + MA_DR_WAV_ASSERT(framesRead <= framesToReadThisIteration); samplesRead = framesRead * pWav->channels; if ((samplesRead * bytesPerSample) > sizeof(sampleData)) { - DRWAV_ASSERT(DRWAV_FALSE); + MA_DR_WAV_ASSERT(MA_FALSE); break; } - drwav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); + ma_dr_wav_mulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); + #ifdef MA_DR_WAV_LIBSNDFILE_COMPAT + { + if (pWav->container == ma_dr_wav_container_aiff) { + ma_uint64 iSample; + for (iSample = 0; iSample < samplesRead; iSample += 1) { + pBufferOut[iSample] = -pBufferOut[iSample]; + } + } + } + #endif pBufferOut += samplesRead; framesToRead -= framesRead; totalFramesRead += framesRead; } return totalFramesRead; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) { if (pWav == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { - return drwav_read_pcm_frames(pWav, framesToRead, NULL); + return ma_dr_wav_read_pcm_frames(pWav, framesToRead, NULL); } - if (framesToRead * pWav->channels * sizeof(drwav_int32) > DRWAV_SIZE_MAX) { - framesToRead = DRWAV_SIZE_MAX / sizeof(drwav_int32) / pWav->channels; + if (framesToRead * pWav->channels * sizeof(ma_int32) > MA_SIZE_MAX) { + framesToRead = MA_SIZE_MAX / sizeof(ma_int32) / pWav->channels; } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) { - return drwav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_PCM) { + return ma_dr_wav_read_pcm_frames_s32__pcm(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == DR_WAVE_FORMAT_DVI_ADPCM) { - return drwav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ADPCM || pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_DVI_ADPCM) { + return ma_dr_wav_read_pcm_frames_s32__msadpcm_ima(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) { - return drwav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_IEEE_FLOAT) { + return ma_dr_wav_read_pcm_frames_s32__ieee(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) { - return drwav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_ALAW) { + return ma_dr_wav_read_pcm_frames_s32__alaw(pWav, framesToRead, pBufferOut); } - if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) { - return drwav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut); + if (pWav->translatedFormatTag == MA_DR_WAVE_FORMAT_MULAW) { + return ma_dr_wav_read_pcm_frames_s32__mulaw(pWav, framesToRead, pBufferOut); } return 0; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32le(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32le(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) { - drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_FALSE) { - drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_FALSE) { + ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); } return framesRead; } -DRWAV_API drwav_uint64 drwav_read_pcm_frames_s32be(drwav* pWav, drwav_uint64 framesToRead, drwav_int32* pBufferOut) +MA_API ma_uint64 ma_dr_wav_read_pcm_frames_s32be(ma_dr_wav* pWav, ma_uint64 framesToRead, ma_int32* pBufferOut) { - drwav_uint64 framesRead = drwav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); - if (pBufferOut != NULL && drwav__is_little_endian() == DRWAV_TRUE) { - drwav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); + ma_uint64 framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, framesToRead, pBufferOut); + if (pBufferOut != NULL && ma_dr_wav__is_little_endian() == MA_TRUE) { + ma_dr_wav__bswap_samples_s32(pBufferOut, framesRead*pWav->channels); } return framesRead; } -DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_u8_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { @@ -81278,7 +81531,7 @@ DRWAV_API void drwav_u8_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t *pOut++ = ((int)pIn[i] - 128) << 24; } } -DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_t sampleCount) +MA_API void ma_dr_wav_s16_to_s32(ma_int32* pOut, const ma_int16* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { @@ -81288,7 +81541,7 @@ DRWAV_API void drwav_s16_to_s32(drwav_int32* pOut, const drwav_int16* pIn, size_ *pOut++ = pIn[i] << 16; } } -DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_s24_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { @@ -81298,73 +81551,73 @@ DRWAV_API void drwav_s24_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_ unsigned int s0 = pIn[i*3 + 0]; unsigned int s1 = pIn[i*3 + 1]; unsigned int s2 = pIn[i*3 + 2]; - drwav_int32 sample32 = (drwav_int32)((s0 << 8) | (s1 << 16) | (s2 << 24)); + ma_int32 sample32 = (ma_int32)((s0 << 8) | (s1 << 16) | (s2 << 24)); *pOut++ = sample32; } } -DRWAV_API void drwav_f32_to_s32(drwav_int32* pOut, const float* pIn, size_t sampleCount) +MA_API void ma_dr_wav_f32_to_s32(ma_int32* pOut, const float* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { - *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]); + *pOut++ = (ma_int32)(2147483648.0 * pIn[i]); } } -DRWAV_API void drwav_f64_to_s32(drwav_int32* pOut, const double* pIn, size_t sampleCount) +MA_API void ma_dr_wav_f64_to_s32(ma_int32* pOut, const double* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { - *pOut++ = (drwav_int32)(2147483648.0 * pIn[i]); + *pOut++ = (ma_int32)(2147483648.0 * pIn[i]); } } -DRWAV_API void drwav_alaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_alaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i = 0; i < sampleCount; ++i) { - *pOut++ = ((drwav_int32)drwav__alaw_to_s16(pIn[i])) << 16; + *pOut++ = ((ma_int32)ma_dr_wav__alaw_to_s16(pIn[i])) << 16; } } -DRWAV_API void drwav_mulaw_to_s32(drwav_int32* pOut, const drwav_uint8* pIn, size_t sampleCount) +MA_API void ma_dr_wav_mulaw_to_s32(ma_int32* pOut, const ma_uint8* pIn, size_t sampleCount) { size_t i; if (pOut == NULL || pIn == NULL) { return; } for (i= 0; i < sampleCount; ++i) { - *pOut++ = ((drwav_int32)drwav__mulaw_to_s16(pIn[i])) << 16; + *pOut++ = ((ma_int32)ma_dr_wav__mulaw_to_s16(pIn[i])) << 16; } } -DRWAV_PRIVATE drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) +MA_PRIVATE ma_int16* ma_dr_wav__read_pcm_frames_and_close_s16(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) { - drwav_uint64 sampleDataSize; - drwav_int16* pSampleData; - drwav_uint64 framesRead; - DRWAV_ASSERT(pWav != NULL); - sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int16); - if (sampleDataSize > DRWAV_SIZE_MAX) { - drwav_uninit(pWav); + ma_uint64 sampleDataSize; + ma_int16* pSampleData; + ma_uint64 framesRead; + MA_DR_WAV_ASSERT(pWav != NULL); + sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int16); + if (sampleDataSize > MA_SIZE_MAX) { + ma_dr_wav_uninit(pWav); return NULL; } - pSampleData = (drwav_int16*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); + pSampleData = (ma_int16*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); if (pSampleData == NULL) { - drwav_uninit(pWav); + ma_dr_wav_uninit(pWav); return NULL; } - framesRead = drwav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); + framesRead = ma_dr_wav_read_pcm_frames_s16(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); if (framesRead != pWav->totalPCMFrameCount) { - drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - drwav_uninit(pWav); + ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); + ma_dr_wav_uninit(pWav); return NULL; } - drwav_uninit(pWav); + ma_dr_wav_uninit(pWav); if (sampleRate) { *sampleRate = pWav->sampleRate; } @@ -81376,29 +81629,29 @@ DRWAV_PRIVATE drwav_int16* drwav__read_pcm_frames_and_close_s16(drwav* pWav, uns } return pSampleData; } -DRWAV_PRIVATE float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) +MA_PRIVATE float* ma_dr_wav__read_pcm_frames_and_close_f32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) { - drwav_uint64 sampleDataSize; + ma_uint64 sampleDataSize; float* pSampleData; - drwav_uint64 framesRead; - DRWAV_ASSERT(pWav != NULL); + ma_uint64 framesRead; + MA_DR_WAV_ASSERT(pWav != NULL); sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(float); - if (sampleDataSize > DRWAV_SIZE_MAX) { - drwav_uninit(pWav); + if (sampleDataSize > MA_SIZE_MAX) { + ma_dr_wav_uninit(pWav); return NULL; } - pSampleData = (float*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); + pSampleData = (float*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); if (pSampleData == NULL) { - drwav_uninit(pWav); + ma_dr_wav_uninit(pWav); return NULL; } - framesRead = drwav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); + framesRead = ma_dr_wav_read_pcm_frames_f32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); if (framesRead != pWav->totalPCMFrameCount) { - drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - drwav_uninit(pWav); + ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); + ma_dr_wav_uninit(pWav); return NULL; } - drwav_uninit(pWav); + ma_dr_wav_uninit(pWav); if (sampleRate) { *sampleRate = pWav->sampleRate; } @@ -81410,29 +81663,29 @@ DRWAV_PRIVATE float* drwav__read_pcm_frames_and_close_f32(drwav* pWav, unsigned } return pSampleData; } -DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, drwav_uint64* totalFrameCount) +MA_PRIVATE ma_int32* ma_dr_wav__read_pcm_frames_and_close_s32(ma_dr_wav* pWav, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalFrameCount) { - drwav_uint64 sampleDataSize; - drwav_int32* pSampleData; - drwav_uint64 framesRead; - DRWAV_ASSERT(pWav != NULL); - sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(drwav_int32); - if (sampleDataSize > DRWAV_SIZE_MAX) { - drwav_uninit(pWav); + ma_uint64 sampleDataSize; + ma_int32* pSampleData; + ma_uint64 framesRead; + MA_DR_WAV_ASSERT(pWav != NULL); + sampleDataSize = pWav->totalPCMFrameCount * pWav->channels * sizeof(ma_int32); + if (sampleDataSize > MA_SIZE_MAX) { + ma_dr_wav_uninit(pWav); return NULL; } - pSampleData = (drwav_int32*)drwav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); + pSampleData = (ma_int32*)ma_dr_wav__malloc_from_callbacks((size_t)sampleDataSize, &pWav->allocationCallbacks); if (pSampleData == NULL) { - drwav_uninit(pWav); + ma_dr_wav_uninit(pWav); return NULL; } - framesRead = drwav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); + framesRead = ma_dr_wav_read_pcm_frames_s32(pWav, (size_t)pWav->totalPCMFrameCount, pSampleData); if (framesRead != pWav->totalPCMFrameCount) { - drwav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); - drwav_uninit(pWav); + ma_dr_wav__free_from_callbacks(pSampleData, &pWav->allocationCallbacks); + ma_dr_wav_uninit(pWav); return NULL; } - drwav_uninit(pWav); + ma_dr_wav_uninit(pWav); if (sampleRate) { *sampleRate = pWav->sampleRate; } @@ -81444,9 +81697,9 @@ DRWAV_PRIVATE drwav_int32* drwav__read_pcm_frames_and_close_s32(drwav* pWav, uns } return pSampleData; } -DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int16* ma_dr_wav_open_and_read_pcm_frames_s16(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (channelsOut) { *channelsOut = 0; } @@ -81456,14 +81709,14 @@ DRWAV_API drwav_int16* drwav_open_and_read_pcm_frames_s16(drwav_read_proc onRead if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API float* ma_dr_wav_open_and_read_pcm_frames_f32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (channelsOut) { *channelsOut = 0; } @@ -81473,14 +81726,14 @@ DRWAV_API float* drwav_open_and_read_pcm_frames_f32(drwav_read_proc onRead, drwa if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int32* ma_dr_wav_open_and_read_pcm_frames_s32(ma_dr_wav_read_proc onRead, ma_dr_wav_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (channelsOut) { *channelsOut = 0; } @@ -81490,15 +81743,15 @@ DRWAV_API drwav_int32* drwav_open_and_read_pcm_frames_s32(drwav_read_proc onRead if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { + if (!ma_dr_wav_init(&wav, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -#ifndef DR_WAV_NO_STDIO -DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +#ifndef MA_DR_WAV_NO_STDIO +MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (channelsOut) { *channelsOut = 0; } @@ -81508,14 +81761,14 @@ DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16(const char* filen if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { + if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (channelsOut) { *channelsOut = 0; } @@ -81525,14 +81778,14 @@ DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32(const char* filename, u if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { + if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (channelsOut) { *channelsOut = 0; } @@ -81542,15 +81795,15 @@ DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32(const char* filen if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init_file(&wav, filename, pAllocationCallbacks)) { + if (!ma_dr_wav_init_file(&wav, filename, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -#ifndef DR_WAV_NO_WCHAR -DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +#ifndef MA_DR_WAV_NO_WCHAR +MA_API ma_int16* ma_dr_wav_open_file_and_read_pcm_frames_s16_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (sampleRateOut) { *sampleRateOut = 0; } @@ -81560,14 +81813,14 @@ DRWAV_API drwav_int16* drwav_open_file_and_read_pcm_frames_s16_w(const wchar_t* if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { + if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API float* ma_dr_wav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (sampleRateOut) { *sampleRateOut = 0; } @@ -81577,14 +81830,14 @@ DRWAV_API float* drwav_open_file_and_read_pcm_frames_f32_w(const wchar_t* filena if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { + if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int32* ma_dr_wav_open_file_and_read_pcm_frames_s32_w(const wchar_t* filename, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (sampleRateOut) { *sampleRateOut = 0; } @@ -81594,16 +81847,16 @@ DRWAV_API drwav_int32* drwav_open_file_and_read_pcm_frames_s32_w(const wchar_t* if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init_file_w(&wav, filename, pAllocationCallbacks)) { + if (!ma_dr_wav_init_file_w(&wav, filename, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } #endif #endif -DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int16* ma_dr_wav_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (channelsOut) { *channelsOut = 0; } @@ -81613,14 +81866,14 @@ DRWAV_API drwav_int16* drwav_open_memory_and_read_pcm_frames_s16(const void* dat if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { + if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_s16(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API float* ma_dr_wav_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (channelsOut) { *channelsOut = 0; } @@ -81630,14 +81883,14 @@ DRWAV_API float* drwav_open_memory_and_read_pcm_frames_f32(const void* data, siz if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { + if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_f32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } -DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, drwav_uint64* totalFrameCountOut, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int32* ma_dr_wav_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drwav wav; + ma_dr_wav wav; if (channelsOut) { *channelsOut = 0; } @@ -81647,66 +81900,66 @@ DRWAV_API drwav_int32* drwav_open_memory_and_read_pcm_frames_s32(const void* dat if (totalFrameCountOut) { *totalFrameCountOut = 0; } - if (!drwav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { + if (!ma_dr_wav_init_memory(&wav, data, dataSize, pAllocationCallbacks)) { return NULL; } - return drwav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); + return ma_dr_wav__read_pcm_frames_and_close_s32(&wav, channelsOut, sampleRateOut, totalFrameCountOut); } #endif -DRWAV_API void drwav_free(void* p, const drwav_allocation_callbacks* pAllocationCallbacks) +MA_API void ma_dr_wav_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { - drwav__free_from_callbacks(p, pAllocationCallbacks); + ma_dr_wav__free_from_callbacks(p, pAllocationCallbacks); } else { - drwav__free_default(p, NULL); + ma_dr_wav__free_default(p, NULL); } } -DRWAV_API drwav_uint16 drwav_bytes_to_u16(const drwav_uint8* data) +MA_API ma_uint16 ma_dr_wav_bytes_to_u16(const ma_uint8* data) { - return ((drwav_uint16)data[0] << 0) | ((drwav_uint16)data[1] << 8); + return ((ma_uint16)data[0] << 0) | ((ma_uint16)data[1] << 8); } -DRWAV_API drwav_int16 drwav_bytes_to_s16(const drwav_uint8* data) +MA_API ma_int16 ma_dr_wav_bytes_to_s16(const ma_uint8* data) { - return (drwav_int16)drwav_bytes_to_u16(data); + return (ma_int16)ma_dr_wav_bytes_to_u16(data); } -DRWAV_API drwav_uint32 drwav_bytes_to_u32(const drwav_uint8* data) +MA_API ma_uint32 ma_dr_wav_bytes_to_u32(const ma_uint8* data) { - return ((drwav_uint32)data[0] << 0) | ((drwav_uint32)data[1] << 8) | ((drwav_uint32)data[2] << 16) | ((drwav_uint32)data[3] << 24); + return ma_dr_wav_bytes_to_u32_le(data); } -DRWAV_API float drwav_bytes_to_f32(const drwav_uint8* data) +MA_API float ma_dr_wav_bytes_to_f32(const ma_uint8* data) { union { - drwav_uint32 u32; + ma_uint32 u32; float f32; } value; - value.u32 = drwav_bytes_to_u32(data); + value.u32 = ma_dr_wav_bytes_to_u32(data); return value.f32; } -DRWAV_API drwav_int32 drwav_bytes_to_s32(const drwav_uint8* data) +MA_API ma_int32 ma_dr_wav_bytes_to_s32(const ma_uint8* data) { - return (drwav_int32)drwav_bytes_to_u32(data); + return (ma_int32)ma_dr_wav_bytes_to_u32(data); } -DRWAV_API drwav_uint64 drwav_bytes_to_u64(const drwav_uint8* data) +MA_API ma_uint64 ma_dr_wav_bytes_to_u64(const ma_uint8* data) { return - ((drwav_uint64)data[0] << 0) | ((drwav_uint64)data[1] << 8) | ((drwav_uint64)data[2] << 16) | ((drwav_uint64)data[3] << 24) | - ((drwav_uint64)data[4] << 32) | ((drwav_uint64)data[5] << 40) | ((drwav_uint64)data[6] << 48) | ((drwav_uint64)data[7] << 56); + ((ma_uint64)data[0] << 0) | ((ma_uint64)data[1] << 8) | ((ma_uint64)data[2] << 16) | ((ma_uint64)data[3] << 24) | + ((ma_uint64)data[4] << 32) | ((ma_uint64)data[5] << 40) | ((ma_uint64)data[6] << 48) | ((ma_uint64)data[7] << 56); } -DRWAV_API drwav_int64 drwav_bytes_to_s64(const drwav_uint8* data) +MA_API ma_int64 ma_dr_wav_bytes_to_s64(const ma_uint8* data) { - return (drwav_int64)drwav_bytes_to_u64(data); + return (ma_int64)ma_dr_wav_bytes_to_u64(data); } -DRWAV_API drwav_bool32 drwav_guid_equal(const drwav_uint8 a[16], const drwav_uint8 b[16]) +MA_API ma_bool32 ma_dr_wav_guid_equal(const ma_uint8 a[16], const ma_uint8 b[16]) { int i; for (i = 0; i < 16; i += 1) { if (a[i] != b[i]) { - return DRWAV_FALSE; + return MA_FALSE; } } - return DRWAV_TRUE; + return MA_TRUE; } -DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) +MA_API ma_bool32 ma_dr_wav_fourcc_equal(const ma_uint8* a, const char* b) { return a[0] == b[0] && @@ -81719,14 +81972,14 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) #endif #endif /* dr_wav_c end */ -#endif /* DRWAV_IMPLEMENTATION */ +#endif /* MA_DR_WAV_IMPLEMENTATION */ #endif /* MA_NO_WAV */ #if !defined(MA_NO_FLAC) && !defined(MA_NO_DECODING) -#if !defined(DR_FLAC_IMPLEMENTATION) && !defined(DRFLAC_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ +#if !defined(MA_DR_FLAC_IMPLEMENTATION) && !defined(MA_DR_FLAC_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ /* dr_flac_c begin */ -#ifndef dr_flac_c -#define dr_flac_c +#ifndef ma_dr_flac_c +#define ma_dr_flac_c #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic push #if __GNUC__ >= 7 @@ -81747,85 +82000,60 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) #endif #include #include -#ifdef _MSC_VER - #define DRFLAC_INLINE __forceinline -#elif defined(__GNUC__) - #if defined(__STRICT_ANSI__) - #define DRFLAC_GNUC_INLINE_HINT __inline__ - #else - #define DRFLAC_GNUC_INLINE_HINT inline - #endif - #if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 2)) || defined(__clang__) - #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT __attribute__((always_inline)) - #else - #define DRFLAC_INLINE DRFLAC_GNUC_INLINE_HINT - #endif -#elif defined(__WATCOMC__) - #define DRFLAC_INLINE __inline -#else - #define DRFLAC_INLINE -#endif -#if defined(__x86_64__) || defined(_M_X64) - #define DRFLAC_X64 -#elif defined(__i386) || defined(_M_IX86) - #define DRFLAC_X86 -#elif defined(__arm__) || defined(_M_ARM) || defined(__arm64) || defined(__arm64__) || defined(__aarch64__) || defined(_M_ARM64) - #define DRFLAC_ARM -#endif -#if !defined(DR_FLAC_NO_SIMD) - #if defined(DRFLAC_X64) || defined(DRFLAC_X86) +#if !defined(MA_DR_FLAC_NO_SIMD) + #if defined(MA_X64) || defined(MA_X86) #if defined(_MSC_VER) && !defined(__clang__) - #if _MSC_VER >= 1400 && !defined(DRFLAC_NO_SSE2) - #define DRFLAC_SUPPORT_SSE2 + #if _MSC_VER >= 1400 && !defined(MA_DR_FLAC_NO_SSE2) + #define MA_DR_FLAC_SUPPORT_SSE2 #endif - #if _MSC_VER >= 1600 && !defined(DRFLAC_NO_SSE41) - #define DRFLAC_SUPPORT_SSE41 + #if _MSC_VER >= 1600 && !defined(MA_DR_FLAC_NO_SSE41) + #define MA_DR_FLAC_SUPPORT_SSE41 #endif #elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))) - #if defined(__SSE2__) && !defined(DRFLAC_NO_SSE2) - #define DRFLAC_SUPPORT_SSE2 + #if defined(__SSE2__) && !defined(MA_DR_FLAC_NO_SSE2) + #define MA_DR_FLAC_SUPPORT_SSE2 #endif - #if defined(__SSE4_1__) && !defined(DRFLAC_NO_SSE41) - #define DRFLAC_SUPPORT_SSE41 + #if defined(__SSE4_1__) && !defined(MA_DR_FLAC_NO_SSE41) + #define MA_DR_FLAC_SUPPORT_SSE41 #endif #endif #if !defined(__GNUC__) && !defined(__clang__) && defined(__has_include) - #if !defined(DRFLAC_SUPPORT_SSE2) && !defined(DRFLAC_NO_SSE2) && __has_include() - #define DRFLAC_SUPPORT_SSE2 + #if !defined(MA_DR_FLAC_SUPPORT_SSE2) && !defined(MA_DR_FLAC_NO_SSE2) && __has_include() + #define MA_DR_FLAC_SUPPORT_SSE2 #endif - #if !defined(DRFLAC_SUPPORT_SSE41) && !defined(DRFLAC_NO_SSE41) && __has_include() - #define DRFLAC_SUPPORT_SSE41 + #if !defined(MA_DR_FLAC_SUPPORT_SSE41) && !defined(MA_DR_FLAC_NO_SSE41) && __has_include() + #define MA_DR_FLAC_SUPPORT_SSE41 #endif #endif - #if defined(DRFLAC_SUPPORT_SSE41) + #if defined(MA_DR_FLAC_SUPPORT_SSE41) #include - #elif defined(DRFLAC_SUPPORT_SSE2) + #elif defined(MA_DR_FLAC_SUPPORT_SSE2) #include #endif #endif - #if defined(DRFLAC_ARM) - #if !defined(DRFLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - #define DRFLAC_SUPPORT_NEON + #if defined(MA_ARM) + #if !defined(MA_DR_FLAC_NO_NEON) && (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) + #define MA_DR_FLAC_SUPPORT_NEON #include #endif #endif #endif -#if !defined(DR_FLAC_NO_SIMD) && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) +#if !defined(MA_DR_FLAC_NO_SIMD) && (defined(MA_X86) || defined(MA_X64)) #if defined(_MSC_VER) && !defined(__clang__) #if _MSC_VER >= 1400 #include - static void drflac__cpuid(int info[4], int fid) + static void ma_dr_flac__cpuid(int info[4], int fid) { __cpuid(info, fid); } #else - #define DRFLAC_NO_CPUID + #define MA_DR_FLAC_NO_CPUID #endif #else #if defined(__GNUC__) || defined(__clang__) - static void drflac__cpuid(int info[4], int fid) + static void ma_dr_flac__cpuid(int info[4], int fid) { - #if defined(DRFLAC_X86) && defined(__PIC__) + #if defined(MA_X86) && defined(__PIC__) __asm__ __volatile__ ( "xchg{l} {%%}ebx, %k1;" "cpuid;" @@ -81839,100 +82067,100 @@ DRWAV_API drwav_bool32 drwav_fourcc_equal(const drwav_uint8* a, const char* b) #endif } #else - #define DRFLAC_NO_CPUID + #define MA_DR_FLAC_NO_CPUID #endif #endif #else - #define DRFLAC_NO_CPUID + #define MA_DR_FLAC_NO_CPUID #endif -static DRFLAC_INLINE drflac_bool32 drflac_has_sse2(void) +static MA_INLINE ma_bool32 ma_dr_flac_has_sse2(void) { -#if defined(DRFLAC_SUPPORT_SSE2) - #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE2) - #if defined(DRFLAC_X64) - return DRFLAC_TRUE; +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE2) + #if defined(MA_X64) + return MA_TRUE; #elif (defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__) - return DRFLAC_TRUE; + return MA_TRUE; #else - #if defined(DRFLAC_NO_CPUID) - return DRFLAC_FALSE; + #if defined(MA_DR_FLAC_NO_CPUID) + return MA_FALSE; #else int info[4]; - drflac__cpuid(info, 1); + ma_dr_flac__cpuid(info, 1); return (info[3] & (1 << 26)) != 0; #endif #endif #else - return DRFLAC_FALSE; + return MA_FALSE; #endif #else - return DRFLAC_FALSE; + return MA_FALSE; #endif } -static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void) +static MA_INLINE ma_bool32 ma_dr_flac_has_sse41(void) { -#if defined(DRFLAC_SUPPORT_SSE41) - #if (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(DRFLAC_NO_SSE41) +#if defined(MA_DR_FLAC_SUPPORT_SSE41) + #if (defined(MA_X64) || defined(MA_X86)) && !defined(MA_DR_FLAC_NO_SSE41) #if defined(__SSE4_1__) || defined(__AVX__) - return DRFLAC_TRUE; + return MA_TRUE; #else - #if defined(DRFLAC_NO_CPUID) - return DRFLAC_FALSE; + #if defined(MA_DR_FLAC_NO_CPUID) + return MA_FALSE; #else int info[4]; - drflac__cpuid(info, 1); + ma_dr_flac__cpuid(info, 1); return (info[2] & (1 << 19)) != 0; #endif #endif #else - return DRFLAC_FALSE; + return MA_FALSE; #endif #else - return DRFLAC_FALSE; + return MA_FALSE; #endif } -#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(DRFLAC_X86) || defined(DRFLAC_X64)) && !defined(__clang__) - #define DRFLAC_HAS_LZCNT_INTRINSIC +#if defined(_MSC_VER) && _MSC_VER >= 1500 && (defined(MA_X86) || defined(MA_X64)) && !defined(__clang__) + #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC #elif (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))) - #define DRFLAC_HAS_LZCNT_INTRINSIC + #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC #elif defined(__clang__) #if defined(__has_builtin) #if __has_builtin(__builtin_clzll) || __has_builtin(__builtin_clzl) - #define DRFLAC_HAS_LZCNT_INTRINSIC + #define MA_DR_FLAC_HAS_LZCNT_INTRINSIC #endif #endif #endif #if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(__clang__) - #define DRFLAC_HAS_BYTESWAP16_INTRINSIC - #define DRFLAC_HAS_BYTESWAP32_INTRINSIC - #define DRFLAC_HAS_BYTESWAP64_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC #elif defined(__clang__) #if defined(__has_builtin) #if __has_builtin(__builtin_bswap16) - #define DRFLAC_HAS_BYTESWAP16_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC #endif #if __has_builtin(__builtin_bswap32) - #define DRFLAC_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC #endif #if __has_builtin(__builtin_bswap64) - #define DRFLAC_HAS_BYTESWAP64_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC #endif #endif #elif defined(__GNUC__) #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) - #define DRFLAC_HAS_BYTESWAP32_INTRINSIC - #define DRFLAC_HAS_BYTESWAP64_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC #endif #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) - #define DRFLAC_HAS_BYTESWAP16_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC #endif #elif defined(__WATCOMC__) && defined(__386__) - #define DRFLAC_HAS_BYTESWAP16_INTRINSIC - #define DRFLAC_HAS_BYTESWAP32_INTRINSIC - #define DRFLAC_HAS_BYTESWAP64_INTRINSIC - extern __inline drflac_uint16 _watcom_bswap16(drflac_uint16); - extern __inline drflac_uint32 _watcom_bswap32(drflac_uint32); - extern __inline drflac_uint64 _watcom_bswap64(drflac_uint64); + #define MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC + #define MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC + extern __inline ma_uint16 _watcom_bswap16(ma_uint16); + extern __inline ma_uint32 _watcom_bswap32(ma_uint32); + extern __inline ma_uint64 _watcom_bswap64(ma_uint64); #pragma aux _watcom_bswap16 = \ "xchg al, ah" \ parm [ax] \ @@ -81951,185 +82179,129 @@ static DRFLAC_INLINE drflac_bool32 drflac_has_sse41(void) value [eax edx] \ modify nomemory; #endif -#ifndef DRFLAC_ASSERT +#ifndef MA_DR_FLAC_ASSERT #include -#define DRFLAC_ASSERT(expression) assert(expression) +#define MA_DR_FLAC_ASSERT(expression) assert(expression) #endif -#ifndef DRFLAC_MALLOC -#define DRFLAC_MALLOC(sz) malloc((sz)) +#ifndef MA_DR_FLAC_MALLOC +#define MA_DR_FLAC_MALLOC(sz) malloc((sz)) #endif -#ifndef DRFLAC_REALLOC -#define DRFLAC_REALLOC(p, sz) realloc((p), (sz)) +#ifndef MA_DR_FLAC_REALLOC +#define MA_DR_FLAC_REALLOC(p, sz) realloc((p), (sz)) #endif -#ifndef DRFLAC_FREE -#define DRFLAC_FREE(p) free((p)) +#ifndef MA_DR_FLAC_FREE +#define MA_DR_FLAC_FREE(p) free((p)) #endif -#ifndef DRFLAC_COPY_MEMORY -#define DRFLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#ifndef MA_DR_FLAC_COPY_MEMORY +#define MA_DR_FLAC_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) #endif -#ifndef DRFLAC_ZERO_MEMORY -#define DRFLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#ifndef MA_DR_FLAC_ZERO_MEMORY +#define MA_DR_FLAC_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) #endif -#ifndef DRFLAC_ZERO_OBJECT -#define DRFLAC_ZERO_OBJECT(p) DRFLAC_ZERO_MEMORY((p), sizeof(*(p))) +#ifndef MA_DR_FLAC_ZERO_OBJECT +#define MA_DR_FLAC_ZERO_OBJECT(p) MA_DR_FLAC_ZERO_MEMORY((p), sizeof(*(p))) #endif -#define DRFLAC_MAX_SIMD_VECTOR_SIZE 64 -typedef drflac_int32 drflac_result; -#define DRFLAC_SUCCESS 0 -#define DRFLAC_ERROR -1 -#define DRFLAC_INVALID_ARGS -2 -#define DRFLAC_INVALID_OPERATION -3 -#define DRFLAC_OUT_OF_MEMORY -4 -#define DRFLAC_OUT_OF_RANGE -5 -#define DRFLAC_ACCESS_DENIED -6 -#define DRFLAC_DOES_NOT_EXIST -7 -#define DRFLAC_ALREADY_EXISTS -8 -#define DRFLAC_TOO_MANY_OPEN_FILES -9 -#define DRFLAC_INVALID_FILE -10 -#define DRFLAC_TOO_BIG -11 -#define DRFLAC_PATH_TOO_LONG -12 -#define DRFLAC_NAME_TOO_LONG -13 -#define DRFLAC_NOT_DIRECTORY -14 -#define DRFLAC_IS_DIRECTORY -15 -#define DRFLAC_DIRECTORY_NOT_EMPTY -16 -#define DRFLAC_END_OF_FILE -17 -#define DRFLAC_NO_SPACE -18 -#define DRFLAC_BUSY -19 -#define DRFLAC_IO_ERROR -20 -#define DRFLAC_INTERRUPT -21 -#define DRFLAC_UNAVAILABLE -22 -#define DRFLAC_ALREADY_IN_USE -23 -#define DRFLAC_BAD_ADDRESS -24 -#define DRFLAC_BAD_SEEK -25 -#define DRFLAC_BAD_PIPE -26 -#define DRFLAC_DEADLOCK -27 -#define DRFLAC_TOO_MANY_LINKS -28 -#define DRFLAC_NOT_IMPLEMENTED -29 -#define DRFLAC_NO_MESSAGE -30 -#define DRFLAC_BAD_MESSAGE -31 -#define DRFLAC_NO_DATA_AVAILABLE -32 -#define DRFLAC_INVALID_DATA -33 -#define DRFLAC_TIMEOUT -34 -#define DRFLAC_NO_NETWORK -35 -#define DRFLAC_NOT_UNIQUE -36 -#define DRFLAC_NOT_SOCKET -37 -#define DRFLAC_NO_ADDRESS -38 -#define DRFLAC_BAD_PROTOCOL -39 -#define DRFLAC_PROTOCOL_UNAVAILABLE -40 -#define DRFLAC_PROTOCOL_NOT_SUPPORTED -41 -#define DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED -42 -#define DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED -43 -#define DRFLAC_SOCKET_NOT_SUPPORTED -44 -#define DRFLAC_CONNECTION_RESET -45 -#define DRFLAC_ALREADY_CONNECTED -46 -#define DRFLAC_NOT_CONNECTED -47 -#define DRFLAC_CONNECTION_REFUSED -48 -#define DRFLAC_NO_HOST -49 -#define DRFLAC_IN_PROGRESS -50 -#define DRFLAC_CANCELLED -51 -#define DRFLAC_MEMORY_ALREADY_MAPPED -52 -#define DRFLAC_AT_END -53 -#define DRFLAC_CRC_MISMATCH -128 -#define DRFLAC_SUBFRAME_CONSTANT 0 -#define DRFLAC_SUBFRAME_VERBATIM 1 -#define DRFLAC_SUBFRAME_FIXED 8 -#define DRFLAC_SUBFRAME_LPC 32 -#define DRFLAC_SUBFRAME_RESERVED 255 -#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0 -#define DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1 -#define DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0 -#define DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 -#define DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 -#define DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 -#define DRFLAC_SEEKPOINT_SIZE_IN_BYTES 18 -#define DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36 -#define DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12 -#define drflac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) -DRFLAC_API void drflac_version(drflac_uint32* pMajor, drflac_uint32* pMinor, drflac_uint32* pRevision) +#define MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE 64 +#define MA_DR_FLAC_SUBFRAME_CONSTANT 0 +#define MA_DR_FLAC_SUBFRAME_VERBATIM 1 +#define MA_DR_FLAC_SUBFRAME_FIXED 8 +#define MA_DR_FLAC_SUBFRAME_LPC 32 +#define MA_DR_FLAC_SUBFRAME_RESERVED 255 +#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE 0 +#define MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2 1 +#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT 0 +#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE 8 +#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE 9 +#define MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE 10 +#define MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES 18 +#define MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES 36 +#define MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES 12 +#define ma_dr_flac_align(x, a) ((((x) + (a) - 1) / (a)) * (a)) +MA_API void ma_dr_flac_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) { if (pMajor) { - *pMajor = DRFLAC_VERSION_MAJOR; + *pMajor = MA_DR_FLAC_VERSION_MAJOR; } if (pMinor) { - *pMinor = DRFLAC_VERSION_MINOR; + *pMinor = MA_DR_FLAC_VERSION_MINOR; } if (pRevision) { - *pRevision = DRFLAC_VERSION_REVISION; + *pRevision = MA_DR_FLAC_VERSION_REVISION; } } -DRFLAC_API const char* drflac_version_string(void) +MA_API const char* ma_dr_flac_version_string(void) { - return DRFLAC_VERSION_STRING; + return MA_DR_FLAC_VERSION_STRING; } #if defined(__has_feature) #if __has_feature(thread_sanitizer) - #define DRFLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread"))) + #define MA_DR_FLAC_NO_THREAD_SANITIZE __attribute__((no_sanitize("thread"))) #else - #define DRFLAC_NO_THREAD_SANITIZE + #define MA_DR_FLAC_NO_THREAD_SANITIZE #endif #else - #define DRFLAC_NO_THREAD_SANITIZE + #define MA_DR_FLAC_NO_THREAD_SANITIZE #endif -#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) -static drflac_bool32 drflac__gIsLZCNTSupported = DRFLAC_FALSE; +#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) +static ma_bool32 ma_dr_flac__gIsLZCNTSupported = MA_FALSE; #endif -#ifndef DRFLAC_NO_CPUID -static drflac_bool32 drflac__gIsSSE2Supported = DRFLAC_FALSE; -static drflac_bool32 drflac__gIsSSE41Supported = DRFLAC_FALSE; -DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void) +#ifndef MA_DR_FLAC_NO_CPUID +static ma_bool32 ma_dr_flac__gIsSSE2Supported = MA_FALSE; +static ma_bool32 ma_dr_flac__gIsSSE41Supported = MA_FALSE; +MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void) { - static drflac_bool32 isCPUCapsInitialized = DRFLAC_FALSE; + static ma_bool32 isCPUCapsInitialized = MA_FALSE; if (!isCPUCapsInitialized) { -#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) +#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) int info[4] = {0}; - drflac__cpuid(info, 0x80000001); - drflac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; + ma_dr_flac__cpuid(info, 0x80000001); + ma_dr_flac__gIsLZCNTSupported = (info[2] & (1 << 5)) != 0; #endif - drflac__gIsSSE2Supported = drflac_has_sse2(); - drflac__gIsSSE41Supported = drflac_has_sse41(); - isCPUCapsInitialized = DRFLAC_TRUE; + ma_dr_flac__gIsSSE2Supported = ma_dr_flac_has_sse2(); + ma_dr_flac__gIsSSE41Supported = ma_dr_flac_has_sse41(); + isCPUCapsInitialized = MA_TRUE; } } #else -static drflac_bool32 drflac__gIsNEONSupported = DRFLAC_FALSE; -static DRFLAC_INLINE drflac_bool32 drflac__has_neon(void) +static ma_bool32 ma_dr_flac__gIsNEONSupported = MA_FALSE; +static MA_INLINE ma_bool32 ma_dr_flac__has_neon(void) { -#if defined(DRFLAC_SUPPORT_NEON) - #if defined(DRFLAC_ARM) && !defined(DRFLAC_NO_NEON) +#if defined(MA_DR_FLAC_SUPPORT_NEON) + #if defined(MA_ARM) && !defined(MA_DR_FLAC_NO_NEON) #if (defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)) - return DRFLAC_TRUE; + return MA_TRUE; #else - return DRFLAC_FALSE; + return MA_FALSE; #endif #else - return DRFLAC_FALSE; + return MA_FALSE; #endif #else - return DRFLAC_FALSE; + return MA_FALSE; #endif } -DRFLAC_NO_THREAD_SANITIZE static void drflac__init_cpu_caps(void) +MA_DR_FLAC_NO_THREAD_SANITIZE static void ma_dr_flac__init_cpu_caps(void) { - drflac__gIsNEONSupported = drflac__has_neon(); -#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) - drflac__gIsLZCNTSupported = DRFLAC_TRUE; + ma_dr_flac__gIsNEONSupported = ma_dr_flac__has_neon(); +#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) + ma_dr_flac__gIsLZCNTSupported = MA_TRUE; #endif } #endif -static DRFLAC_INLINE drflac_bool32 drflac__is_little_endian(void) +static MA_INLINE ma_bool32 ma_dr_flac__is_little_endian(void) { -#if defined(DRFLAC_X86) || defined(DRFLAC_X64) - return DRFLAC_TRUE; +#if defined(MA_X86) || defined(MA_X64) + return MA_TRUE; #elif defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && __BYTE_ORDER == __LITTLE_ENDIAN - return DRFLAC_TRUE; + return MA_TRUE; #else int n = 1; return (*(char*)&n) == 1; #endif } -static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n) +static MA_INLINE ma_uint16 ma_dr_flac__swap_endian_uint16(ma_uint16 n) { -#ifdef DRFLAC_HAS_BYTESWAP16_INTRINSIC +#ifdef MA_DR_FLAC_HAS_BYTESWAP16_INTRINSIC #if defined(_MSC_VER) && !defined(__clang__) return _byteswap_ushort(n); #elif defined(__GNUC__) || defined(__clang__) @@ -82144,16 +82316,16 @@ static DRFLAC_INLINE drflac_uint16 drflac__swap_endian_uint16(drflac_uint16 n) ((n & 0x00FF) << 8); #endif } -static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n) +static MA_INLINE ma_uint32 ma_dr_flac__swap_endian_uint32(ma_uint32 n) { -#ifdef DRFLAC_HAS_BYTESWAP32_INTRINSIC +#ifdef MA_DR_FLAC_HAS_BYTESWAP32_INTRINSIC #if defined(_MSC_VER) && !defined(__clang__) return _byteswap_ulong(n); #elif defined(__GNUC__) || defined(__clang__) - #if defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(DRFLAC_64BIT) - drflac_uint32 r; + #if defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 6) && !defined(MA_64BIT) + ma_uint32 r; __asm__ __volatile__ ( - #if defined(DRFLAC_64BIT) + #if defined(MA_64BIT) "rev %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(n) #else "rev %[out], %[in]" : [out]"=r"(r) : [in]"r"(n) @@ -82175,9 +82347,9 @@ static DRFLAC_INLINE drflac_uint32 drflac__swap_endian_uint32(drflac_uint32 n) ((n & 0x000000FF) << 24); #endif } -static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n) +static MA_INLINE ma_uint64 ma_dr_flac__swap_endian_uint64(ma_uint64 n) { -#ifdef DRFLAC_HAS_BYTESWAP64_INTRINSIC +#ifdef MA_DR_FLAC_HAS_BYTESWAP64_INTRINSIC #if defined(_MSC_VER) && !defined(__clang__) return _byteswap_uint64(n); #elif defined(__GNUC__) || defined(__clang__) @@ -82188,64 +82360,64 @@ static DRFLAC_INLINE drflac_uint64 drflac__swap_endian_uint64(drflac_uint64 n) #error "This compiler does not support the byte swap intrinsic." #endif #else - return ((n & ((drflac_uint64)0xFF000000 << 32)) >> 56) | - ((n & ((drflac_uint64)0x00FF0000 << 32)) >> 40) | - ((n & ((drflac_uint64)0x0000FF00 << 32)) >> 24) | - ((n & ((drflac_uint64)0x000000FF << 32)) >> 8) | - ((n & ((drflac_uint64)0xFF000000 )) << 8) | - ((n & ((drflac_uint64)0x00FF0000 )) << 24) | - ((n & ((drflac_uint64)0x0000FF00 )) << 40) | - ((n & ((drflac_uint64)0x000000FF )) << 56); + return ((n & ((ma_uint64)0xFF000000 << 32)) >> 56) | + ((n & ((ma_uint64)0x00FF0000 << 32)) >> 40) | + ((n & ((ma_uint64)0x0000FF00 << 32)) >> 24) | + ((n & ((ma_uint64)0x000000FF << 32)) >> 8) | + ((n & ((ma_uint64)0xFF000000 )) << 8) | + ((n & ((ma_uint64)0x00FF0000 )) << 24) | + ((n & ((ma_uint64)0x0000FF00 )) << 40) | + ((n & ((ma_uint64)0x000000FF )) << 56); #endif } -static DRFLAC_INLINE drflac_uint16 drflac__be2host_16(drflac_uint16 n) +static MA_INLINE ma_uint16 ma_dr_flac__be2host_16(ma_uint16 n) { - if (drflac__is_little_endian()) { - return drflac__swap_endian_uint16(n); + if (ma_dr_flac__is_little_endian()) { + return ma_dr_flac__swap_endian_uint16(n); } return n; } -static DRFLAC_INLINE drflac_uint32 drflac__be2host_32(drflac_uint32 n) +static MA_INLINE ma_uint32 ma_dr_flac__be2host_32(ma_uint32 n) { - if (drflac__is_little_endian()) { - return drflac__swap_endian_uint32(n); + if (ma_dr_flac__is_little_endian()) { + return ma_dr_flac__swap_endian_uint32(n); } return n; } -static DRFLAC_INLINE drflac_uint32 drflac__be2host_32_ptr_unaligned(const void* pData) +static MA_INLINE ma_uint32 ma_dr_flac__be2host_32_ptr_unaligned(const void* pData) { - const drflac_uint8* pNum = (drflac_uint8*)pData; + const ma_uint8* pNum = (ma_uint8*)pData; return *(pNum) << 24 | *(pNum+1) << 16 | *(pNum+2) << 8 | *(pNum+3); } -static DRFLAC_INLINE drflac_uint64 drflac__be2host_64(drflac_uint64 n) +static MA_INLINE ma_uint64 ma_dr_flac__be2host_64(ma_uint64 n) { - if (drflac__is_little_endian()) { - return drflac__swap_endian_uint64(n); + if (ma_dr_flac__is_little_endian()) { + return ma_dr_flac__swap_endian_uint64(n); } return n; } -static DRFLAC_INLINE drflac_uint32 drflac__le2host_32(drflac_uint32 n) +static MA_INLINE ma_uint32 ma_dr_flac__le2host_32(ma_uint32 n) { - if (!drflac__is_little_endian()) { - return drflac__swap_endian_uint32(n); + if (!ma_dr_flac__is_little_endian()) { + return ma_dr_flac__swap_endian_uint32(n); } return n; } -static DRFLAC_INLINE drflac_uint32 drflac__le2host_32_ptr_unaligned(const void* pData) +static MA_INLINE ma_uint32 ma_dr_flac__le2host_32_ptr_unaligned(const void* pData) { - const drflac_uint8* pNum = (drflac_uint8*)pData; + const ma_uint8* pNum = (ma_uint8*)pData; return *pNum | *(pNum+1) << 8 | *(pNum+2) << 16 | *(pNum+3) << 24; } -static DRFLAC_INLINE drflac_uint32 drflac__unsynchsafe_32(drflac_uint32 n) +static MA_INLINE ma_uint32 ma_dr_flac__unsynchsafe_32(ma_uint32 n) { - drflac_uint32 result = 0; + ma_uint32 result = 0; result |= (n & 0x7F000000) >> 3; result |= (n & 0x007F0000) >> 2; result |= (n & 0x00007F00) >> 1; result |= (n & 0x0000007F) >> 0; return result; } -static drflac_uint8 drflac__crc8_table[] = { +static ma_uint8 ma_dr_flac__crc8_table[] = { 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, @@ -82263,7 +82435,7 @@ static drflac_uint8 drflac__crc8_table[] = { 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 }; -static drflac_uint16 drflac__crc16_table[] = { +static ma_uint16 ma_dr_flac__crc16_table[] = { 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, @@ -82297,22 +82469,22 @@ static drflac_uint16 drflac__crc16_table[] = { 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 }; -static DRFLAC_INLINE drflac_uint8 drflac_crc8_byte(drflac_uint8 crc, drflac_uint8 data) +static MA_INLINE ma_uint8 ma_dr_flac_crc8_byte(ma_uint8 crc, ma_uint8 data) { - return drflac__crc8_table[crc ^ data]; + return ma_dr_flac__crc8_table[crc ^ data]; } -static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 data, drflac_uint32 count) +static MA_INLINE ma_uint8 ma_dr_flac_crc8(ma_uint8 crc, ma_uint32 data, ma_uint32 count) { -#ifdef DR_FLAC_NO_CRC +#ifdef MA_DR_FLAC_NO_CRC (void)crc; (void)data; (void)count; return 0; #else #if 0 - drflac_uint8 p = 0x07; + ma_uint8 p = 0x07; for (int i = count-1; i >= 0; --i) { - drflac_uint8 bit = (data & (1 << i)) >> i; + ma_uint8 bit = (data & (1 << i)) >> i; if (crc & 0x80) { crc = ((crc << 1) | bit) ^ p; } else { @@ -82321,75 +82493,75 @@ static DRFLAC_INLINE drflac_uint8 drflac_crc8(drflac_uint8 crc, drflac_uint32 da } return crc; #else - drflac_uint32 wholeBytes; - drflac_uint32 leftoverBits; - drflac_uint64 leftoverDataMask; - static drflac_uint64 leftoverDataMaskTable[8] = { + ma_uint32 wholeBytes; + ma_uint32 leftoverBits; + ma_uint64 leftoverDataMask; + static ma_uint64 leftoverDataMaskTable[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; - DRFLAC_ASSERT(count <= 32); + MA_DR_FLAC_ASSERT(count <= 32); wholeBytes = count >> 3; leftoverBits = count - (wholeBytes*8); leftoverDataMask = leftoverDataMaskTable[leftoverBits]; switch (wholeBytes) { - case 4: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = drflac_crc8_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (drflac_uint8)((crc << leftoverBits) ^ drflac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]); + case 4: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = ma_dr_flac_crc8_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (ma_uint8)((crc << leftoverBits) ^ ma_dr_flac__crc8_table[(crc >> (8 - leftoverBits)) ^ (data & leftoverDataMask)]); } return crc; #endif #endif } -static DRFLAC_INLINE drflac_uint16 drflac_crc16_byte(drflac_uint16 crc, drflac_uint8 data) +static MA_INLINE ma_uint16 ma_dr_flac_crc16_byte(ma_uint16 crc, ma_uint8 data) { - return (crc << 8) ^ drflac__crc16_table[(drflac_uint8)(crc >> 8) ^ data]; + return (crc << 8) ^ ma_dr_flac__crc16_table[(ma_uint8)(crc >> 8) ^ data]; } -static DRFLAC_INLINE drflac_uint16 drflac_crc16_cache(drflac_uint16 crc, drflac_cache_t data) +static MA_INLINE ma_uint16 ma_dr_flac_crc16_cache(ma_uint16 crc, ma_dr_flac_cache_t data) { -#ifdef DRFLAC_64BIT - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF)); - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF)); - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF)); - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF)); +#ifdef MA_64BIT + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF)); #endif - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF)); - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF)); - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF)); - crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF)); + crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF)); return crc; } -static DRFLAC_INLINE drflac_uint16 drflac_crc16_bytes(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 byteCount) +static MA_INLINE ma_uint16 ma_dr_flac_crc16_bytes(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 byteCount) { switch (byteCount) { -#ifdef DRFLAC_64BIT - case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 56) & 0xFF)); - case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 48) & 0xFF)); - case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 40) & 0xFF)); - case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 32) & 0xFF)); +#ifdef MA_64BIT + case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 56) & 0xFF)); + case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 48) & 0xFF)); + case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 40) & 0xFF)); + case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 32) & 0xFF)); #endif - case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 24) & 0xFF)); - case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 16) & 0xFF)); - case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 8) & 0xFF)); - case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data >> 0) & 0xFF)); + case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 24) & 0xFF)); + case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 16) & 0xFF)); + case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 8) & 0xFF)); + case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data >> 0) & 0xFF)); } return crc; } #if 0 -static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac_uint32 data, drflac_uint32 count) +static MA_INLINE ma_uint16 ma_dr_flac_crc16__32bit(ma_uint16 crc, ma_uint32 data, ma_uint32 count) { -#ifdef DR_FLAC_NO_CRC +#ifdef MA_DR_FLAC_NO_CRC (void)crc; (void)data; (void)count; return 0; #else #if 0 - drflac_uint16 p = 0x8005; + ma_uint16 p = 0x8005; for (int i = count-1; i >= 0; --i) { - drflac_uint16 bit = (data & (1ULL << i)) >> i; + ma_uint16 bit = (data & (1ULL << i)) >> i; if (r & 0x8000) { r = ((r << 1) | bit) ^ p; } else { @@ -82398,433 +82570,433 @@ static DRFLAC_INLINE drflac_uint16 drflac_crc16__32bit(drflac_uint16 crc, drflac } return crc; #else - drflac_uint32 wholeBytes; - drflac_uint32 leftoverBits; - drflac_uint64 leftoverDataMask; - static drflac_uint64 leftoverDataMaskTable[8] = { + ma_uint32 wholeBytes; + ma_uint32 leftoverBits; + ma_uint64 leftoverDataMask; + static ma_uint64 leftoverDataMaskTable[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; - DRFLAC_ASSERT(count <= 64); + MA_DR_FLAC_ASSERT(count <= 64); wholeBytes = count >> 3; leftoverBits = count & 7; leftoverDataMask = leftoverDataMaskTable[leftoverBits]; switch (wholeBytes) { default: - case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; + case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0xFF000000UL << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x00FF0000UL << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x0000FF00UL << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (0x000000FFUL << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; } return crc; #endif #endif } -static DRFLAC_INLINE drflac_uint16 drflac_crc16__64bit(drflac_uint16 crc, drflac_uint64 data, drflac_uint32 count) +static MA_INLINE ma_uint16 ma_dr_flac_crc16__64bit(ma_uint16 crc, ma_uint64 data, ma_uint32 count) { -#ifdef DR_FLAC_NO_CRC +#ifdef MA_DR_FLAC_NO_CRC (void)crc; (void)data; (void)count; return 0; #else - drflac_uint32 wholeBytes; - drflac_uint32 leftoverBits; - drflac_uint64 leftoverDataMask; - static drflac_uint64 leftoverDataMaskTable[8] = { + ma_uint32 wholeBytes; + ma_uint32 leftoverBits; + ma_uint64 leftoverDataMask; + static ma_uint64 leftoverDataMaskTable[8] = { 0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F }; - DRFLAC_ASSERT(count <= 64); + MA_DR_FLAC_ASSERT(count <= 64); wholeBytes = count >> 3; leftoverBits = count & 7; leftoverDataMask = leftoverDataMaskTable[leftoverBits]; switch (wholeBytes) { default: - case 8: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits))); - case 7: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits))); - case 6: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits))); - case 5: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits))); - case 4: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits))); - case 3: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits))); - case 2: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits))); - case 1: crc = drflac_crc16_byte(crc, (drflac_uint8)((data & (((drflac_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits))); - case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ drflac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; + case 8: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 << 32) << leftoverBits)) >> (56 + leftoverBits))); + case 7: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 << 32) << leftoverBits)) >> (48 + leftoverBits))); + case 6: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 << 32) << leftoverBits)) >> (40 + leftoverBits))); + case 5: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF << 32) << leftoverBits)) >> (32 + leftoverBits))); + case 4: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0xFF000000 ) << leftoverBits)) >> (24 + leftoverBits))); + case 3: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x00FF0000 ) << leftoverBits)) >> (16 + leftoverBits))); + case 2: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x0000FF00 ) << leftoverBits)) >> ( 8 + leftoverBits))); + case 1: crc = ma_dr_flac_crc16_byte(crc, (ma_uint8)((data & (((ma_uint64)0x000000FF ) << leftoverBits)) >> ( 0 + leftoverBits))); + case 0: if (leftoverBits > 0) crc = (crc << leftoverBits) ^ ma_dr_flac__crc16_table[(crc >> (16 - leftoverBits)) ^ (data & leftoverDataMask)]; } return crc; #endif } -static DRFLAC_INLINE drflac_uint16 drflac_crc16(drflac_uint16 crc, drflac_cache_t data, drflac_uint32 count) +static MA_INLINE ma_uint16 ma_dr_flac_crc16(ma_uint16 crc, ma_dr_flac_cache_t data, ma_uint32 count) { -#ifdef DRFLAC_64BIT - return drflac_crc16__64bit(crc, data, count); +#ifdef MA_64BIT + return ma_dr_flac_crc16__64bit(crc, data, count); #else - return drflac_crc16__32bit(crc, data, count); + return ma_dr_flac_crc16__32bit(crc, data, count); #endif } #endif -#ifdef DRFLAC_64BIT -#define drflac__be2host__cache_line drflac__be2host_64 +#ifdef MA_64BIT +#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_64 #else -#define drflac__be2host__cache_line drflac__be2host_32 +#define ma_dr_flac__be2host__cache_line ma_dr_flac__be2host_32 #endif -#define DRFLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) -#define DRFLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) -#define DRFLAC_CACHE_L1_BITS_REMAINING(bs) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits) -#define DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(drflac_cache_t)0) >> (_bitCount))) -#define DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) -#define DRFLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & DRFLAC_CACHE_L1_SELECTION_MASK(_bitCount)) -#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount))) -#define DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(DRFLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (DRFLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1))) -#define DRFLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) -#define DRFLAC_CACHE_L2_LINE_COUNT(bs) (DRFLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) -#define DRFLAC_CACHE_L2_LINES_REMAINING(bs) (DRFLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) -#ifndef DR_FLAC_NO_CRC -static DRFLAC_INLINE void drflac__reset_crc16(drflac_bs* bs) +#define MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) (sizeof((bs)->cache)) +#define MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) (sizeof((bs)->cache)*8) +#define MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (bs)->consumedBits) +#define MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount) (~((~(ma_dr_flac_cache_t)0) >> (_bitCount))) +#define MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - (_bitCount)) +#define MA_DR_FLAC_CACHE_L1_SELECT(bs, _bitCount) (((bs)->cache) & MA_DR_FLAC_CACHE_L1_SELECTION_MASK(_bitCount)) +#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, _bitCount) (MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount))) +#define MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, _bitCount)(MA_DR_FLAC_CACHE_L1_SELECT((bs), (_bitCount)) >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT((bs), (_bitCount)) & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1))) +#define MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) (sizeof((bs)->cacheL2)) +#define MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) (MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs) / sizeof((bs)->cacheL2[0])) +#define MA_DR_FLAC_CACHE_L2_LINES_REMAINING(bs) (MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - (bs)->nextL2Line) +#ifndef MA_DR_FLAC_NO_CRC +static MA_INLINE void ma_dr_flac__reset_crc16(ma_dr_flac_bs* bs) { bs->crc16 = 0; bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; } -static DRFLAC_INLINE void drflac__update_crc16(drflac_bs* bs) +static MA_INLINE void ma_dr_flac__update_crc16(ma_dr_flac_bs* bs) { if (bs->crc16CacheIgnoredBytes == 0) { - bs->crc16 = drflac_crc16_cache(bs->crc16, bs->crc16Cache); + bs->crc16 = ma_dr_flac_crc16_cache(bs->crc16, bs->crc16Cache); } else { - bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache, DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); + bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache, MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bs->crc16CacheIgnoredBytes); bs->crc16CacheIgnoredBytes = 0; } } -static DRFLAC_INLINE drflac_uint16 drflac__flush_crc16(drflac_bs* bs) +static MA_INLINE ma_uint16 ma_dr_flac__flush_crc16(ma_dr_flac_bs* bs) { - DRFLAC_ASSERT((DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); - if (DRFLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { - drflac__update_crc16(bs); + MA_DR_FLAC_ASSERT((MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7) == 0); + if (MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) == 0) { + ma_dr_flac__update_crc16(bs); } else { - bs->crc16 = drflac_crc16_bytes(bs->crc16, bs->crc16Cache >> DRFLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); + bs->crc16 = ma_dr_flac_crc16_bytes(bs->crc16, bs->crc16Cache >> MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs), (bs->consumedBits >> 3) - bs->crc16CacheIgnoredBytes); bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; } return bs->crc16; } #endif -static DRFLAC_INLINE drflac_bool32 drflac__reload_l1_cache_from_l2(drflac_bs* bs) +static MA_INLINE ma_bool32 ma_dr_flac__reload_l1_cache_from_l2(ma_dr_flac_bs* bs) { size_t bytesRead; size_t alignedL1LineCount; - if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { bs->cache = bs->cacheL2[bs->nextL2Line++]; - return DRFLAC_TRUE; + return MA_TRUE; } if (bs->unalignedByteCount > 0) { - return DRFLAC_FALSE; + return MA_FALSE; } - bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, DRFLAC_CACHE_L2_SIZE_BYTES(bs)); + bytesRead = bs->onRead(bs->pUserData, bs->cacheL2, MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)); bs->nextL2Line = 0; - if (bytesRead == DRFLAC_CACHE_L2_SIZE_BYTES(bs)) { + if (bytesRead == MA_DR_FLAC_CACHE_L2_SIZE_BYTES(bs)) { bs->cache = bs->cacheL2[bs->nextL2Line++]; - return DRFLAC_TRUE; + return MA_TRUE; } - alignedL1LineCount = bytesRead / DRFLAC_CACHE_L1_SIZE_BYTES(bs); - bs->unalignedByteCount = bytesRead - (alignedL1LineCount * DRFLAC_CACHE_L1_SIZE_BYTES(bs)); + alignedL1LineCount = bytesRead / MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs); + bs->unalignedByteCount = bytesRead - (alignedL1LineCount * MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs)); if (bs->unalignedByteCount > 0) { bs->unalignedCache = bs->cacheL2[alignedL1LineCount]; } if (alignedL1LineCount > 0) { - size_t offset = DRFLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; + size_t offset = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs) - alignedL1LineCount; size_t i; for (i = alignedL1LineCount; i > 0; --i) { bs->cacheL2[i-1 + offset] = bs->cacheL2[i-1]; } - bs->nextL2Line = (drflac_uint32)offset; + bs->nextL2Line = (ma_uint32)offset; bs->cache = bs->cacheL2[bs->nextL2Line++]; - return DRFLAC_TRUE; + return MA_TRUE; } else { - bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); - return DRFLAC_FALSE; + bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs); + return MA_FALSE; } } -static drflac_bool32 drflac__reload_cache(drflac_bs* bs) +static ma_bool32 ma_dr_flac__reload_cache(ma_dr_flac_bs* bs) { size_t bytesRead; -#ifndef DR_FLAC_NO_CRC - drflac__update_crc16(bs); +#ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); #endif - if (drflac__reload_l1_cache_from_l2(bs)) { - bs->cache = drflac__be2host__cache_line(bs->cache); + if (ma_dr_flac__reload_l1_cache_from_l2(bs)) { + bs->cache = ma_dr_flac__be2host__cache_line(bs->cache); bs->consumedBits = 0; -#ifndef DR_FLAC_NO_CRC +#ifndef MA_DR_FLAC_NO_CRC bs->crc16Cache = bs->cache; #endif - return DRFLAC_TRUE; + return MA_TRUE; } bytesRead = bs->unalignedByteCount; if (bytesRead == 0) { - bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); - return DRFLAC_FALSE; + bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); + return MA_FALSE; } - DRFLAC_ASSERT(bytesRead < DRFLAC_CACHE_L1_SIZE_BYTES(bs)); - bs->consumedBits = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; - bs->cache = drflac__be2host__cache_line(bs->unalignedCache); - bs->cache &= DRFLAC_CACHE_L1_SELECTION_MASK(DRFLAC_CACHE_L1_BITS_REMAINING(bs)); + MA_DR_FLAC_ASSERT(bytesRead < MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs)); + bs->consumedBits = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BYTES(bs) - bytesRead) * 8; + bs->cache = ma_dr_flac__be2host__cache_line(bs->unalignedCache); + bs->cache &= MA_DR_FLAC_CACHE_L1_SELECTION_MASK(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)); bs->unalignedByteCount = 0; -#ifndef DR_FLAC_NO_CRC +#ifndef MA_DR_FLAC_NO_CRC bs->crc16Cache = bs->cache >> bs->consumedBits; bs->crc16CacheIgnoredBytes = bs->consumedBits >> 3; #endif - return DRFLAC_TRUE; + return MA_TRUE; } -static void drflac__reset_cache(drflac_bs* bs) +static void ma_dr_flac__reset_cache(ma_dr_flac_bs* bs) { - bs->nextL2Line = DRFLAC_CACHE_L2_LINE_COUNT(bs); - bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); + bs->nextL2Line = MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs); + bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); bs->cache = 0; bs->unalignedByteCount = 0; bs->unalignedCache = 0; -#ifndef DR_FLAC_NO_CRC +#ifndef MA_DR_FLAC_NO_CRC bs->crc16Cache = 0; bs->crc16CacheIgnoredBytes = 0; #endif } -static DRFLAC_INLINE drflac_bool32 drflac__read_uint32(drflac_bs* bs, unsigned int bitCount, drflac_uint32* pResultOut) +static MA_INLINE ma_bool32 ma_dr_flac__read_uint32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint32* pResultOut) { - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pResultOut != NULL); - DRFLAC_ASSERT(bitCount > 0); - DRFLAC_ASSERT(bitCount <= 32); - if (bs->consumedBits == DRFLAC_CACHE_L1_SIZE_BITS(bs)) { - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResultOut != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 32); + if (bs->consumedBits == MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; } } - if (bitCount <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { -#ifdef DRFLAC_64BIT - *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); + if (bitCount <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { +#ifdef MA_64BIT + *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); bs->consumedBits += bitCount; bs->cache <<= bitCount; #else - if (bitCount < DRFLAC_CACHE_L1_SIZE_BITS(bs)) { - *pResultOut = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); + if (bitCount < MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { + *pResultOut = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCount); bs->consumedBits += bitCount; bs->cache <<= bitCount; } else { - *pResultOut = (drflac_uint32)bs->cache; - bs->consumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs); + *pResultOut = (ma_uint32)bs->cache; + bs->consumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); bs->cache = 0; } #endif - return DRFLAC_TRUE; + return MA_TRUE; } else { - drflac_uint32 bitCountHi = DRFLAC_CACHE_L1_BITS_REMAINING(bs); - drflac_uint32 bitCountLo = bitCount - bitCountHi; - drflac_uint32 resultHi; - DRFLAC_ASSERT(bitCountHi > 0); - DRFLAC_ASSERT(bitCountHi < 32); - resultHi = (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; + ma_uint32 bitCountHi = MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); + ma_uint32 bitCountLo = bitCount - bitCountHi; + ma_uint32 resultHi; + MA_DR_FLAC_ASSERT(bitCountHi > 0); + MA_DR_FLAC_ASSERT(bitCountHi < 32); + resultHi = (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountHi); + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; } - if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - return DRFLAC_FALSE; + if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + return MA_FALSE; } - *pResultOut = (resultHi << bitCountLo) | (drflac_uint32)DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); + *pResultOut = (resultHi << bitCountLo) | (ma_uint32)MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, bitCountLo); bs->consumedBits += bitCountLo; bs->cache <<= bitCountLo; - return DRFLAC_TRUE; + return MA_TRUE; } } -static drflac_bool32 drflac__read_int32(drflac_bs* bs, unsigned int bitCount, drflac_int32* pResult) +static ma_bool32 ma_dr_flac__read_int32(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int32* pResult) { - drflac_uint32 result; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pResult != NULL); - DRFLAC_ASSERT(bitCount > 0); - DRFLAC_ASSERT(bitCount <= 32); - if (!drflac__read_uint32(bs, bitCount, &result)) { - return DRFLAC_FALSE; + ma_uint32 result; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResult != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 32); + if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { + return MA_FALSE; } if (bitCount < 32) { - drflac_uint32 signbit; + ma_uint32 signbit; signbit = ((result >> (bitCount-1)) & 0x01); result |= (~signbit + 1) << bitCount; } - *pResult = (drflac_int32)result; - return DRFLAC_TRUE; + *pResult = (ma_int32)result; + return MA_TRUE; } -#ifdef DRFLAC_64BIT -static drflac_bool32 drflac__read_uint64(drflac_bs* bs, unsigned int bitCount, drflac_uint64* pResultOut) +#ifdef MA_64BIT +static ma_bool32 ma_dr_flac__read_uint64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint64* pResultOut) { - drflac_uint32 resultHi; - drflac_uint32 resultLo; - DRFLAC_ASSERT(bitCount <= 64); - DRFLAC_ASSERT(bitCount > 32); - if (!drflac__read_uint32(bs, bitCount - 32, &resultHi)) { - return DRFLAC_FALSE; + ma_uint32 resultHi; + ma_uint32 resultLo; + MA_DR_FLAC_ASSERT(bitCount <= 64); + MA_DR_FLAC_ASSERT(bitCount > 32); + if (!ma_dr_flac__read_uint32(bs, bitCount - 32, &resultHi)) { + return MA_FALSE; } - if (!drflac__read_uint32(bs, 32, &resultLo)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint32(bs, 32, &resultLo)) { + return MA_FALSE; } - *pResultOut = (((drflac_uint64)resultHi) << 32) | ((drflac_uint64)resultLo); - return DRFLAC_TRUE; + *pResultOut = (((ma_uint64)resultHi) << 32) | ((ma_uint64)resultLo); + return MA_TRUE; } #endif #if 0 -static drflac_bool32 drflac__read_int64(drflac_bs* bs, unsigned int bitCount, drflac_int64* pResultOut) +static ma_bool32 ma_dr_flac__read_int64(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int64* pResultOut) { - drflac_uint64 result; - drflac_uint64 signbit; - DRFLAC_ASSERT(bitCount <= 64); - if (!drflac__read_uint64(bs, bitCount, &result)) { - return DRFLAC_FALSE; + ma_uint64 result; + ma_uint64 signbit; + MA_DR_FLAC_ASSERT(bitCount <= 64); + if (!ma_dr_flac__read_uint64(bs, bitCount, &result)) { + return MA_FALSE; } signbit = ((result >> (bitCount-1)) & 0x01); result |= (~signbit + 1) << bitCount; - *pResultOut = (drflac_int64)result; - return DRFLAC_TRUE; + *pResultOut = (ma_int64)result; + return MA_TRUE; } #endif -static drflac_bool32 drflac__read_uint16(drflac_bs* bs, unsigned int bitCount, drflac_uint16* pResult) +static ma_bool32 ma_dr_flac__read_uint16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint16* pResult) { - drflac_uint32 result; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pResult != NULL); - DRFLAC_ASSERT(bitCount > 0); - DRFLAC_ASSERT(bitCount <= 16); - if (!drflac__read_uint32(bs, bitCount, &result)) { - return DRFLAC_FALSE; + ma_uint32 result; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResult != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 16); + if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { + return MA_FALSE; } - *pResult = (drflac_uint16)result; - return DRFLAC_TRUE; + *pResult = (ma_uint16)result; + return MA_TRUE; } #if 0 -static drflac_bool32 drflac__read_int16(drflac_bs* bs, unsigned int bitCount, drflac_int16* pResult) +static ma_bool32 ma_dr_flac__read_int16(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int16* pResult) { - drflac_int32 result; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pResult != NULL); - DRFLAC_ASSERT(bitCount > 0); - DRFLAC_ASSERT(bitCount <= 16); - if (!drflac__read_int32(bs, bitCount, &result)) { - return DRFLAC_FALSE; + ma_int32 result; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResult != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 16); + if (!ma_dr_flac__read_int32(bs, bitCount, &result)) { + return MA_FALSE; } - *pResult = (drflac_int16)result; - return DRFLAC_TRUE; + *pResult = (ma_int16)result; + return MA_TRUE; } #endif -static drflac_bool32 drflac__read_uint8(drflac_bs* bs, unsigned int bitCount, drflac_uint8* pResult) +static ma_bool32 ma_dr_flac__read_uint8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_uint8* pResult) { - drflac_uint32 result; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pResult != NULL); - DRFLAC_ASSERT(bitCount > 0); - DRFLAC_ASSERT(bitCount <= 8); - if (!drflac__read_uint32(bs, bitCount, &result)) { - return DRFLAC_FALSE; + ma_uint32 result; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResult != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 8); + if (!ma_dr_flac__read_uint32(bs, bitCount, &result)) { + return MA_FALSE; } - *pResult = (drflac_uint8)result; - return DRFLAC_TRUE; + *pResult = (ma_uint8)result; + return MA_TRUE; } -static drflac_bool32 drflac__read_int8(drflac_bs* bs, unsigned int bitCount, drflac_int8* pResult) +static ma_bool32 ma_dr_flac__read_int8(ma_dr_flac_bs* bs, unsigned int bitCount, ma_int8* pResult) { - drflac_int32 result; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pResult != NULL); - DRFLAC_ASSERT(bitCount > 0); - DRFLAC_ASSERT(bitCount <= 8); - if (!drflac__read_int32(bs, bitCount, &result)) { - return DRFLAC_FALSE; + ma_int32 result; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pResult != NULL); + MA_DR_FLAC_ASSERT(bitCount > 0); + MA_DR_FLAC_ASSERT(bitCount <= 8); + if (!ma_dr_flac__read_int32(bs, bitCount, &result)) { + return MA_FALSE; } - *pResult = (drflac_int8)result; - return DRFLAC_TRUE; + *pResult = (ma_int8)result; + return MA_TRUE; } -static drflac_bool32 drflac__seek_bits(drflac_bs* bs, size_t bitsToSeek) +static ma_bool32 ma_dr_flac__seek_bits(ma_dr_flac_bs* bs, size_t bitsToSeek) { - if (bitsToSeek <= DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - bs->consumedBits += (drflac_uint32)bitsToSeek; + if (bitsToSeek <= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + bs->consumedBits += (ma_uint32)bitsToSeek; bs->cache <<= bitsToSeek; - return DRFLAC_TRUE; + return MA_TRUE; } else { - bitsToSeek -= DRFLAC_CACHE_L1_BITS_REMAINING(bs); - bs->consumedBits += DRFLAC_CACHE_L1_BITS_REMAINING(bs); + bitsToSeek -= MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); + bs->consumedBits += MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); bs->cache = 0; -#ifdef DRFLAC_64BIT - while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { - drflac_uint64 bin; - if (!drflac__read_uint64(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { - return DRFLAC_FALSE; +#ifdef MA_64BIT + while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { + ma_uint64 bin; + if (!ma_dr_flac__read_uint64(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { + return MA_FALSE; } - bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); + bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); } #else - while (bitsToSeek >= DRFLAC_CACHE_L1_SIZE_BITS(bs)) { - drflac_uint32 bin; - if (!drflac__read_uint32(bs, DRFLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { - return DRFLAC_FALSE; + while (bitsToSeek >= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)) { + ma_uint32 bin; + if (!ma_dr_flac__read_uint32(bs, MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs), &bin)) { + return MA_FALSE; } - bitsToSeek -= DRFLAC_CACHE_L1_SIZE_BITS(bs); + bitsToSeek -= MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); } #endif while (bitsToSeek >= 8) { - drflac_uint8 bin; - if (!drflac__read_uint8(bs, 8, &bin)) { - return DRFLAC_FALSE; + ma_uint8 bin; + if (!ma_dr_flac__read_uint8(bs, 8, &bin)) { + return MA_FALSE; } bitsToSeek -= 8; } if (bitsToSeek > 0) { - drflac_uint8 bin; - if (!drflac__read_uint8(bs, (drflac_uint32)bitsToSeek, &bin)) { - return DRFLAC_FALSE; + ma_uint8 bin; + if (!ma_dr_flac__read_uint8(bs, (ma_uint32)bitsToSeek, &bin)) { + return MA_FALSE; } bitsToSeek = 0; } - DRFLAC_ASSERT(bitsToSeek == 0); - return DRFLAC_TRUE; + MA_DR_FLAC_ASSERT(bitsToSeek == 0); + return MA_TRUE; } } -static drflac_bool32 drflac__find_and_seek_to_next_sync_code(drflac_bs* bs) +static ma_bool32 ma_dr_flac__find_and_seek_to_next_sync_code(ma_dr_flac_bs* bs) { - DRFLAC_ASSERT(bs != NULL); - if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { - return DRFLAC_FALSE; + MA_DR_FLAC_ASSERT(bs != NULL); + if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { + return MA_FALSE; } for (;;) { - drflac_uint8 hi; -#ifndef DR_FLAC_NO_CRC - drflac__reset_crc16(bs); + ma_uint8 hi; +#ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__reset_crc16(bs); #endif - if (!drflac__read_uint8(bs, 8, &hi)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint8(bs, 8, &hi)) { + return MA_FALSE; } if (hi == 0xFF) { - drflac_uint8 lo; - if (!drflac__read_uint8(bs, 6, &lo)) { - return DRFLAC_FALSE; + ma_uint8 lo; + if (!ma_dr_flac__read_uint8(bs, 6, &lo)) { + return MA_FALSE; } if (lo == 0x3E) { - return DRFLAC_TRUE; + return MA_TRUE; } else { - if (!drflac__seek_bits(bs, DRFLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_bits(bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) & 7)) { + return MA_FALSE; } } } } } -#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) -#define DRFLAC_IMPLEMENT_CLZ_LZCNT +#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) +#define MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT #endif -#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(DRFLAC_X64) || defined(DRFLAC_X86)) && !defined(__clang__) -#define DRFLAC_IMPLEMENT_CLZ_MSVC +#if defined(_MSC_VER) && _MSC_VER >= 1400 && (defined(MA_X64) || defined(MA_X86)) && !defined(__clang__) +#define MA_DR_FLAC_IMPLEMENT_CLZ_MSVC #endif #if defined(__WATCOMC__) && defined(__386__) -#define DRFLAC_IMPLEMENT_CLZ_WATCOM +#define MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM #endif #ifdef __MRC__ #include -#define DRFLAC_IMPLEMENT_CLZ_MRC +#define MA_DR_FLAC_IMPLEMENT_CLZ_MRC #endif -static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) +static MA_INLINE ma_uint32 ma_dr_flac__clz_software(ma_dr_flac_cache_t x) { - drflac_uint32 n; - static drflac_uint32 clz_table_4[] = { + ma_uint32 n; + static ma_uint32 clz_table_4[] = { 0, 4, 3, 3, @@ -82836,11 +83008,11 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) } n = clz_table_4[x >> (sizeof(x)*8 - 4)]; if (n == 0) { -#ifdef DRFLAC_64BIT - if ((x & ((drflac_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; } - if ((x & ((drflac_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; } - if ((x & ((drflac_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; } - if ((x & ((drflac_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; } +#ifdef MA_64BIT + if ((x & ((ma_uint64)0xFFFFFFFF << 32)) == 0) { n = 32; x <<= 32; } + if ((x & ((ma_uint64)0xFFFF0000 << 32)) == 0) { n += 16; x <<= 16; } + if ((x & ((ma_uint64)0xFF000000 << 32)) == 0) { n += 8; x <<= 8; } + if ((x & ((ma_uint64)0xF0000000 << 32)) == 0) { n += 4; x <<= 4; } #else if ((x & 0xFFFF0000) == 0) { n = 16; x <<= 16; } if ((x & 0xFF000000) == 0) { n += 8; x <<= 8; } @@ -82850,52 +83022,52 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_software(drflac_cache_t x) } return n - 1; } -#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT -static DRFLAC_INLINE drflac_bool32 drflac__is_lzcnt_supported(void) +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT +static MA_INLINE ma_bool32 ma_dr_flac__is_lzcnt_supported(void) { -#if defined(DRFLAC_HAS_LZCNT_INTRINSIC) && defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) - return DRFLAC_TRUE; +#if defined(MA_DR_FLAC_HAS_LZCNT_INTRINSIC) && defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) + return MA_TRUE; #elif defined(__MRC__) - return DRFLAC_TRUE; + return MA_TRUE; #else - #ifdef DRFLAC_HAS_LZCNT_INTRINSIC - return drflac__gIsLZCNTSupported; + #ifdef MA_DR_FLAC_HAS_LZCNT_INTRINSIC + return ma_dr_flac__gIsLZCNTSupported; #else - return DRFLAC_FALSE; + return MA_FALSE; #endif #endif } -static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) +static MA_INLINE ma_uint32 ma_dr_flac__clz_lzcnt(ma_dr_flac_cache_t x) { #if defined(_MSC_VER) - #ifdef DRFLAC_64BIT - return (drflac_uint32)__lzcnt64(x); + #ifdef MA_64BIT + return (ma_uint32)__lzcnt64(x); #else - return (drflac_uint32)__lzcnt(x); + return (ma_uint32)__lzcnt(x); #endif #else #if defined(__GNUC__) || defined(__clang__) - #if defined(DRFLAC_X64) + #if defined(MA_X64) { - drflac_uint64 r; + ma_uint64 r; __asm__ __volatile__ ( "lzcnt{ %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" ); - return (drflac_uint32)r; + return (ma_uint32)r; } - #elif defined(DRFLAC_X86) + #elif defined(MA_X86) { - drflac_uint32 r; + ma_uint32 r; __asm__ __volatile__ ( "lzcnt{l %1, %0| %0, %1}" : "=r"(r) : "r"(x) : "cc" ); return r; } - #elif defined(DRFLAC_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(DRFLAC_64BIT) + #elif defined(MA_ARM) && (defined(__ARM_ARCH) && __ARM_ARCH >= 5) && !defined(MA_64BIT) { unsigned int r; __asm__ __volatile__ ( - #if defined(DRFLAC_64BIT) + #if defined(MA_64BIT) "clz %w[out], %w[in]" : [out]"=r"(r) : [in]"r"(x) #else "clz %[out], %[in]" : [out]"=r"(r) : [in]"r"(x) @@ -82907,10 +83079,10 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) if (x == 0) { return sizeof(x)*8; } - #ifdef DRFLAC_64BIT - return (drflac_uint32)__builtin_clzll((drflac_uint64)x); + #ifdef MA_64BIT + return (ma_uint32)__builtin_clzll((ma_uint64)x); #else - return (drflac_uint32)__builtin_clzl((drflac_uint32)x); + return (ma_uint32)__builtin_clzl((ma_uint32)x); #endif #endif #else @@ -82919,15 +83091,15 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_lzcnt(drflac_cache_t x) #endif } #endif -#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC #include -static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x) +static MA_INLINE ma_uint32 ma_dr_flac__clz_msvc(ma_dr_flac_cache_t x) { - drflac_uint32 n; + ma_uint32 n; if (x == 0) { return sizeof(x)*8; } -#ifdef DRFLAC_64BIT +#ifdef MA_64BIT _BitScanReverse64((unsigned long*)&n, x); #else _BitScanReverse((unsigned long*)&n, x); @@ -82935,16 +83107,16 @@ static DRFLAC_INLINE drflac_uint32 drflac__clz_msvc(drflac_cache_t x) return sizeof(x)*8 - n - 1; } #endif -#ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM -static __inline drflac_uint32 drflac__clz_watcom (drflac_uint32); -#ifdef DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT -#pragma aux drflac__clz_watcom_lzcnt = \ +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM +static __inline ma_uint32 ma_dr_flac__clz_watcom (ma_uint32); +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT +#pragma aux ma_dr_flac__clz_watcom_lzcnt = \ "db 0F3h, 0Fh, 0BDh, 0C0h" \ parm [eax] \ value [eax] \ modify nomemory; #else -#pragma aux drflac__clz_watcom = \ +#pragma aux ma_dr_flac__clz_watcom = \ "bsr eax, eax" \ "xor eax, 31" \ parm [eax] nomemory \ @@ -82952,103 +83124,103 @@ static __inline drflac_uint32 drflac__clz_watcom (drflac_uint32); modify exact [eax] nomemory; #endif #endif -static DRFLAC_INLINE drflac_uint32 drflac__clz(drflac_cache_t x) +static MA_INLINE ma_uint32 ma_dr_flac__clz(ma_dr_flac_cache_t x) { -#ifdef DRFLAC_IMPLEMENT_CLZ_LZCNT - if (drflac__is_lzcnt_supported()) { - return drflac__clz_lzcnt(x); +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_LZCNT + if (ma_dr_flac__is_lzcnt_supported()) { + return ma_dr_flac__clz_lzcnt(x); } else #endif { -#ifdef DRFLAC_IMPLEMENT_CLZ_MSVC - return drflac__clz_msvc(x); -#elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM_LZCNT) - return drflac__clz_watcom_lzcnt(x); -#elif defined(DRFLAC_IMPLEMENT_CLZ_WATCOM) - return (x == 0) ? sizeof(x)*8 : drflac__clz_watcom(x); +#ifdef MA_DR_FLAC_IMPLEMENT_CLZ_MSVC + return ma_dr_flac__clz_msvc(x); +#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM_LZCNT) + return ma_dr_flac__clz_watcom_lzcnt(x); +#elif defined(MA_DR_FLAC_IMPLEMENT_CLZ_WATCOM) + return (x == 0) ? sizeof(x)*8 : ma_dr_flac__clz_watcom(x); #elif defined(__MRC__) return __cntlzw(x); #else - return drflac__clz_software(x); + return ma_dr_flac__clz_software(x); #endif } } -static DRFLAC_INLINE drflac_bool32 drflac__seek_past_next_set_bit(drflac_bs* bs, unsigned int* pOffsetOut) +static MA_INLINE ma_bool32 ma_dr_flac__seek_past_next_set_bit(ma_dr_flac_bs* bs, unsigned int* pOffsetOut) { - drflac_uint32 zeroCounter = 0; - drflac_uint32 setBitOffsetPlus1; + ma_uint32 zeroCounter = 0; + ma_uint32 setBitOffsetPlus1; while (bs->cache == 0) { - zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; + zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; } } if (bs->cache == 1) { - *pOffsetOut = zeroCounter + (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs) - 1; - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; + *pOffsetOut = zeroCounter + (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs) - 1; + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; } - return DRFLAC_TRUE; + return MA_TRUE; } - setBitOffsetPlus1 = drflac__clz(bs->cache); + setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache); setBitOffsetPlus1 += 1; - if (setBitOffsetPlus1 > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - return DRFLAC_FALSE; + if (setBitOffsetPlus1 > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + return MA_FALSE; } bs->consumedBits += setBitOffsetPlus1; bs->cache <<= setBitOffsetPlus1; *pOffsetOut = zeroCounter + setBitOffsetPlus1 - 1; - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__seek_to_byte(drflac_bs* bs, drflac_uint64 offsetFromStart) +static ma_bool32 ma_dr_flac__seek_to_byte(ma_dr_flac_bs* bs, ma_uint64 offsetFromStart) { - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(offsetFromStart > 0); + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(offsetFromStart > 0); if (offsetFromStart > 0x7FFFFFFF) { - drflac_uint64 bytesRemaining = offsetFromStart; - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { - return DRFLAC_FALSE; + ma_uint64 bytesRemaining = offsetFromStart; + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; } bytesRemaining -= 0x7FFFFFFF; while (bytesRemaining > 0x7FFFFFFF) { - if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { - return DRFLAC_FALSE; + if (!bs->onSeek(bs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; } bytesRemaining -= 0x7FFFFFFF; } if (bytesRemaining > 0) { - if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, drflac_seek_origin_current)) { - return DRFLAC_FALSE; + if (!bs->onSeek(bs->pUserData, (int)bytesRemaining, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; } } } else { - if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, drflac_seek_origin_start)) { - return DRFLAC_FALSE; + if (!bs->onSeek(bs->pUserData, (int)offsetFromStart, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; } } - drflac__reset_cache(bs); - return DRFLAC_TRUE; + ma_dr_flac__reset_cache(bs); + return MA_TRUE; } -static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64* pNumberOut, drflac_uint8* pCRCOut) +static ma_result ma_dr_flac__read_utf8_coded_number(ma_dr_flac_bs* bs, ma_uint64* pNumberOut, ma_uint8* pCRCOut) { - drflac_uint8 crc; - drflac_uint64 result; - drflac_uint8 utf8[7] = {0}; + ma_uint8 crc; + ma_uint64 result; + ma_uint8 utf8[7] = {0}; int byteCount; int i; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pNumberOut != NULL); - DRFLAC_ASSERT(pCRCOut != NULL); + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pNumberOut != NULL); + MA_DR_FLAC_ASSERT(pCRCOut != NULL); crc = *pCRCOut; - if (!drflac__read_uint8(bs, 8, utf8)) { + if (!ma_dr_flac__read_uint8(bs, 8, utf8)) { *pNumberOut = 0; - return DRFLAC_AT_END; + return MA_AT_END; } - crc = drflac_crc8(crc, utf8[0], 8); + crc = ma_dr_flac_crc8(crc, utf8[0], 8); if ((utf8[0] & 0x80) == 0) { *pNumberOut = utf8[0]; *pCRCOut = crc; - return DRFLAC_SUCCESS; + return MA_SUCCESS; } if ((utf8[0] & 0xE0) == 0xC0) { byteCount = 2; @@ -83064,26 +83236,26 @@ static drflac_result drflac__read_utf8_coded_number(drflac_bs* bs, drflac_uint64 byteCount = 7; } else { *pNumberOut = 0; - return DRFLAC_CRC_MISMATCH; + return MA_CRC_MISMATCH; } - DRFLAC_ASSERT(byteCount > 1); - result = (drflac_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); + MA_DR_FLAC_ASSERT(byteCount > 1); + result = (ma_uint64)(utf8[0] & (0xFF >> (byteCount + 1))); for (i = 1; i < byteCount; ++i) { - if (!drflac__read_uint8(bs, 8, utf8 + i)) { + if (!ma_dr_flac__read_uint8(bs, 8, utf8 + i)) { *pNumberOut = 0; - return DRFLAC_AT_END; + return MA_AT_END; } - crc = drflac_crc8(crc, utf8[i], 8); + crc = ma_dr_flac_crc8(crc, utf8[i], 8); result = (result << 6) | (utf8[i] & 0x3F); } *pNumberOut = result; *pCRCOut = crc; - return DRFLAC_SUCCESS; + return MA_SUCCESS; } -static DRFLAC_INLINE drflac_uint32 drflac__ilog2_u32(drflac_uint32 x) +static MA_INLINE ma_uint32 ma_dr_flac__ilog2_u32(ma_uint32 x) { #if 1 - drflac_uint32 result = 0; + ma_uint32 result = 0; while (x > 0) { result += 1; x >>= 1; @@ -83091,17 +83263,17 @@ static DRFLAC_INLINE drflac_uint32 drflac__ilog2_u32(drflac_uint32 x) return result; #endif } -static DRFLAC_INLINE drflac_bool32 drflac__use_64_bit_prediction(drflac_uint32 bitsPerSample, drflac_uint32 order, drflac_uint32 precision) +static MA_INLINE ma_bool32 ma_dr_flac__use_64_bit_prediction(ma_uint32 bitsPerSample, ma_uint32 order, ma_uint32 precision) { - return bitsPerSample + precision + drflac__ilog2_u32(order) > 32; + return bitsPerSample + precision + ma_dr_flac__ilog2_u32(order) > 32; } #if defined(__clang__) __attribute__((no_sanitize("signed-integer-overflow"))) #endif -static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) +static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_32(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples) { - drflac_int32 prediction = 0; - DRFLAC_ASSERT(order <= 32); + ma_int32 prediction = 0; + MA_DR_FLAC_ASSERT(order <= 32); switch (order) { case 32: prediction += coefficients[31] * pDecodedSamples[-32]; @@ -83137,188 +83309,188 @@ static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_32(drflac_uint32 case 2: prediction += coefficients[ 1] * pDecodedSamples[- 2]; case 1: prediction += coefficients[ 0] * pDecodedSamples[- 1]; } - return (drflac_int32)(prediction >> shift); + return (ma_int32)(prediction >> shift); } -static DRFLAC_INLINE drflac_int32 drflac__calculate_prediction_64(drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) +static MA_INLINE ma_int32 ma_dr_flac__calculate_prediction_64(ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pDecodedSamples) { - drflac_int64 prediction; - DRFLAC_ASSERT(order <= 32); -#ifndef DRFLAC_64BIT + ma_int64 prediction; + MA_DR_FLAC_ASSERT(order <= 32); +#ifndef MA_64BIT if (order == 8) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; } else if (order == 7) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; } else if (order == 3) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; } else if (order == 6) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; } else if (order == 5) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; } else if (order == 4) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; } else if (order == 12) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; - prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; - prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; + prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; + prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12]; } else if (order == 2) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; } else if (order == 1) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; } else if (order == 10) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; } else if (order == 9) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; } else if (order == 11) { - prediction = coefficients[0] * (drflac_int64)pDecodedSamples[-1]; - prediction += coefficients[1] * (drflac_int64)pDecodedSamples[-2]; - prediction += coefficients[2] * (drflac_int64)pDecodedSamples[-3]; - prediction += coefficients[3] * (drflac_int64)pDecodedSamples[-4]; - prediction += coefficients[4] * (drflac_int64)pDecodedSamples[-5]; - prediction += coefficients[5] * (drflac_int64)pDecodedSamples[-6]; - prediction += coefficients[6] * (drflac_int64)pDecodedSamples[-7]; - prediction += coefficients[7] * (drflac_int64)pDecodedSamples[-8]; - prediction += coefficients[8] * (drflac_int64)pDecodedSamples[-9]; - prediction += coefficients[9] * (drflac_int64)pDecodedSamples[-10]; - prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; + prediction = coefficients[0] * (ma_int64)pDecodedSamples[-1]; + prediction += coefficients[1] * (ma_int64)pDecodedSamples[-2]; + prediction += coefficients[2] * (ma_int64)pDecodedSamples[-3]; + prediction += coefficients[3] * (ma_int64)pDecodedSamples[-4]; + prediction += coefficients[4] * (ma_int64)pDecodedSamples[-5]; + prediction += coefficients[5] * (ma_int64)pDecodedSamples[-6]; + prediction += coefficients[6] * (ma_int64)pDecodedSamples[-7]; + prediction += coefficients[7] * (ma_int64)pDecodedSamples[-8]; + prediction += coefficients[8] * (ma_int64)pDecodedSamples[-9]; + prediction += coefficients[9] * (ma_int64)pDecodedSamples[-10]; + prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; } else { int j; prediction = 0; for (j = 0; j < (int)order; ++j) { - prediction += coefficients[j] * (drflac_int64)pDecodedSamples[-j-1]; + prediction += coefficients[j] * (ma_int64)pDecodedSamples[-j-1]; } } #endif -#ifdef DRFLAC_64BIT +#ifdef MA_64BIT prediction = 0; switch (order) { - case 32: prediction += coefficients[31] * (drflac_int64)pDecodedSamples[-32]; - case 31: prediction += coefficients[30] * (drflac_int64)pDecodedSamples[-31]; - case 30: prediction += coefficients[29] * (drflac_int64)pDecodedSamples[-30]; - case 29: prediction += coefficients[28] * (drflac_int64)pDecodedSamples[-29]; - case 28: prediction += coefficients[27] * (drflac_int64)pDecodedSamples[-28]; - case 27: prediction += coefficients[26] * (drflac_int64)pDecodedSamples[-27]; - case 26: prediction += coefficients[25] * (drflac_int64)pDecodedSamples[-26]; - case 25: prediction += coefficients[24] * (drflac_int64)pDecodedSamples[-25]; - case 24: prediction += coefficients[23] * (drflac_int64)pDecodedSamples[-24]; - case 23: prediction += coefficients[22] * (drflac_int64)pDecodedSamples[-23]; - case 22: prediction += coefficients[21] * (drflac_int64)pDecodedSamples[-22]; - case 21: prediction += coefficients[20] * (drflac_int64)pDecodedSamples[-21]; - case 20: prediction += coefficients[19] * (drflac_int64)pDecodedSamples[-20]; - case 19: prediction += coefficients[18] * (drflac_int64)pDecodedSamples[-19]; - case 18: prediction += coefficients[17] * (drflac_int64)pDecodedSamples[-18]; - case 17: prediction += coefficients[16] * (drflac_int64)pDecodedSamples[-17]; - case 16: prediction += coefficients[15] * (drflac_int64)pDecodedSamples[-16]; - case 15: prediction += coefficients[14] * (drflac_int64)pDecodedSamples[-15]; - case 14: prediction += coefficients[13] * (drflac_int64)pDecodedSamples[-14]; - case 13: prediction += coefficients[12] * (drflac_int64)pDecodedSamples[-13]; - case 12: prediction += coefficients[11] * (drflac_int64)pDecodedSamples[-12]; - case 11: prediction += coefficients[10] * (drflac_int64)pDecodedSamples[-11]; - case 10: prediction += coefficients[ 9] * (drflac_int64)pDecodedSamples[-10]; - case 9: prediction += coefficients[ 8] * (drflac_int64)pDecodedSamples[- 9]; - case 8: prediction += coefficients[ 7] * (drflac_int64)pDecodedSamples[- 8]; - case 7: prediction += coefficients[ 6] * (drflac_int64)pDecodedSamples[- 7]; - case 6: prediction += coefficients[ 5] * (drflac_int64)pDecodedSamples[- 6]; - case 5: prediction += coefficients[ 4] * (drflac_int64)pDecodedSamples[- 5]; - case 4: prediction += coefficients[ 3] * (drflac_int64)pDecodedSamples[- 4]; - case 3: prediction += coefficients[ 2] * (drflac_int64)pDecodedSamples[- 3]; - case 2: prediction += coefficients[ 1] * (drflac_int64)pDecodedSamples[- 2]; - case 1: prediction += coefficients[ 0] * (drflac_int64)pDecodedSamples[- 1]; + case 32: prediction += coefficients[31] * (ma_int64)pDecodedSamples[-32]; + case 31: prediction += coefficients[30] * (ma_int64)pDecodedSamples[-31]; + case 30: prediction += coefficients[29] * (ma_int64)pDecodedSamples[-30]; + case 29: prediction += coefficients[28] * (ma_int64)pDecodedSamples[-29]; + case 28: prediction += coefficients[27] * (ma_int64)pDecodedSamples[-28]; + case 27: prediction += coefficients[26] * (ma_int64)pDecodedSamples[-27]; + case 26: prediction += coefficients[25] * (ma_int64)pDecodedSamples[-26]; + case 25: prediction += coefficients[24] * (ma_int64)pDecodedSamples[-25]; + case 24: prediction += coefficients[23] * (ma_int64)pDecodedSamples[-24]; + case 23: prediction += coefficients[22] * (ma_int64)pDecodedSamples[-23]; + case 22: prediction += coefficients[21] * (ma_int64)pDecodedSamples[-22]; + case 21: prediction += coefficients[20] * (ma_int64)pDecodedSamples[-21]; + case 20: prediction += coefficients[19] * (ma_int64)pDecodedSamples[-20]; + case 19: prediction += coefficients[18] * (ma_int64)pDecodedSamples[-19]; + case 18: prediction += coefficients[17] * (ma_int64)pDecodedSamples[-18]; + case 17: prediction += coefficients[16] * (ma_int64)pDecodedSamples[-17]; + case 16: prediction += coefficients[15] * (ma_int64)pDecodedSamples[-16]; + case 15: prediction += coefficients[14] * (ma_int64)pDecodedSamples[-15]; + case 14: prediction += coefficients[13] * (ma_int64)pDecodedSamples[-14]; + case 13: prediction += coefficients[12] * (ma_int64)pDecodedSamples[-13]; + case 12: prediction += coefficients[11] * (ma_int64)pDecodedSamples[-12]; + case 11: prediction += coefficients[10] * (ma_int64)pDecodedSamples[-11]; + case 10: prediction += coefficients[ 9] * (ma_int64)pDecodedSamples[-10]; + case 9: prediction += coefficients[ 8] * (ma_int64)pDecodedSamples[- 9]; + case 8: prediction += coefficients[ 7] * (ma_int64)pDecodedSamples[- 8]; + case 7: prediction += coefficients[ 6] * (ma_int64)pDecodedSamples[- 7]; + case 6: prediction += coefficients[ 5] * (ma_int64)pDecodedSamples[- 6]; + case 5: prediction += coefficients[ 4] * (ma_int64)pDecodedSamples[- 5]; + case 4: prediction += coefficients[ 3] * (ma_int64)pDecodedSamples[- 4]; + case 3: prediction += coefficients[ 2] * (ma_int64)pDecodedSamples[- 3]; + case 2: prediction += coefficients[ 1] * (ma_int64)pDecodedSamples[- 2]; + case 1: prediction += coefficients[ 0] * (ma_int64)pDecodedSamples[- 1]; } #endif - return (drflac_int32)(prediction >> shift); + return (ma_int32)(prediction >> shift); } #if 0 -static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__reference(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) { - drflac_uint32 i; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pSamplesOut != NULL); + ma_uint32 i; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); for (i = 0; i < count; ++i) { - drflac_uint32 zeroCounter = 0; + ma_uint32 zeroCounter = 0; for (;;) { - drflac_uint8 bit; - if (!drflac__read_uint8(bs, 1, &bit)) { - return DRFLAC_FALSE; + ma_uint8 bit; + if (!ma_dr_flac__read_uint8(bs, 1, &bit)) { + return MA_FALSE; } if (bit == 0) { zeroCounter += 1; @@ -83326,10 +83498,10 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drfla break; } } - drflac_uint32 decodedRice; + ma_uint32 decodedRice; if (riceParam > 0) { - if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) { + return MA_FALSE; } } else { decodedRice = 0; @@ -83340,24 +83512,24 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__reference(drfla } else { decodedRice = (decodedRice >> 1); } - if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[i] = decodedRice + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } else { - pSamplesOut[i] = decodedRice + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + pSamplesOut[i] = decodedRice + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } } - return DRFLAC_TRUE; + return MA_TRUE; } #endif #if 0 -static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) +static ma_bool32 ma_dr_flac__read_rice_parts__reference(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) { - drflac_uint32 zeroCounter = 0; - drflac_uint32 decodedRice; + ma_uint32 zeroCounter = 0; + ma_uint32 decodedRice; for (;;) { - drflac_uint8 bit; - if (!drflac__read_uint8(bs, 1, &bit)) { - return DRFLAC_FALSE; + ma_uint8 bit; + if (!ma_dr_flac__read_uint8(bs, 1, &bit)) { + return MA_FALSE; } if (bit == 0) { zeroCounter += 1; @@ -83366,142 +83538,142 @@ static drflac_bool32 drflac__read_rice_parts__reference(drflac_bs* bs, drflac_ui } } if (riceParam > 0) { - if (!drflac__read_uint32(bs, riceParam, &decodedRice)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint32(bs, riceParam, &decodedRice)) { + return MA_FALSE; } } else { decodedRice = 0; } *pZeroCounterOut = zeroCounter; *pRiceParamPartOut = decodedRice; - return DRFLAC_TRUE; + return MA_TRUE; } #endif #if 0 -static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) +static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) { - drflac_cache_t riceParamMask; - drflac_uint32 zeroCounter; - drflac_uint32 setBitOffsetPlus1; - drflac_uint32 riceParamPart; - drflac_uint32 riceLength; - DRFLAC_ASSERT(riceParam > 0); - riceParamMask = DRFLAC_CACHE_L1_SELECTION_MASK(riceParam); + ma_dr_flac_cache_t riceParamMask; + ma_uint32 zeroCounter; + ma_uint32 setBitOffsetPlus1; + ma_uint32 riceParamPart; + ma_uint32 riceLength; + MA_DR_FLAC_ASSERT(riceParam > 0); + riceParamMask = MA_DR_FLAC_CACHE_L1_SELECTION_MASK(riceParam); zeroCounter = 0; while (bs->cache == 0) { - zeroCounter += (drflac_uint32)DRFLAC_CACHE_L1_BITS_REMAINING(bs); - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; + zeroCounter += (ma_uint32)MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs); + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; } } - setBitOffsetPlus1 = drflac__clz(bs->cache); + setBitOffsetPlus1 = ma_dr_flac__clz(bs->cache); zeroCounter += setBitOffsetPlus1; setBitOffsetPlus1 += 1; riceLength = setBitOffsetPlus1 + riceParam; - if (riceLength < DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - riceParamPart = (drflac_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength)); + if (riceLength < MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + riceParamPart = (ma_uint32)((bs->cache & (riceParamMask >> setBitOffsetPlus1)) >> MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceLength)); bs->consumedBits += riceLength; bs->cache <<= riceLength; } else { - drflac_uint32 bitCountLo; - drflac_cache_t resultHi; + ma_uint32 bitCountLo; + ma_dr_flac_cache_t resultHi; bs->consumedBits += riceLength; - bs->cache <<= setBitOffsetPlus1 & (DRFLAC_CACHE_L1_SIZE_BITS(bs)-1); - bitCountLo = bs->consumedBits - DRFLAC_CACHE_L1_SIZE_BITS(bs); - resultHi = DRFLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); - if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { -#ifndef DR_FLAC_NO_CRC - drflac__update_crc16(bs); + bs->cache <<= setBitOffsetPlus1 & (MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs)-1); + bitCountLo = bs->consumedBits - MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs); + resultHi = MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT(bs, riceParam); + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { +#ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); #endif - bs->cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs->cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs->consumedBits = 0; -#ifndef DR_FLAC_NO_CRC +#ifndef MA_DR_FLAC_NO_CRC bs->crc16Cache = bs->cache; #endif } else { - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; } - if (bitCountLo > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - return DRFLAC_FALSE; + if (bitCountLo > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + return MA_FALSE; } } - riceParamPart = (drflac_uint32)(resultHi | DRFLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo)); + riceParamPart = (ma_uint32)(resultHi | MA_DR_FLAC_CACHE_L1_SELECT_AND_SHIFT_SAFE(bs, bitCountLo)); bs->consumedBits += bitCountLo; bs->cache <<= bitCountLo; } pZeroCounterOut[0] = zeroCounter; pRiceParamPartOut[0] = riceParamPart; - return DRFLAC_TRUE; + return MA_TRUE; } #endif -static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x1(drflac_bs* bs, drflac_uint8 riceParam, drflac_uint32* pZeroCounterOut, drflac_uint32* pRiceParamPartOut) +static MA_INLINE ma_bool32 ma_dr_flac__read_rice_parts_x1(ma_dr_flac_bs* bs, ma_uint8 riceParam, ma_uint32* pZeroCounterOut, ma_uint32* pRiceParamPartOut) { - drflac_uint32 riceParamPlus1 = riceParam + 1; - drflac_uint32 riceParamPlus1Shift = DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1); - drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; - drflac_cache_t bs_cache = bs->cache; - drflac_uint32 bs_consumedBits = bs->consumedBits; - drflac_uint32 lzcount = drflac__clz(bs_cache); + ma_uint32 riceParamPlus1 = riceParam + 1; + ma_uint32 riceParamPlus1Shift = MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPlus1); + ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; + ma_dr_flac_cache_t bs_cache = bs->cache; + ma_uint32 bs_consumedBits = bs->consumedBits; + ma_uint32 lzcount = ma_dr_flac__clz(bs_cache); if (lzcount < sizeof(bs_cache)*8) { pZeroCounterOut[0] = lzcount; extract_rice_param_part: bs_cache <<= lzcount; bs_consumedBits += lzcount; if (bs_consumedBits <= riceParamPlus1MaxConsumedBits) { - pRiceParamPartOut[0] = (drflac_uint32)(bs_cache >> riceParamPlus1Shift); + pRiceParamPartOut[0] = (ma_uint32)(bs_cache >> riceParamPlus1Shift); bs_cache <<= riceParamPlus1; bs_consumedBits += riceParamPlus1; } else { - drflac_uint32 riceParamPartHi; - drflac_uint32 riceParamPartLo; - drflac_uint32 riceParamPartLoBitCount; - riceParamPartHi = (drflac_uint32)(bs_cache >> riceParamPlus1Shift); + ma_uint32 riceParamPartHi; + ma_uint32 riceParamPartLo; + ma_uint32 riceParamPartLoBitCount; + riceParamPartHi = (ma_uint32)(bs_cache >> riceParamPlus1Shift); riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; - DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); - if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef DR_FLAC_NO_CRC - drflac__update_crc16(bs); + MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); #endif - bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs_consumedBits = riceParamPartLoBitCount; - #ifndef DR_FLAC_NO_CRC + #ifndef MA_DR_FLAC_NO_CRC bs->crc16Cache = bs_cache; #endif } else { - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; } - if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - return DRFLAC_FALSE; + if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + return MA_FALSE; } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; } - riceParamPartLo = (drflac_uint32)(bs_cache >> (DRFLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount))); + riceParamPartLo = (ma_uint32)(bs_cache >> (MA_DR_FLAC_CACHE_L1_SELECTION_SHIFT(bs, riceParamPartLoBitCount))); pRiceParamPartOut[0] = riceParamPartHi | riceParamPartLo; bs_cache <<= riceParamPartLoBitCount; } } else { - drflac_uint32 zeroCounter = (drflac_uint32)(DRFLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits); + ma_uint32 zeroCounter = (ma_uint32)(MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - bs_consumedBits); for (;;) { - if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef DR_FLAC_NO_CRC - drflac__update_crc16(bs); + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); #endif - bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs_consumedBits = 0; - #ifndef DR_FLAC_NO_CRC + #ifndef MA_DR_FLAC_NO_CRC bs->crc16Cache = bs_cache; #endif } else { - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits; } - lzcount = drflac__clz(bs_cache); + lzcount = ma_dr_flac__clz(bs_cache); zeroCounter += lzcount; if (lzcount < sizeof(bs_cache)*8) { break; @@ -83512,15 +83684,15 @@ static DRFLAC_INLINE drflac_bool32 drflac__read_rice_parts_x1(drflac_bs* bs, drf } bs->cache = bs_cache; bs->consumedBits = bs_consumedBits; - return DRFLAC_TRUE; + return MA_TRUE; } -static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac_uint8 riceParam) +static MA_INLINE ma_bool32 ma_dr_flac__seek_rice_parts(ma_dr_flac_bs* bs, ma_uint8 riceParam) { - drflac_uint32 riceParamPlus1 = riceParam + 1; - drflac_uint32 riceParamPlus1MaxConsumedBits = DRFLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; - drflac_cache_t bs_cache = bs->cache; - drflac_uint32 bs_consumedBits = bs->consumedBits; - drflac_uint32 lzcount = drflac__clz(bs_cache); + ma_uint32 riceParamPlus1 = riceParam + 1; + ma_uint32 riceParamPlus1MaxConsumedBits = MA_DR_FLAC_CACHE_L1_SIZE_BITS(bs) - riceParamPlus1; + ma_dr_flac_cache_t bs_cache = bs->cache; + ma_uint32 bs_consumedBits = bs->consumedBits; + ma_uint32 lzcount = ma_dr_flac__clz(bs_cache); if (lzcount < sizeof(bs_cache)*8) { extract_rice_param_part: bs_cache <<= lzcount; @@ -83529,23 +83701,23 @@ static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac bs_cache <<= riceParamPlus1; bs_consumedBits += riceParamPlus1; } else { - drflac_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; - DRFLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); - if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef DR_FLAC_NO_CRC - drflac__update_crc16(bs); + ma_uint32 riceParamPartLoBitCount = bs_consumedBits - riceParamPlus1MaxConsumedBits; + MA_DR_FLAC_ASSERT(riceParamPartLoBitCount > 0 && riceParamPartLoBitCount < 32); + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); #endif - bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs_consumedBits = riceParamPartLoBitCount; - #ifndef DR_FLAC_NO_CRC + #ifndef MA_DR_FLAC_NO_CRC bs->crc16Cache = bs_cache; #endif } else { - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; } - if (riceParamPartLoBitCount > DRFLAC_CACHE_L1_BITS_REMAINING(bs)) { - return DRFLAC_FALSE; + if (riceParamPartLoBitCount > MA_DR_FLAC_CACHE_L1_BITS_REMAINING(bs)) { + return MA_FALSE; } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits + riceParamPartLoBitCount; @@ -83554,23 +83726,23 @@ static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac } } else { for (;;) { - if (bs->nextL2Line < DRFLAC_CACHE_L2_LINE_COUNT(bs)) { - #ifndef DR_FLAC_NO_CRC - drflac__update_crc16(bs); + if (bs->nextL2Line < MA_DR_FLAC_CACHE_L2_LINE_COUNT(bs)) { + #ifndef MA_DR_FLAC_NO_CRC + ma_dr_flac__update_crc16(bs); #endif - bs_cache = drflac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); + bs_cache = ma_dr_flac__be2host__cache_line(bs->cacheL2[bs->nextL2Line++]); bs_consumedBits = 0; - #ifndef DR_FLAC_NO_CRC + #ifndef MA_DR_FLAC_NO_CRC bs->crc16Cache = bs_cache; #endif } else { - if (!drflac__reload_cache(bs)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__reload_cache(bs)) { + return MA_FALSE; } bs_cache = bs->cache; bs_consumedBits = bs->consumedBits; } - lzcount = drflac__clz(bs_cache); + lzcount = ma_dr_flac__clz(bs_cache); if (lzcount < sizeof(bs_cache)*8) { break; } @@ -83579,26 +83751,26 @@ static DRFLAC_INLINE drflac_bool32 drflac__seek_rice_parts(drflac_bs* bs, drflac } bs->cache = bs_cache; bs->consumedBits = bs_consumedBits; - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar_zeroorder(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) { - drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - drflac_uint32 zeroCountPart0; - drflac_uint32 riceParamPart0; - drflac_uint32 riceParamMask; - drflac_uint32 i; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pSamplesOut != NULL); + ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + ma_uint32 zeroCountPart0; + ma_uint32 riceParamPart0; + ma_uint32 riceParamMask; + ma_uint32 i; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); (void)bitsPerSample; (void)order; (void)shift; (void)coefficients; - riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + riceParamMask = (ma_uint32)~((~0UL) << riceParam); i = 0; while (i < count) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { + return MA_FALSE; } riceParamPart0 &= riceParamMask; riceParamPart0 |= (zeroCountPart0 << riceParam); @@ -83606,36 +83778,36 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar_zeroorde pSamplesOut[i] = riceParamPart0; i += 1; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__scalar(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) { - drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - drflac_uint32 zeroCountPart0 = 0; - drflac_uint32 zeroCountPart1 = 0; - drflac_uint32 zeroCountPart2 = 0; - drflac_uint32 zeroCountPart3 = 0; - drflac_uint32 riceParamPart0 = 0; - drflac_uint32 riceParamPart1 = 0; - drflac_uint32 riceParamPart2 = 0; - drflac_uint32 riceParamPart3 = 0; - drflac_uint32 riceParamMask; - const drflac_int32* pSamplesOutEnd; - drflac_uint32 i; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pSamplesOut != NULL); + ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + ma_uint32 zeroCountPart0 = 0; + ma_uint32 zeroCountPart1 = 0; + ma_uint32 zeroCountPart2 = 0; + ma_uint32 zeroCountPart3 = 0; + ma_uint32 riceParamPart0 = 0; + ma_uint32 riceParamPart1 = 0; + ma_uint32 riceParamPart2 = 0; + ma_uint32 riceParamPart3 = 0; + ma_uint32 riceParamMask; + const ma_int32* pSamplesOutEnd; + ma_uint32 i; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); if (lpcOrder == 0) { - return drflac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + return ma_dr_flac__decode_samples_with_residual__rice__scalar_zeroorder(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } - riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + riceParamMask = (ma_uint32)~((~0UL) << riceParam); pSamplesOutEnd = pSamplesOut + (count & ~3); - if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { while (pSamplesOut < pSamplesOutEnd) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { + return MA_FALSE; } riceParamPart0 &= riceParamMask; riceParamPart1 &= riceParamMask; @@ -83649,19 +83821,19 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_b riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; - pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); - pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); - pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); + pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); pSamplesOut += 4; } } else { while (pSamplesOut < pSamplesOutEnd) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart1, &riceParamPart1) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart2, &riceParamPart2) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart3, &riceParamPart3)) { + return MA_FALSE; } riceParamPart0 &= riceParamMask; riceParamPart1 &= riceParamMask; @@ -83675,33 +83847,33 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__scalar(drflac_b riceParamPart1 = (riceParamPart1 >> 1) ^ t[riceParamPart1 & 0x01]; riceParamPart2 = (riceParamPart2 >> 1) ^ t[riceParamPart2 & 0x01]; riceParamPart3 = (riceParamPart3 >> 1) ^ t[riceParamPart3 & 0x01]; - pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); - pSamplesOut[1] = riceParamPart1 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); - pSamplesOut[2] = riceParamPart2 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); - pSamplesOut[3] = riceParamPart3 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); + pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + pSamplesOut[1] = riceParamPart1 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 1); + pSamplesOut[2] = riceParamPart2 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 2); + pSamplesOut[3] = riceParamPart3 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 3); pSamplesOut += 4; } } i = (count & ~3); while (i < count) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountPart0, &riceParamPart0)) { + return MA_FALSE; } riceParamPart0 &= riceParamMask; riceParamPart0 |= (zeroCountPart0 << riceParam); riceParamPart0 = (riceParamPart0 >> 1) ^ t[riceParamPart0 & 0x01]; - if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); } else { - pSamplesOut[0] = riceParamPart0 + drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); + pSamplesOut[0] = riceParamPart0 + ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + 0); } i += 1; pSamplesOut += 1; } - return DRFLAC_TRUE; + return MA_TRUE; } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE __m128i drflac__mm_packs_interleaved_epi32(__m128i a, __m128i b) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE __m128i ma_dr_flac__mm_packs_interleaved_epi32(__m128i a, __m128i b) { __m128i r; r = _mm_packs_epi32(a, b); @@ -83711,42 +83883,42 @@ static DRFLAC_INLINE __m128i drflac__mm_packs_interleaved_epi32(__m128i a, __m12 return r; } #endif -#if defined(DRFLAC_SUPPORT_SSE41) -static DRFLAC_INLINE __m128i drflac__mm_not_si128(__m128i a) +#if defined(MA_DR_FLAC_SUPPORT_SSE41) +static MA_INLINE __m128i ma_dr_flac__mm_not_si128(__m128i a) { return _mm_xor_si128(a, _mm_cmpeq_epi32(_mm_setzero_si128(), _mm_setzero_si128())); } -static DRFLAC_INLINE __m128i drflac__mm_hadd_epi32(__m128i x) +static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi32(__m128i x) { __m128i x64 = _mm_add_epi32(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); __m128i x32 = _mm_shufflelo_epi16(x64, _MM_SHUFFLE(1, 0, 3, 2)); return _mm_add_epi32(x64, x32); } -static DRFLAC_INLINE __m128i drflac__mm_hadd_epi64(__m128i x) +static MA_INLINE __m128i ma_dr_flac__mm_hadd_epi64(__m128i x) { return _mm_add_epi64(x, _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2))); } -static DRFLAC_INLINE __m128i drflac__mm_srai_epi64(__m128i x, int count) +static MA_INLINE __m128i ma_dr_flac__mm_srai_epi64(__m128i x, int count) { __m128i lo = _mm_srli_epi64(x, count); __m128i hi = _mm_srai_epi32(x, count); hi = _mm_and_si128(hi, _mm_set_epi32(0xFFFFFFFF, 0, 0xFFFFFFFF, 0)); return _mm_or_si128(lo, hi); } -static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) { int i; - drflac_uint32 riceParamMask; - drflac_int32* pDecodedSamples = pSamplesOut; - drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - drflac_uint32 zeroCountParts0 = 0; - drflac_uint32 zeroCountParts1 = 0; - drflac_uint32 zeroCountParts2 = 0; - drflac_uint32 zeroCountParts3 = 0; - drflac_uint32 riceParamParts0 = 0; - drflac_uint32 riceParamParts1 = 0; - drflac_uint32 riceParamParts2 = 0; - drflac_uint32 riceParamParts3 = 0; + ma_uint32 riceParamMask; + ma_int32* pDecodedSamples = pSamplesOut; + ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + ma_uint32 zeroCountParts0 = 0; + ma_uint32 zeroCountParts1 = 0; + ma_uint32 zeroCountParts2 = 0; + ma_uint32 zeroCountParts3 = 0; + ma_uint32 riceParamParts0 = 0; + ma_uint32 riceParamParts1 = 0; + ma_uint32 riceParamParts2 = 0; + ma_uint32 riceParamParts3 = 0; __m128i coefficients128_0; __m128i coefficients128_4; __m128i coefficients128_8; @@ -83754,8 +83926,8 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac __m128i samples128_4; __m128i samples128_8; __m128i riceParamMask128; - const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + riceParamMask = (ma_uint32)~((~0UL) << riceParam); riceParamMask128 = _mm_set1_epi32(riceParamMask); coefficients128_0 = _mm_setzero_si128(); coefficients128_4 = _mm_setzero_si128(); @@ -83809,39 +83981,39 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac #else switch (order) { - case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12]; - case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11]; - case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10]; - case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; - case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; - case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; - case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; - case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; - case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; - case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; - case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; - case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; + case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12]; + case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11]; + case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10]; + case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; + case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; + case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; + case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; + case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; + case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; + case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; + case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; + case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; } #endif while (pDecodedSamples < pDecodedSamplesEnd) { __m128i prediction128; __m128i zeroCountPart128; __m128i riceParamPart128; - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { + return MA_FALSE; } zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); - riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01))); + riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(0x01))), _mm_set1_epi32(0x01))); if (order <= 4) { for (i = 0; i < 4; i += 1) { prediction128 = _mm_mullo_epi32(coefficients128_0, samples128_0); - prediction128 = drflac__mm_hadd_epi32(prediction128); + prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); prediction128 = _mm_srai_epi32(prediction128, shift); prediction128 = _mm_add_epi32(riceParamPart128, prediction128); samples128_0 = _mm_alignr_epi8(prediction128, samples128_0, 4); @@ -83851,7 +84023,7 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac for (i = 0; i < 4; i += 1) { prediction128 = _mm_mullo_epi32(coefficients128_4, samples128_4); prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); - prediction128 = drflac__mm_hadd_epi32(prediction128); + prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); prediction128 = _mm_srai_epi32(prediction128, shift); prediction128 = _mm_add_epi32(riceParamPart128, prediction128); samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); @@ -83863,7 +84035,7 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac prediction128 = _mm_mullo_epi32(coefficients128_8, samples128_8); prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_4, samples128_4)); prediction128 = _mm_add_epi32(prediction128, _mm_mullo_epi32(coefficients128_0, samples128_0)); - prediction128 = drflac__mm_hadd_epi32(prediction128); + prediction128 = ma_dr_flac__mm_hadd_epi32(prediction128); prediction128 = _mm_srai_epi32(prediction128, shift); prediction128 = _mm_add_epi32(riceParamPart128, prediction128); samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); @@ -83877,32 +84049,32 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_32(drflac } i = (count & ~3); while (i < (int)count) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { + return MA_FALSE; } riceParamParts0 &= riceParamMask; riceParamParts0 |= (zeroCountParts0 << riceParam); riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; - pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); + pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); i += 1; pDecodedSamples += 1; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) { int i; - drflac_uint32 riceParamMask; - drflac_int32* pDecodedSamples = pSamplesOut; - drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - drflac_uint32 zeroCountParts0 = 0; - drflac_uint32 zeroCountParts1 = 0; - drflac_uint32 zeroCountParts2 = 0; - drflac_uint32 zeroCountParts3 = 0; - drflac_uint32 riceParamParts0 = 0; - drflac_uint32 riceParamParts1 = 0; - drflac_uint32 riceParamParts2 = 0; - drflac_uint32 riceParamParts3 = 0; + ma_uint32 riceParamMask; + ma_int32* pDecodedSamples = pSamplesOut; + ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + ma_uint32 zeroCountParts0 = 0; + ma_uint32 zeroCountParts1 = 0; + ma_uint32 zeroCountParts2 = 0; + ma_uint32 zeroCountParts3 = 0; + ma_uint32 riceParamParts0 = 0; + ma_uint32 riceParamParts1 = 0; + ma_uint32 riceParamParts2 = 0; + ma_uint32 riceParamParts3 = 0; __m128i coefficients128_0; __m128i coefficients128_4; __m128i coefficients128_8; @@ -83911,9 +84083,9 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac __m128i samples128_8; __m128i prediction128; __m128i riceParamMask128; - const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - DRFLAC_ASSERT(order <= 12); - riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + MA_DR_FLAC_ASSERT(order <= 12); + riceParamMask = (ma_uint32)~((~0UL) << riceParam); riceParamMask128 = _mm_set1_epi32(riceParamMask); prediction128 = _mm_setzero_si128(); coefficients128_0 = _mm_setzero_si128(); @@ -83968,34 +84140,34 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac #else switch (order) { - case 12: ((drflac_int32*)&coefficients128_8)[0] = coefficients[11]; ((drflac_int32*)&samples128_8)[0] = pDecodedSamples[-12]; - case 11: ((drflac_int32*)&coefficients128_8)[1] = coefficients[10]; ((drflac_int32*)&samples128_8)[1] = pDecodedSamples[-11]; - case 10: ((drflac_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((drflac_int32*)&samples128_8)[2] = pDecodedSamples[-10]; - case 9: ((drflac_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((drflac_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; - case 8: ((drflac_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((drflac_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; - case 7: ((drflac_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((drflac_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; - case 6: ((drflac_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((drflac_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; - case 5: ((drflac_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((drflac_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; - case 4: ((drflac_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((drflac_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; - case 3: ((drflac_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((drflac_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; - case 2: ((drflac_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((drflac_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; - case 1: ((drflac_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((drflac_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; + case 12: ((ma_int32*)&coefficients128_8)[0] = coefficients[11]; ((ma_int32*)&samples128_8)[0] = pDecodedSamples[-12]; + case 11: ((ma_int32*)&coefficients128_8)[1] = coefficients[10]; ((ma_int32*)&samples128_8)[1] = pDecodedSamples[-11]; + case 10: ((ma_int32*)&coefficients128_8)[2] = coefficients[ 9]; ((ma_int32*)&samples128_8)[2] = pDecodedSamples[-10]; + case 9: ((ma_int32*)&coefficients128_8)[3] = coefficients[ 8]; ((ma_int32*)&samples128_8)[3] = pDecodedSamples[- 9]; + case 8: ((ma_int32*)&coefficients128_4)[0] = coefficients[ 7]; ((ma_int32*)&samples128_4)[0] = pDecodedSamples[- 8]; + case 7: ((ma_int32*)&coefficients128_4)[1] = coefficients[ 6]; ((ma_int32*)&samples128_4)[1] = pDecodedSamples[- 7]; + case 6: ((ma_int32*)&coefficients128_4)[2] = coefficients[ 5]; ((ma_int32*)&samples128_4)[2] = pDecodedSamples[- 6]; + case 5: ((ma_int32*)&coefficients128_4)[3] = coefficients[ 4]; ((ma_int32*)&samples128_4)[3] = pDecodedSamples[- 5]; + case 4: ((ma_int32*)&coefficients128_0)[0] = coefficients[ 3]; ((ma_int32*)&samples128_0)[0] = pDecodedSamples[- 4]; + case 3: ((ma_int32*)&coefficients128_0)[1] = coefficients[ 2]; ((ma_int32*)&samples128_0)[1] = pDecodedSamples[- 3]; + case 2: ((ma_int32*)&coefficients128_0)[2] = coefficients[ 1]; ((ma_int32*)&samples128_0)[2] = pDecodedSamples[- 2]; + case 1: ((ma_int32*)&coefficients128_0)[3] = coefficients[ 0]; ((ma_int32*)&samples128_0)[3] = pDecodedSamples[- 1]; } #endif while (pDecodedSamples < pDecodedSamplesEnd) { __m128i zeroCountPart128; __m128i riceParamPart128; - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts1, &riceParamParts1) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts2, &riceParamParts2) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts3, &riceParamParts3)) { + return MA_FALSE; } zeroCountPart128 = _mm_set_epi32(zeroCountParts3, zeroCountParts2, zeroCountParts1, zeroCountParts0); riceParamPart128 = _mm_set_epi32(riceParamParts3, riceParamParts2, riceParamParts1, riceParamParts0); riceParamPart128 = _mm_and_si128(riceParamPart128, riceParamMask128); riceParamPart128 = _mm_or_si128(riceParamPart128, _mm_slli_epi32(zeroCountPart128, riceParam)); - riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(drflac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1))); + riceParamPart128 = _mm_xor_si128(_mm_srli_epi32(riceParamPart128, 1), _mm_add_epi32(ma_dr_flac__mm_not_si128(_mm_and_si128(riceParamPart128, _mm_set1_epi32(1))), _mm_set1_epi32(1))); for (i = 0; i < 4; i += 1) { prediction128 = _mm_xor_si128(prediction128, prediction128); switch (order) @@ -84013,8 +84185,8 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac case 2: case 1: prediction128 = _mm_add_epi64(prediction128, _mm_mul_epi32(_mm_shuffle_epi32(coefficients128_0, _MM_SHUFFLE(3, 3, 2, 2)), _mm_shuffle_epi32(samples128_0, _MM_SHUFFLE(3, 3, 2, 2)))); } - prediction128 = drflac__mm_hadd_epi64(prediction128); - prediction128 = drflac__mm_srai_epi64(prediction128, shift); + prediction128 = ma_dr_flac__mm_hadd_epi64(prediction128); + prediction128 = ma_dr_flac__mm_srai_epi64(prediction128, shift); prediction128 = _mm_add_epi32(riceParamPart128, prediction128); samples128_8 = _mm_alignr_epi8(samples128_4, samples128_8, 4); samples128_4 = _mm_alignr_epi8(samples128_0, samples128_4, 4); @@ -84026,103 +84198,103 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41_64(drflac } i = (count & ~3); while (i < (int)count) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts0, &riceParamParts0)) { + return MA_FALSE; } riceParamParts0 &= riceParamMask; riceParamParts0 |= (zeroCountParts0 << riceParam); riceParamParts0 = (riceParamParts0 >> 1) ^ t[riceParamParts0 & 0x01]; - pDecodedSamples[0] = riceParamParts0 + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); + pDecodedSamples[0] = riceParamParts0 + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); i += 1; pDecodedSamples += 1; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual__rice__sse41(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__sse41(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) { - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pSamplesOut != NULL); + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); if (lpcOrder > 0 && lpcOrder <= 12) { - if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - return drflac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + return ma_dr_flac__decode_samples_with_residual__rice__sse41_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } else { - return drflac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + return ma_dr_flac__decode_samples_with_residual__rice__sse41_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } } else { - return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac__vst2q_s32(drflac_int32* p, int32x4x2_t x) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac__vst2q_s32(ma_int32* p, int32x4x2_t x) { vst1q_s32(p+0, x.val[0]); vst1q_s32(p+4, x.val[1]); } -static DRFLAC_INLINE void drflac__vst2q_u32(drflac_uint32* p, uint32x4x2_t x) +static MA_INLINE void ma_dr_flac__vst2q_u32(ma_uint32* p, uint32x4x2_t x) { vst1q_u32(p+0, x.val[0]); vst1q_u32(p+4, x.val[1]); } -static DRFLAC_INLINE void drflac__vst2q_f32(float* p, float32x4x2_t x) +static MA_INLINE void ma_dr_flac__vst2q_f32(float* p, float32x4x2_t x) { vst1q_f32(p+0, x.val[0]); vst1q_f32(p+4, x.val[1]); } -static DRFLAC_INLINE void drflac__vst2q_s16(drflac_int16* p, int16x4x2_t x) +static MA_INLINE void ma_dr_flac__vst2q_s16(ma_int16* p, int16x4x2_t x) { vst1q_s16(p, vcombine_s16(x.val[0], x.val[1])); } -static DRFLAC_INLINE void drflac__vst2q_u16(drflac_uint16* p, uint16x4x2_t x) +static MA_INLINE void ma_dr_flac__vst2q_u16(ma_uint16* p, uint16x4x2_t x) { vst1q_u16(p, vcombine_u16(x.val[0], x.val[1])); } -static DRFLAC_INLINE int32x4_t drflac__vdupq_n_s32x4(drflac_int32 x3, drflac_int32 x2, drflac_int32 x1, drflac_int32 x0) +static MA_INLINE int32x4_t ma_dr_flac__vdupq_n_s32x4(ma_int32 x3, ma_int32 x2, ma_int32 x1, ma_int32 x0) { - drflac_int32 x[4]; + ma_int32 x[4]; x[3] = x3; x[2] = x2; x[1] = x1; x[0] = x0; return vld1q_s32(x); } -static DRFLAC_INLINE int32x4_t drflac__valignrq_s32_1(int32x4_t a, int32x4_t b) +static MA_INLINE int32x4_t ma_dr_flac__valignrq_s32_1(int32x4_t a, int32x4_t b) { return vextq_s32(b, a, 1); } -static DRFLAC_INLINE uint32x4_t drflac__valignrq_u32_1(uint32x4_t a, uint32x4_t b) +static MA_INLINE uint32x4_t ma_dr_flac__valignrq_u32_1(uint32x4_t a, uint32x4_t b) { return vextq_u32(b, a, 1); } -static DRFLAC_INLINE int32x2_t drflac__vhaddq_s32(int32x4_t x) +static MA_INLINE int32x2_t ma_dr_flac__vhaddq_s32(int32x4_t x) { int32x2_t r = vadd_s32(vget_high_s32(x), vget_low_s32(x)); return vpadd_s32(r, r); } -static DRFLAC_INLINE int64x1_t drflac__vhaddq_s64(int64x2_t x) +static MA_INLINE int64x1_t ma_dr_flac__vhaddq_s64(int64x2_t x) { return vadd_s64(vget_high_s64(x), vget_low_s64(x)); } -static DRFLAC_INLINE int32x4_t drflac__vrevq_s32(int32x4_t x) +static MA_INLINE int32x4_t ma_dr_flac__vrevq_s32(int32x4_t x) { return vrev64q_s32(vcombine_s32(vget_high_s32(x), vget_low_s32(x))); } -static DRFLAC_INLINE int32x4_t drflac__vnotq_s32(int32x4_t x) +static MA_INLINE int32x4_t ma_dr_flac__vnotq_s32(int32x4_t x) { return veorq_s32(x, vdupq_n_s32(0xFFFFFFFF)); } -static DRFLAC_INLINE uint32x4_t drflac__vnotq_u32(uint32x4_t x) +static MA_INLINE uint32x4_t ma_dr_flac__vnotq_u32(uint32x4_t x) { return veorq_u32(x, vdupq_n_u32(0xFFFFFFFF)); } -static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_32(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) { int i; - drflac_uint32 riceParamMask; - drflac_int32* pDecodedSamples = pSamplesOut; - drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - drflac_uint32 zeroCountParts[4]; - drflac_uint32 riceParamParts[4]; + ma_uint32 riceParamMask; + ma_int32* pDecodedSamples = pSamplesOut; + ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + ma_uint32 zeroCountParts[4]; + ma_uint32 riceParamParts[4]; int32x4_t coefficients128_0; int32x4_t coefficients128_4; int32x4_t coefficients128_8; @@ -84133,16 +84305,16 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_ int32x4_t riceParam128; int32x2_t shift64; uint32x4_t one128; - const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + riceParamMask = (ma_uint32)~((~0UL) << riceParam); riceParamMask128 = vdupq_n_u32(riceParamMask); riceParam128 = vdupq_n_s32(riceParam); shift64 = vdup_n_s32(-shift); one128 = vdupq_n_u32(1); { int runningOrder = order; - drflac_int32 tempC[4] = {0, 0, 0, 0}; - drflac_int32 tempS[4] = {0, 0, 0, 0}; + ma_int32 tempC[4] = {0, 0, 0, 0}; + ma_int32 tempS[4] = {0, 0, 0, 0}; if (runningOrder >= 4) { coefficients128_0 = vld1q_s32(coefficients + 0); samples128_0 = vld1q_s32(pSamplesOut - 4); @@ -84185,58 +84357,58 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_ samples128_8 = vld1q_s32(tempS); runningOrder = 0; } - coefficients128_0 = drflac__vrevq_s32(coefficients128_0); - coefficients128_4 = drflac__vrevq_s32(coefficients128_4); - coefficients128_8 = drflac__vrevq_s32(coefficients128_8); + coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0); + coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4); + coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8); } while (pDecodedSamples < pDecodedSamplesEnd) { int32x4_t prediction128; int32x2_t prediction64; uint32x4_t zeroCountPart128; uint32x4_t riceParamPart128; - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { + return MA_FALSE; } zeroCountPart128 = vld1q_u32(zeroCountParts); riceParamPart128 = vld1q_u32(riceParamParts); riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); - riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); + riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); if (order <= 4) { for (i = 0; i < 4; i += 1) { prediction128 = vmulq_s32(coefficients128_0, samples128_0); - prediction64 = drflac__vhaddq_s32(prediction128); + prediction64 = ma_dr_flac__vhaddq_s32(prediction128); prediction64 = vshl_s32(prediction64, shift64); prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); + riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); } } else if (order <= 8) { for (i = 0; i < 4; i += 1) { prediction128 = vmulq_s32(coefficients128_4, samples128_4); prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); - prediction64 = drflac__vhaddq_s32(prediction128); + prediction64 = ma_dr_flac__vhaddq_s32(prediction128); prediction64 = vshl_s32(prediction64, shift64); prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); + samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); + riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); } } else { for (i = 0; i < 4; i += 1) { prediction128 = vmulq_s32(coefficients128_8, samples128_8); prediction128 = vmlaq_s32(prediction128, coefficients128_4, samples128_4); prediction128 = vmlaq_s32(prediction128, coefficients128_0, samples128_0); - prediction64 = drflac__vhaddq_s32(prediction128); + prediction64 = ma_dr_flac__vhaddq_s32(prediction128); prediction64 = vshl_s32(prediction64, shift64); prediction64 = vadd_s32(prediction64, vget_low_s32(vreinterpretq_s32_u32(riceParamPart128))); - samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8); - samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = drflac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); - riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8); + samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); + samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(prediction64, vdup_n_s32(0)), samples128_0); + riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); } } vst1q_s32(pDecodedSamples, samples128_0); @@ -84244,26 +84416,26 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_32(drflac_ } i = (count & ~3); while (i < (int)count) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { + return MA_FALSE; } riceParamParts[0] &= riceParamMask; riceParamParts[0] |= (zeroCountParts[0] << riceParam); riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; - pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); + pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_32(order, shift, coefficients, pDecodedSamples); i += 1; pDecodedSamples += 1; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 order, drflac_int32 shift, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon_64(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam, ma_uint32 order, ma_int32 shift, const ma_int32* coefficients, ma_int32* pSamplesOut) { int i; - drflac_uint32 riceParamMask; - drflac_int32* pDecodedSamples = pSamplesOut; - drflac_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); - drflac_uint32 zeroCountParts[4]; - drflac_uint32 riceParamParts[4]; + ma_uint32 riceParamMask; + ma_int32* pDecodedSamples = pSamplesOut; + ma_int32* pDecodedSamplesEnd = pSamplesOut + (count & ~3); + ma_uint32 zeroCountParts[4]; + ma_uint32 riceParamParts[4]; int32x4_t coefficients128_0; int32x4_t coefficients128_4; int32x4_t coefficients128_8; @@ -84277,16 +84449,16 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_ int64x2_t prediction128 = { 0 }; uint32x4_t zeroCountPart128; uint32x4_t riceParamPart128; - const drflac_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; - riceParamMask = (drflac_uint32)~((~0UL) << riceParam); + const ma_uint32 t[2] = {0x00000000, 0xFFFFFFFF}; + riceParamMask = (ma_uint32)~((~0UL) << riceParam); riceParamMask128 = vdupq_n_u32(riceParamMask); riceParam128 = vdupq_n_s32(riceParam); shift64 = vdup_n_s64(-shift); one128 = vdupq_n_u32(1); { int runningOrder = order; - drflac_int32 tempC[4] = {0, 0, 0, 0}; - drflac_int32 tempS[4] = {0, 0, 0, 0}; + ma_int32 tempC[4] = {0, 0, 0, 0}; + ma_int32 tempS[4] = {0, 0, 0, 0}; if (runningOrder >= 4) { coefficients128_0 = vld1q_s32(coefficients + 0); samples128_0 = vld1q_s32(pSamplesOut - 4); @@ -84329,22 +84501,22 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_ samples128_8 = vld1q_s32(tempS); runningOrder = 0; } - coefficients128_0 = drflac__vrevq_s32(coefficients128_0); - coefficients128_4 = drflac__vrevq_s32(coefficients128_4); - coefficients128_8 = drflac__vrevq_s32(coefficients128_8); + coefficients128_0 = ma_dr_flac__vrevq_s32(coefficients128_0); + coefficients128_4 = ma_dr_flac__vrevq_s32(coefficients128_4); + coefficients128_8 = ma_dr_flac__vrevq_s32(coefficients128_8); } while (pDecodedSamples < pDecodedSamplesEnd) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || - !drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[1], &riceParamParts[1]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[2], &riceParamParts[2]) || + !ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[3], &riceParamParts[3])) { + return MA_FALSE; } zeroCountPart128 = vld1q_u32(zeroCountParts); riceParamPart128 = vld1q_u32(riceParamParts); riceParamPart128 = vandq_u32(riceParamPart128, riceParamMask128); riceParamPart128 = vorrq_u32(riceParamPart128, vshlq_u32(zeroCountPart128, riceParam128)); - riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(drflac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); + riceParamPart128 = veorq_u32(vshrq_n_u32(riceParamPart128, 1), vaddq_u32(ma_dr_flac__vnotq_u32(vandq_u32(riceParamPart128, one128)), one128)); for (i = 0; i < 4; i += 1) { int64x1_t prediction64; prediction128 = veorq_s64(prediction128, prediction128); @@ -84363,156 +84535,156 @@ static drflac_bool32 drflac__decode_samples_with_residual__rice__neon_64(drflac_ case 2: case 1: prediction128 = vaddq_s64(prediction128, vmull_s32(vget_high_s32(coefficients128_0), vget_high_s32(samples128_0))); } - prediction64 = drflac__vhaddq_s64(prediction128); + prediction64 = ma_dr_flac__vhaddq_s64(prediction128); prediction64 = vshl_s64(prediction64, shift64); prediction64 = vadd_s64(prediction64, vdup_n_s64(vgetq_lane_u32(riceParamPart128, 0))); - samples128_8 = drflac__valignrq_s32_1(samples128_4, samples128_8); - samples128_4 = drflac__valignrq_s32_1(samples128_0, samples128_4); - samples128_0 = drflac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0); - riceParamPart128 = drflac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); + samples128_8 = ma_dr_flac__valignrq_s32_1(samples128_4, samples128_8); + samples128_4 = ma_dr_flac__valignrq_s32_1(samples128_0, samples128_4); + samples128_0 = ma_dr_flac__valignrq_s32_1(vcombine_s32(vreinterpret_s32_s64(prediction64), vdup_n_s32(0)), samples128_0); + riceParamPart128 = ma_dr_flac__valignrq_u32_1(vdupq_n_u32(0), riceParamPart128); } vst1q_s32(pDecodedSamples, samples128_0); pDecodedSamples += 4; } i = (count & ~3); while (i < (int)count) { - if (!drflac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_rice_parts_x1(bs, riceParam, &zeroCountParts[0], &riceParamParts[0])) { + return MA_FALSE; } riceParamParts[0] &= riceParamMask; riceParamParts[0] |= (zeroCountParts[0] << riceParam); riceParamParts[0] = (riceParamParts[0] >> 1) ^ t[riceParamParts[0] & 0x01]; - pDecodedSamples[0] = riceParamParts[0] + drflac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); + pDecodedSamples[0] = riceParamParts[0] + ma_dr_flac__calculate_prediction_64(order, shift, coefficients, pDecodedSamples); i += 1; pDecodedSamples += 1; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual__rice__neon(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice__neon(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) { - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(pSamplesOut != NULL); + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); if (lpcOrder > 0 && lpcOrder <= 12) { - if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - return drflac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + return ma_dr_flac__decode_samples_with_residual__rice__neon_64(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } else { - return drflac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); + return ma_dr_flac__decode_samples_with_residual__rice__neon_32(bs, count, riceParam, lpcOrder, lpcShift, coefficients, pSamplesOut); } } else { - return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } } #endif -static drflac_bool32 drflac__decode_samples_with_residual__rice(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 riceParam, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__rice(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 riceParam, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) { -#if defined(DRFLAC_SUPPORT_SSE41) - if (drflac__gIsSSE41Supported) { - return drflac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); +#if defined(MA_DR_FLAC_SUPPORT_SSE41) + if (ma_dr_flac__gIsSSE41Supported) { + return ma_dr_flac__decode_samples_with_residual__rice__sse41(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported) { - return drflac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported) { + return ma_dr_flac__decode_samples_with_residual__rice__neon(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); } else #endif { #if 0 - return drflac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + return ma_dr_flac__decode_samples_with_residual__rice__reference(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); #else - return drflac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); + return ma_dr_flac__decode_samples_with_residual__rice__scalar(bs, bitsPerSample, count, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pSamplesOut); #endif } } -static drflac_bool32 drflac__read_and_seek_residual__rice(drflac_bs* bs, drflac_uint32 count, drflac_uint8 riceParam) +static ma_bool32 ma_dr_flac__read_and_seek_residual__rice(ma_dr_flac_bs* bs, ma_uint32 count, ma_uint8 riceParam) { - drflac_uint32 i; - DRFLAC_ASSERT(bs != NULL); + ma_uint32 i; + MA_DR_FLAC_ASSERT(bs != NULL); for (i = 0; i < count; ++i) { - if (!drflac__seek_rice_parts(bs, riceParam)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_rice_parts(bs, riceParam)) { + return MA_FALSE; } } - return DRFLAC_TRUE; + return MA_TRUE; } #if defined(__clang__) __attribute__((no_sanitize("signed-integer-overflow"))) #endif -static drflac_bool32 drflac__decode_samples_with_residual__unencoded(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 count, drflac_uint8 unencodedBitsPerSample, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pSamplesOut) +static ma_bool32 ma_dr_flac__decode_samples_with_residual__unencoded(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 count, ma_uint8 unencodedBitsPerSample, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pSamplesOut) { - drflac_uint32 i; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(unencodedBitsPerSample <= 31); - DRFLAC_ASSERT(pSamplesOut != NULL); + ma_uint32 i; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(unencodedBitsPerSample <= 31); + MA_DR_FLAC_ASSERT(pSamplesOut != NULL); for (i = 0; i < count; ++i) { if (unencodedBitsPerSample > 0) { - if (!drflac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_int32(bs, unencodedBitsPerSample, pSamplesOut + i)) { + return MA_FALSE; } } else { pSamplesOut[i] = 0; } - if (drflac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { - pSamplesOut[i] += drflac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + if (ma_dr_flac__use_64_bit_prediction(bitsPerSample, lpcOrder, lpcPrecision)) { + pSamplesOut[i] += ma_dr_flac__calculate_prediction_64(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } else { - pSamplesOut[i] += drflac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); + pSamplesOut[i] += ma_dr_flac__calculate_prediction_32(lpcOrder, lpcShift, coefficients, pSamplesOut + i); } } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_uint32 bitsPerSample, drflac_uint32 blockSize, drflac_uint32 lpcOrder, drflac_int32 lpcShift, drflac_uint32 lpcPrecision, const drflac_int32* coefficients, drflac_int32* pDecodedSamples) +static ma_bool32 ma_dr_flac__decode_samples_with_residual(ma_dr_flac_bs* bs, ma_uint32 bitsPerSample, ma_uint32 blockSize, ma_uint32 lpcOrder, ma_int32 lpcShift, ma_uint32 lpcPrecision, const ma_int32* coefficients, ma_int32* pDecodedSamples) { - drflac_uint8 residualMethod; - drflac_uint8 partitionOrder; - drflac_uint32 samplesInPartition; - drflac_uint32 partitionsRemaining; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(blockSize != 0); - DRFLAC_ASSERT(pDecodedSamples != NULL); - if (!drflac__read_uint8(bs, 2, &residualMethod)) { - return DRFLAC_FALSE; + ma_uint8 residualMethod; + ma_uint8 partitionOrder; + ma_uint32 samplesInPartition; + ma_uint32 partitionsRemaining; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(blockSize != 0); + MA_DR_FLAC_ASSERT(pDecodedSamples != NULL); + if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) { + return MA_FALSE; } - if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - return DRFLAC_FALSE; + if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + return MA_FALSE; } pDecodedSamples += lpcOrder; - if (!drflac__read_uint8(bs, 4, &partitionOrder)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) { + return MA_FALSE; } if (partitionOrder > 8) { - return DRFLAC_FALSE; + return MA_FALSE; } if ((blockSize / (1 << partitionOrder)) < lpcOrder) { - return DRFLAC_FALSE; + return MA_FALSE; } samplesInPartition = (blockSize / (1 << partitionOrder)) - lpcOrder; partitionsRemaining = (1 << partitionOrder); for (;;) { - drflac_uint8 riceParam = 0; - if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { - if (!drflac__read_uint8(bs, 4, &riceParam)) { - return DRFLAC_FALSE; + ma_uint8 riceParam = 0; + if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { + if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) { + return MA_FALSE; } if (riceParam == 15) { riceParam = 0xFF; } - } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - if (!drflac__read_uint8(bs, 5, &riceParam)) { - return DRFLAC_FALSE; + } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) { + return MA_FALSE; } if (riceParam == 31) { riceParam = 0xFF; } } if (riceParam != 0xFF) { - if (!drflac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__decode_samples_with_residual__rice(bs, bitsPerSample, samplesInPartition, riceParam, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { + return MA_FALSE; } } else { - drflac_uint8 unencodedBitsPerSample = 0; - if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { - return DRFLAC_FALSE; + ma_uint8 unencodedBitsPerSample = 0; + if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) { + return MA_FALSE; } - if (!drflac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__decode_samples_with_residual__unencoded(bs, bitsPerSample, samplesInPartition, unencodedBitsPerSample, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { + return MA_FALSE; } } pDecodedSamples += samplesInPartition; @@ -84524,62 +84696,62 @@ static drflac_bool32 drflac__decode_samples_with_residual(drflac_bs* bs, drflac_ samplesInPartition = blockSize / (1 << partitionOrder); } } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 order) +static ma_bool32 ma_dr_flac__read_and_seek_residual(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 order) { - drflac_uint8 residualMethod; - drflac_uint8 partitionOrder; - drflac_uint32 samplesInPartition; - drflac_uint32 partitionsRemaining; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(blockSize != 0); - if (!drflac__read_uint8(bs, 2, &residualMethod)) { - return DRFLAC_FALSE; + ma_uint8 residualMethod; + ma_uint8 partitionOrder; + ma_uint32 samplesInPartition; + ma_uint32 partitionsRemaining; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(blockSize != 0); + if (!ma_dr_flac__read_uint8(bs, 2, &residualMethod)) { + return MA_FALSE; } - if (residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - return DRFLAC_FALSE; + if (residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE && residualMethod != MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + return MA_FALSE; } - if (!drflac__read_uint8(bs, 4, &partitionOrder)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint8(bs, 4, &partitionOrder)) { + return MA_FALSE; } if (partitionOrder > 8) { - return DRFLAC_FALSE; + return MA_FALSE; } if ((blockSize / (1 << partitionOrder)) <= order) { - return DRFLAC_FALSE; + return MA_FALSE; } samplesInPartition = (blockSize / (1 << partitionOrder)) - order; partitionsRemaining = (1 << partitionOrder); for (;;) { - drflac_uint8 riceParam = 0; - if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { - if (!drflac__read_uint8(bs, 4, &riceParam)) { - return DRFLAC_FALSE; + ma_uint8 riceParam = 0; + if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE) { + if (!ma_dr_flac__read_uint8(bs, 4, &riceParam)) { + return MA_FALSE; } if (riceParam == 15) { riceParam = 0xFF; } - } else if (residualMethod == DRFLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { - if (!drflac__read_uint8(bs, 5, &riceParam)) { - return DRFLAC_FALSE; + } else if (residualMethod == MA_DR_FLAC_RESIDUAL_CODING_METHOD_PARTITIONED_RICE2) { + if (!ma_dr_flac__read_uint8(bs, 5, &riceParam)) { + return MA_FALSE; } if (riceParam == 31) { riceParam = 0xFF; } } if (riceParam != 0xFF) { - if (!drflac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_and_seek_residual__rice(bs, samplesInPartition, riceParam)) { + return MA_FALSE; } } else { - drflac_uint8 unencodedBitsPerSample = 0; - if (!drflac__read_uint8(bs, 5, &unencodedBitsPerSample)) { - return DRFLAC_FALSE; + ma_uint8 unencodedBitsPerSample = 0; + if (!ma_dr_flac__read_uint8(bs, 5, &unencodedBitsPerSample)) { + return MA_FALSE; } - if (!drflac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_bits(bs, unencodedBitsPerSample * samplesInPartition)) { + return MA_FALSE; } } if (partitionsRemaining == 1) { @@ -84588,36 +84760,36 @@ static drflac_bool32 drflac__read_and_seek_residual(drflac_bs* bs, drflac_uint32 partitionsRemaining -= 1; samplesInPartition = blockSize / (1 << partitionOrder); } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples__constant(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples) +static ma_bool32 ma_dr_flac__decode_samples__constant(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples) { - drflac_uint32 i; - drflac_int32 sample; - if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { - return DRFLAC_FALSE; + ma_uint32 i; + ma_int32 sample; + if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { + return MA_FALSE; } for (i = 0; i < blockSize; ++i) { pDecodedSamples[i] = sample; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples__verbatim(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_int32* pDecodedSamples) +static ma_bool32 ma_dr_flac__decode_samples__verbatim(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_int32* pDecodedSamples) { - drflac_uint32 i; + ma_uint32 i; for (i = 0; i < blockSize; ++i) { - drflac_int32 sample; - if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { - return DRFLAC_FALSE; + ma_int32 sample; + if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { + return MA_FALSE; } pDecodedSamples[i] = sample; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 subframeBitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) +static ma_bool32 ma_dr_flac__decode_samples__fixed(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 subframeBitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples) { - drflac_uint32 i; - static drflac_int32 lpcCoefficientsTable[5][4] = { + ma_uint32 i; + static ma_int32 lpcCoefficientsTable[5][4] = { {0, 0, 0, 0}, {1, 0, 0, 0}, {2, -1, 0, 0}, @@ -84625,122 +84797,122 @@ static drflac_bool32 drflac__decode_samples__fixed(drflac_bs* bs, drflac_uint32 {4, -6, 4, -1} }; for (i = 0; i < lpcOrder; ++i) { - drflac_int32 sample; - if (!drflac__read_int32(bs, subframeBitsPerSample, &sample)) { - return DRFLAC_FALSE; + ma_int32 sample; + if (!ma_dr_flac__read_int32(bs, subframeBitsPerSample, &sample)) { + return MA_FALSE; } pDecodedSamples[i] = sample; } - if (!drflac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__decode_samples_with_residual(bs, subframeBitsPerSample, blockSize, lpcOrder, 0, 4, lpcCoefficientsTable[lpcOrder], pDecodedSamples)) { + return MA_FALSE; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_samples__lpc(drflac_bs* bs, drflac_uint32 blockSize, drflac_uint32 bitsPerSample, drflac_uint8 lpcOrder, drflac_int32* pDecodedSamples) +static ma_bool32 ma_dr_flac__decode_samples__lpc(ma_dr_flac_bs* bs, ma_uint32 blockSize, ma_uint32 bitsPerSample, ma_uint8 lpcOrder, ma_int32* pDecodedSamples) { - drflac_uint8 i; - drflac_uint8 lpcPrecision; - drflac_int8 lpcShift; - drflac_int32 coefficients[32]; + ma_uint8 i; + ma_uint8 lpcPrecision; + ma_int8 lpcShift; + ma_int32 coefficients[32]; for (i = 0; i < lpcOrder; ++i) { - drflac_int32 sample; - if (!drflac__read_int32(bs, bitsPerSample, &sample)) { - return DRFLAC_FALSE; + ma_int32 sample; + if (!ma_dr_flac__read_int32(bs, bitsPerSample, &sample)) { + return MA_FALSE; } pDecodedSamples[i] = sample; } - if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) { + return MA_FALSE; } if (lpcPrecision == 15) { - return DRFLAC_FALSE; + return MA_FALSE; } lpcPrecision += 1; - if (!drflac__read_int8(bs, 5, &lpcShift)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_int8(bs, 5, &lpcShift)) { + return MA_FALSE; } if (lpcShift < 0) { - return DRFLAC_FALSE; + return MA_FALSE; } - DRFLAC_ZERO_MEMORY(coefficients, sizeof(coefficients)); + MA_DR_FLAC_ZERO_MEMORY(coefficients, sizeof(coefficients)); for (i = 0; i < lpcOrder; ++i) { - if (!drflac__read_int32(bs, lpcPrecision, coefficients + i)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_int32(bs, lpcPrecision, coefficients + i)) { + return MA_FALSE; } } - if (!drflac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__decode_samples_with_residual(bs, bitsPerSample, blockSize, lpcOrder, lpcShift, lpcPrecision, coefficients, pDecodedSamples)) { + return MA_FALSE; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_uint8 streaminfoBitsPerSample, drflac_frame_header* header) +static ma_bool32 ma_dr_flac__read_next_flac_frame_header(ma_dr_flac_bs* bs, ma_uint8 streaminfoBitsPerSample, ma_dr_flac_frame_header* header) { - const drflac_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; - const drflac_uint8 bitsPerSampleTable[8] = {0, 8, 12, (drflac_uint8)-1, 16, 20, 24, (drflac_uint8)-1}; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(header != NULL); + const ma_uint32 sampleRateTable[12] = {0, 88200, 176400, 192000, 8000, 16000, 22050, 24000, 32000, 44100, 48000, 96000}; + const ma_uint8 bitsPerSampleTable[8] = {0, 8, 12, (ma_uint8)-1, 16, 20, 24, (ma_uint8)-1}; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(header != NULL); for (;;) { - drflac_uint8 crc8 = 0xCE; - drflac_uint8 reserved = 0; - drflac_uint8 blockingStrategy = 0; - drflac_uint8 blockSize = 0; - drflac_uint8 sampleRate = 0; - drflac_uint8 channelAssignment = 0; - drflac_uint8 bitsPerSample = 0; - drflac_bool32 isVariableBlockSize; - if (!drflac__find_and_seek_to_next_sync_code(bs)) { - return DRFLAC_FALSE; + ma_uint8 crc8 = 0xCE; + ma_uint8 reserved = 0; + ma_uint8 blockingStrategy = 0; + ma_uint8 blockSize = 0; + ma_uint8 sampleRate = 0; + ma_uint8 channelAssignment = 0; + ma_uint8 bitsPerSample = 0; + ma_bool32 isVariableBlockSize; + if (!ma_dr_flac__find_and_seek_to_next_sync_code(bs)) { + return MA_FALSE; } - if (!drflac__read_uint8(bs, 1, &reserved)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) { + return MA_FALSE; } if (reserved == 1) { continue; } - crc8 = drflac_crc8(crc8, reserved, 1); - if (!drflac__read_uint8(bs, 1, &blockingStrategy)) { - return DRFLAC_FALSE; + crc8 = ma_dr_flac_crc8(crc8, reserved, 1); + if (!ma_dr_flac__read_uint8(bs, 1, &blockingStrategy)) { + return MA_FALSE; } - crc8 = drflac_crc8(crc8, blockingStrategy, 1); - if (!drflac__read_uint8(bs, 4, &blockSize)) { - return DRFLAC_FALSE; + crc8 = ma_dr_flac_crc8(crc8, blockingStrategy, 1); + if (!ma_dr_flac__read_uint8(bs, 4, &blockSize)) { + return MA_FALSE; } if (blockSize == 0) { continue; } - crc8 = drflac_crc8(crc8, blockSize, 4); - if (!drflac__read_uint8(bs, 4, &sampleRate)) { - return DRFLAC_FALSE; + crc8 = ma_dr_flac_crc8(crc8, blockSize, 4); + if (!ma_dr_flac__read_uint8(bs, 4, &sampleRate)) { + return MA_FALSE; } - crc8 = drflac_crc8(crc8, sampleRate, 4); - if (!drflac__read_uint8(bs, 4, &channelAssignment)) { - return DRFLAC_FALSE; + crc8 = ma_dr_flac_crc8(crc8, sampleRate, 4); + if (!ma_dr_flac__read_uint8(bs, 4, &channelAssignment)) { + return MA_FALSE; } if (channelAssignment > 10) { continue; } - crc8 = drflac_crc8(crc8, channelAssignment, 4); - if (!drflac__read_uint8(bs, 3, &bitsPerSample)) { - return DRFLAC_FALSE; + crc8 = ma_dr_flac_crc8(crc8, channelAssignment, 4); + if (!ma_dr_flac__read_uint8(bs, 3, &bitsPerSample)) { + return MA_FALSE; } if (bitsPerSample == 3 || bitsPerSample == 7) { continue; } - crc8 = drflac_crc8(crc8, bitsPerSample, 3); - if (!drflac__read_uint8(bs, 1, &reserved)) { - return DRFLAC_FALSE; + crc8 = ma_dr_flac_crc8(crc8, bitsPerSample, 3); + if (!ma_dr_flac__read_uint8(bs, 1, &reserved)) { + return MA_FALSE; } if (reserved == 1) { continue; } - crc8 = drflac_crc8(crc8, reserved, 1); + crc8 = ma_dr_flac_crc8(crc8, reserved, 1); isVariableBlockSize = blockingStrategy == 1; if (isVariableBlockSize) { - drflac_uint64 pcmFrameNumber; - drflac_result result = drflac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8); - if (result != DRFLAC_SUCCESS) { - if (result == DRFLAC_AT_END) { - return DRFLAC_FALSE; + ma_uint64 pcmFrameNumber; + ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &pcmFrameNumber, &crc8); + if (result != MA_SUCCESS) { + if (result == MA_AT_END) { + return MA_FALSE; } else { continue; } @@ -84748,61 +84920,61 @@ static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_u header->flacFrameNumber = 0; header->pcmFrameNumber = pcmFrameNumber; } else { - drflac_uint64 flacFrameNumber = 0; - drflac_result result = drflac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8); - if (result != DRFLAC_SUCCESS) { - if (result == DRFLAC_AT_END) { - return DRFLAC_FALSE; + ma_uint64 flacFrameNumber = 0; + ma_result result = ma_dr_flac__read_utf8_coded_number(bs, &flacFrameNumber, &crc8); + if (result != MA_SUCCESS) { + if (result == MA_AT_END) { + return MA_FALSE; } else { continue; } } - header->flacFrameNumber = (drflac_uint32)flacFrameNumber; + header->flacFrameNumber = (ma_uint32)flacFrameNumber; header->pcmFrameNumber = 0; } - DRFLAC_ASSERT(blockSize > 0); + MA_DR_FLAC_ASSERT(blockSize > 0); if (blockSize == 1) { header->blockSizeInPCMFrames = 192; } else if (blockSize <= 5) { - DRFLAC_ASSERT(blockSize >= 2); + MA_DR_FLAC_ASSERT(blockSize >= 2); header->blockSizeInPCMFrames = 576 * (1 << (blockSize - 2)); } else if (blockSize == 6) { - if (!drflac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint16(bs, 8, &header->blockSizeInPCMFrames)) { + return MA_FALSE; } - crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 8); + crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 8); header->blockSizeInPCMFrames += 1; } else if (blockSize == 7) { - if (!drflac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint16(bs, 16, &header->blockSizeInPCMFrames)) { + return MA_FALSE; } - crc8 = drflac_crc8(crc8, header->blockSizeInPCMFrames, 16); + crc8 = ma_dr_flac_crc8(crc8, header->blockSizeInPCMFrames, 16); if (header->blockSizeInPCMFrames == 0xFFFF) { - return DRFLAC_FALSE; + return MA_FALSE; } header->blockSizeInPCMFrames += 1; } else { - DRFLAC_ASSERT(blockSize >= 8); + MA_DR_FLAC_ASSERT(blockSize >= 8); header->blockSizeInPCMFrames = 256 * (1 << (blockSize - 8)); } if (sampleRate <= 11) { header->sampleRate = sampleRateTable[sampleRate]; } else if (sampleRate == 12) { - if (!drflac__read_uint32(bs, 8, &header->sampleRate)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint32(bs, 8, &header->sampleRate)) { + return MA_FALSE; } - crc8 = drflac_crc8(crc8, header->sampleRate, 8); + crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 8); header->sampleRate *= 1000; } else if (sampleRate == 13) { - if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) { + return MA_FALSE; } - crc8 = drflac_crc8(crc8, header->sampleRate, 16); + crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16); } else if (sampleRate == 14) { - if (!drflac__read_uint32(bs, 16, &header->sampleRate)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint32(bs, 16, &header->sampleRate)) { + return MA_FALSE; } - crc8 = drflac_crc8(crc8, header->sampleRate, 16); + crc8 = ma_dr_flac_crc8(crc8, header->sampleRate, 16); header->sampleRate *= 10; } else { continue; @@ -84813,286 +84985,286 @@ static drflac_bool32 drflac__read_next_flac_frame_header(drflac_bs* bs, drflac_u header->bitsPerSample = streaminfoBitsPerSample; } if (header->bitsPerSample != streaminfoBitsPerSample) { - return DRFLAC_FALSE; + return MA_FALSE; } - if (!drflac__read_uint8(bs, 8, &header->crc8)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint8(bs, 8, &header->crc8)) { + return MA_FALSE; } -#ifndef DR_FLAC_NO_CRC +#ifndef MA_DR_FLAC_NO_CRC if (header->crc8 != crc8) { continue; } #endif - return DRFLAC_TRUE; + return MA_TRUE; } } -static drflac_bool32 drflac__read_subframe_header(drflac_bs* bs, drflac_subframe* pSubframe) +static ma_bool32 ma_dr_flac__read_subframe_header(ma_dr_flac_bs* bs, ma_dr_flac_subframe* pSubframe) { - drflac_uint8 header; + ma_uint8 header; int type; - if (!drflac__read_uint8(bs, 8, &header)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint8(bs, 8, &header)) { + return MA_FALSE; } if ((header & 0x80) != 0) { - return DRFLAC_FALSE; + return MA_FALSE; } type = (header & 0x7E) >> 1; if (type == 0) { - pSubframe->subframeType = DRFLAC_SUBFRAME_CONSTANT; + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_CONSTANT; } else if (type == 1) { - pSubframe->subframeType = DRFLAC_SUBFRAME_VERBATIM; + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_VERBATIM; } else { if ((type & 0x20) != 0) { - pSubframe->subframeType = DRFLAC_SUBFRAME_LPC; - pSubframe->lpcOrder = (drflac_uint8)(type & 0x1F) + 1; + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_LPC; + pSubframe->lpcOrder = (ma_uint8)(type & 0x1F) + 1; } else if ((type & 0x08) != 0) { - pSubframe->subframeType = DRFLAC_SUBFRAME_FIXED; - pSubframe->lpcOrder = (drflac_uint8)(type & 0x07); + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_FIXED; + pSubframe->lpcOrder = (ma_uint8)(type & 0x07); if (pSubframe->lpcOrder > 4) { - pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED; pSubframe->lpcOrder = 0; } } else { - pSubframe->subframeType = DRFLAC_SUBFRAME_RESERVED; + pSubframe->subframeType = MA_DR_FLAC_SUBFRAME_RESERVED; } } - if (pSubframe->subframeType == DRFLAC_SUBFRAME_RESERVED) { - return DRFLAC_FALSE; + if (pSubframe->subframeType == MA_DR_FLAC_SUBFRAME_RESERVED) { + return MA_FALSE; } pSubframe->wastedBitsPerSample = 0; if ((header & 0x01) == 1) { unsigned int wastedBitsPerSample; - if (!drflac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_past_next_set_bit(bs, &wastedBitsPerSample)) { + return MA_FALSE; } - pSubframe->wastedBitsPerSample = (drflac_uint8)wastedBitsPerSample + 1; + pSubframe->wastedBitsPerSample = (ma_uint8)wastedBitsPerSample + 1; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex, drflac_int32* pDecodedSamplesOut) +static ma_bool32 ma_dr_flac__decode_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex, ma_int32* pDecodedSamplesOut) { - drflac_subframe* pSubframe; - drflac_uint32 subframeBitsPerSample; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(frame != NULL); + ma_dr_flac_subframe* pSubframe; + ma_uint32 subframeBitsPerSample; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(frame != NULL); pSubframe = frame->subframes + subframeIndex; - if (!drflac__read_subframe_header(bs, pSubframe)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) { + return MA_FALSE; } subframeBitsPerSample = frame->header.bitsPerSample; - if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { subframeBitsPerSample += 1; - } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { subframeBitsPerSample += 1; } if (subframeBitsPerSample > 32) { - return DRFLAC_FALSE; + return MA_FALSE; } if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { - return DRFLAC_FALSE; + return MA_FALSE; } subframeBitsPerSample -= pSubframe->wastedBitsPerSample; pSubframe->pSamplesS32 = pDecodedSamplesOut; switch (pSubframe->subframeType) { - case DRFLAC_SUBFRAME_CONSTANT: + case MA_DR_FLAC_SUBFRAME_CONSTANT: { - drflac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); + ma_dr_flac__decode_samples__constant(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); } break; - case DRFLAC_SUBFRAME_VERBATIM: + case MA_DR_FLAC_SUBFRAME_VERBATIM: { - drflac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); + ma_dr_flac__decode_samples__verbatim(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->pSamplesS32); } break; - case DRFLAC_SUBFRAME_FIXED: + case MA_DR_FLAC_SUBFRAME_FIXED: { - drflac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); + ma_dr_flac__decode_samples__fixed(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); } break; - case DRFLAC_SUBFRAME_LPC: + case MA_DR_FLAC_SUBFRAME_LPC: { - drflac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); + ma_dr_flac__decode_samples__lpc(bs, frame->header.blockSizeInPCMFrames, subframeBitsPerSample, pSubframe->lpcOrder, pSubframe->pSamplesS32); } break; - default: return DRFLAC_FALSE; + default: return MA_FALSE; } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__seek_subframe(drflac_bs* bs, drflac_frame* frame, int subframeIndex) +static ma_bool32 ma_dr_flac__seek_subframe(ma_dr_flac_bs* bs, ma_dr_flac_frame* frame, int subframeIndex) { - drflac_subframe* pSubframe; - drflac_uint32 subframeBitsPerSample; - DRFLAC_ASSERT(bs != NULL); - DRFLAC_ASSERT(frame != NULL); + ma_dr_flac_subframe* pSubframe; + ma_uint32 subframeBitsPerSample; + MA_DR_FLAC_ASSERT(bs != NULL); + MA_DR_FLAC_ASSERT(frame != NULL); pSubframe = frame->subframes + subframeIndex; - if (!drflac__read_subframe_header(bs, pSubframe)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_subframe_header(bs, pSubframe)) { + return MA_FALSE; } subframeBitsPerSample = frame->header.bitsPerSample; - if ((frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { + if ((frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE || frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE) && subframeIndex == 1) { subframeBitsPerSample += 1; - } else if (frame->header.channelAssignment == DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { + } else if (frame->header.channelAssignment == MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE && subframeIndex == 0) { subframeBitsPerSample += 1; } if (pSubframe->wastedBitsPerSample >= subframeBitsPerSample) { - return DRFLAC_FALSE; + return MA_FALSE; } subframeBitsPerSample -= pSubframe->wastedBitsPerSample; pSubframe->pSamplesS32 = NULL; switch (pSubframe->subframeType) { - case DRFLAC_SUBFRAME_CONSTANT: + case MA_DR_FLAC_SUBFRAME_CONSTANT: { - if (!drflac__seek_bits(bs, subframeBitsPerSample)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_bits(bs, subframeBitsPerSample)) { + return MA_FALSE; } } break; - case DRFLAC_SUBFRAME_VERBATIM: + case MA_DR_FLAC_SUBFRAME_VERBATIM: { unsigned int bitsToSeek = frame->header.blockSizeInPCMFrames * subframeBitsPerSample; - if (!drflac__seek_bits(bs, bitsToSeek)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { + return MA_FALSE; } } break; - case DRFLAC_SUBFRAME_FIXED: + case MA_DR_FLAC_SUBFRAME_FIXED: { unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; - if (!drflac__seek_bits(bs, bitsToSeek)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { + return MA_FALSE; } - if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { + return MA_FALSE; } } break; - case DRFLAC_SUBFRAME_LPC: + case MA_DR_FLAC_SUBFRAME_LPC: { - drflac_uint8 lpcPrecision; + ma_uint8 lpcPrecision; unsigned int bitsToSeek = pSubframe->lpcOrder * subframeBitsPerSample; - if (!drflac__seek_bits(bs, bitsToSeek)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { + return MA_FALSE; } - if (!drflac__read_uint8(bs, 4, &lpcPrecision)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_uint8(bs, 4, &lpcPrecision)) { + return MA_FALSE; } if (lpcPrecision == 15) { - return DRFLAC_FALSE; + return MA_FALSE; } lpcPrecision += 1; bitsToSeek = (pSubframe->lpcOrder * lpcPrecision) + 5; - if (!drflac__seek_bits(bs, bitsToSeek)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_bits(bs, bitsToSeek)) { + return MA_FALSE; } - if (!drflac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_and_seek_residual(bs, frame->header.blockSizeInPCMFrames, pSubframe->lpcOrder)) { + return MA_FALSE; } } break; - default: return DRFLAC_FALSE; + default: return MA_FALSE; } - return DRFLAC_TRUE; + return MA_TRUE; } -static DRFLAC_INLINE drflac_uint8 drflac__get_channel_count_from_channel_assignment(drflac_int8 channelAssignment) +static MA_INLINE ma_uint8 ma_dr_flac__get_channel_count_from_channel_assignment(ma_int8 channelAssignment) { - drflac_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; - DRFLAC_ASSERT(channelAssignment <= 10); + ma_uint8 lookup[] = {1, 2, 3, 4, 5, 6, 7, 8, 2, 2, 2}; + MA_DR_FLAC_ASSERT(channelAssignment <= 10); return lookup[channelAssignment]; } -static drflac_result drflac__decode_flac_frame(drflac* pFlac) +static ma_result ma_dr_flac__decode_flac_frame(ma_dr_flac* pFlac) { int channelCount; int i; - drflac_uint8 paddingSizeInBits; - drflac_uint16 desiredCRC16; -#ifndef DR_FLAC_NO_CRC - drflac_uint16 actualCRC16; + ma_uint8 paddingSizeInBits; + ma_uint16 desiredCRC16; +#ifndef MA_DR_FLAC_NO_CRC + ma_uint16 actualCRC16; #endif - DRFLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes)); + MA_DR_FLAC_ZERO_MEMORY(pFlac->currentFLACFrame.subframes, sizeof(pFlac->currentFLACFrame.subframes)); if (pFlac->currentFLACFrame.header.blockSizeInPCMFrames > pFlac->maxBlockSizeInPCMFrames) { - return DRFLAC_ERROR; + return MA_ERROR; } - channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); if (channelCount != (int)pFlac->channels) { - return DRFLAC_ERROR; + return MA_ERROR; } for (i = 0; i < channelCount; ++i) { - if (!drflac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) { - return DRFLAC_ERROR; + if (!ma_dr_flac__decode_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i, pFlac->pDecodedSamples + (pFlac->currentFLACFrame.header.blockSizeInPCMFrames * i))) { + return MA_ERROR; } } - paddingSizeInBits = (drflac_uint8)(DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7); + paddingSizeInBits = (ma_uint8)(MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7); if (paddingSizeInBits > 0) { - drflac_uint8 padding = 0; - if (!drflac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { - return DRFLAC_AT_END; + ma_uint8 padding = 0; + if (!ma_dr_flac__read_uint8(&pFlac->bs, paddingSizeInBits, &padding)) { + return MA_AT_END; } } -#ifndef DR_FLAC_NO_CRC - actualCRC16 = drflac__flush_crc16(&pFlac->bs); +#ifndef MA_DR_FLAC_NO_CRC + actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs); #endif - if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { - return DRFLAC_AT_END; + if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { + return MA_AT_END; } -#ifndef DR_FLAC_NO_CRC +#ifndef MA_DR_FLAC_NO_CRC if (actualCRC16 != desiredCRC16) { - return DRFLAC_CRC_MISMATCH; + return MA_CRC_MISMATCH; } #endif pFlac->currentFLACFrame.pcmFramesRemaining = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; - return DRFLAC_SUCCESS; + return MA_SUCCESS; } -static drflac_result drflac__seek_flac_frame(drflac* pFlac) +static ma_result ma_dr_flac__seek_flac_frame(ma_dr_flac* pFlac) { int channelCount; int i; - drflac_uint16 desiredCRC16; -#ifndef DR_FLAC_NO_CRC - drflac_uint16 actualCRC16; + ma_uint16 desiredCRC16; +#ifndef MA_DR_FLAC_NO_CRC + ma_uint16 actualCRC16; #endif - channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); for (i = 0; i < channelCount; ++i) { - if (!drflac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) { - return DRFLAC_ERROR; + if (!ma_dr_flac__seek_subframe(&pFlac->bs, &pFlac->currentFLACFrame, i)) { + return MA_ERROR; } } - if (!drflac__seek_bits(&pFlac->bs, DRFLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { - return DRFLAC_ERROR; + if (!ma_dr_flac__seek_bits(&pFlac->bs, MA_DR_FLAC_CACHE_L1_BITS_REMAINING(&pFlac->bs) & 7)) { + return MA_ERROR; } -#ifndef DR_FLAC_NO_CRC - actualCRC16 = drflac__flush_crc16(&pFlac->bs); +#ifndef MA_DR_FLAC_NO_CRC + actualCRC16 = ma_dr_flac__flush_crc16(&pFlac->bs); #endif - if (!drflac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { - return DRFLAC_AT_END; + if (!ma_dr_flac__read_uint16(&pFlac->bs, 16, &desiredCRC16)) { + return MA_AT_END; } -#ifndef DR_FLAC_NO_CRC +#ifndef MA_DR_FLAC_NO_CRC if (actualCRC16 != desiredCRC16) { - return DRFLAC_CRC_MISMATCH; + return MA_CRC_MISMATCH; } #endif - return DRFLAC_SUCCESS; + return MA_SUCCESS; } -static drflac_bool32 drflac__read_and_decode_next_flac_frame(drflac* pFlac) +static ma_bool32 ma_dr_flac__read_and_decode_next_flac_frame(ma_dr_flac* pFlac) { - DRFLAC_ASSERT(pFlac != NULL); + MA_DR_FLAC_ASSERT(pFlac != NULL); for (;;) { - drflac_result result; - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; + ma_result result; + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; } - result = drflac__decode_flac_frame(pFlac); - if (result != DRFLAC_SUCCESS) { - if (result == DRFLAC_CRC_MISMATCH) { + result = ma_dr_flac__decode_flac_frame(pFlac); + if (result != MA_SUCCESS) { + if (result == MA_CRC_MISMATCH) { continue; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } - return DRFLAC_TRUE; + return MA_TRUE; } } -static void drflac__get_pcm_frame_range_of_current_flac_frame(drflac* pFlac, drflac_uint64* pFirstPCMFrame, drflac_uint64* pLastPCMFrame) +static void ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(ma_dr_flac* pFlac, ma_uint64* pFirstPCMFrame, ma_uint64* pLastPCMFrame) { - drflac_uint64 firstPCMFrame; - drflac_uint64 lastPCMFrame; - DRFLAC_ASSERT(pFlac != NULL); + ma_uint64 firstPCMFrame; + ma_uint64 lastPCMFrame; + MA_DR_FLAC_ASSERT(pFlac != NULL); firstPCMFrame = pFlac->currentFLACFrame.header.pcmFrameNumber; if (firstPCMFrame == 0) { - firstPCMFrame = ((drflac_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames; + firstPCMFrame = ((ma_uint64)pFlac->currentFLACFrame.header.flacFrameNumber) * pFlac->maxBlockSizeInPCMFrames; } lastPCMFrame = firstPCMFrame + pFlac->currentFLACFrame.header.blockSizeInPCMFrames; if (lastPCMFrame > 0) { @@ -85105,32 +85277,32 @@ static void drflac__get_pcm_frame_range_of_current_flac_frame(drflac* pFlac, drf *pLastPCMFrame = lastPCMFrame; } } -static drflac_bool32 drflac__seek_to_first_frame(drflac* pFlac) +static ma_bool32 ma_dr_flac__seek_to_first_frame(ma_dr_flac* pFlac) { - drflac_bool32 result; - DRFLAC_ASSERT(pFlac != NULL); - result = drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes); - DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); + ma_bool32 result; + MA_DR_FLAC_ASSERT(pFlac != NULL); + result = ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes); + MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); pFlac->currentPCMFrame = 0; return result; } -static DRFLAC_INLINE drflac_result drflac__seek_to_next_flac_frame(drflac* pFlac) +static MA_INLINE ma_result ma_dr_flac__seek_to_next_flac_frame(ma_dr_flac* pFlac) { - DRFLAC_ASSERT(pFlac != NULL); - return drflac__seek_flac_frame(pFlac); + MA_DR_FLAC_ASSERT(pFlac != NULL); + return ma_dr_flac__seek_flac_frame(pFlac); } -static drflac_uint64 drflac__seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 pcmFramesToSeek) +static ma_uint64 ma_dr_flac__seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 pcmFramesToSeek) { - drflac_uint64 pcmFramesRead = 0; + ma_uint64 pcmFramesRead = 0; while (pcmFramesToSeek > 0) { if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!drflac__read_and_decode_next_flac_frame(pFlac)) { + if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { break; } } else { if (pFlac->currentFLACFrame.pcmFramesRemaining > pcmFramesToSeek) { pcmFramesRead += pcmFramesToSeek; - pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)pcmFramesToSeek; + pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)pcmFramesToSeek; pcmFramesToSeek = 0; } else { pcmFramesRead += pFlac->currentFLACFrame.pcmFramesRemaining; @@ -85142,107 +85314,107 @@ static drflac_uint64 drflac__seek_forward_by_pcm_frames(drflac* pFlac, drflac_ui pFlac->currentPCMFrame += pcmFramesRead; return pcmFramesRead; } -static drflac_bool32 drflac__seek_to_pcm_frame__brute_force(drflac* pFlac, drflac_uint64 pcmFrameIndex) +static ma_bool32 ma_dr_flac__seek_to_pcm_frame__brute_force(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) { - drflac_bool32 isMidFrame = DRFLAC_FALSE; - drflac_uint64 runningPCMFrameCount; - DRFLAC_ASSERT(pFlac != NULL); + ma_bool32 isMidFrame = MA_FALSE; + ma_uint64 runningPCMFrameCount; + MA_DR_FLAC_ASSERT(pFlac != NULL); if (pcmFrameIndex >= pFlac->currentPCMFrame) { runningPCMFrameCount = pFlac->currentPCMFrame; if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; } } else { - isMidFrame = DRFLAC_TRUE; + isMidFrame = MA_TRUE; } } else { runningPCMFrameCount = 0; - if (!drflac__seek_to_first_frame(pFlac)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_to_first_frame(pFlac)) { + return MA_FALSE; } - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; } } for (;;) { - drflac_uint64 pcmFrameCountInThisFLACFrame; - drflac_uint64 firstPCMFrameInFLACFrame = 0; - drflac_uint64 lastPCMFrameInFLACFrame = 0; - drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); + ma_uint64 pcmFrameCountInThisFLACFrame; + ma_uint64 firstPCMFrameInFLACFrame = 0; + ma_uint64 lastPCMFrameInFLACFrame = 0; + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { - drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; + ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; if (!isMidFrame) { - drflac_result result = drflac__decode_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + ma_result result = ma_dr_flac__decode_flac_frame(pFlac); + if (result == MA_SUCCESS) { + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } else { - if (result == DRFLAC_CRC_MISMATCH) { + if (result == MA_CRC_MISMATCH) { goto next_iteration; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } } else { - return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } } else { if (!isMidFrame) { - drflac_result result = drflac__seek_to_next_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { + ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); + if (result == MA_SUCCESS) { runningPCMFrameCount += pcmFrameCountInThisFLACFrame; } else { - if (result == DRFLAC_CRC_MISMATCH) { + if (result == MA_CRC_MISMATCH) { goto next_iteration; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } } else { runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; pFlac->currentFLACFrame.pcmFramesRemaining = 0; - isMidFrame = DRFLAC_FALSE; + isMidFrame = MA_FALSE; } if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { - return DRFLAC_TRUE; + return MA_TRUE; } } next_iteration: - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; } } } -#if !defined(DR_FLAC_NO_CRC) -#define DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f -static drflac_bool32 drflac__seek_to_approximate_flac_frame_to_byte(drflac* pFlac, drflac_uint64 targetByte, drflac_uint64 rangeLo, drflac_uint64 rangeHi, drflac_uint64* pLastSuccessfulSeekOffset) +#if !defined(MA_DR_FLAC_NO_CRC) +#define MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO 0.6f +static ma_bool32 ma_dr_flac__seek_to_approximate_flac_frame_to_byte(ma_dr_flac* pFlac, ma_uint64 targetByte, ma_uint64 rangeLo, ma_uint64 rangeHi, ma_uint64* pLastSuccessfulSeekOffset) { - DRFLAC_ASSERT(pFlac != NULL); - DRFLAC_ASSERT(pLastSuccessfulSeekOffset != NULL); - DRFLAC_ASSERT(targetByte >= rangeLo); - DRFLAC_ASSERT(targetByte <= rangeHi); + MA_DR_FLAC_ASSERT(pFlac != NULL); + MA_DR_FLAC_ASSERT(pLastSuccessfulSeekOffset != NULL); + MA_DR_FLAC_ASSERT(targetByte >= rangeLo); + MA_DR_FLAC_ASSERT(targetByte <= rangeHi); *pLastSuccessfulSeekOffset = pFlac->firstFLACFramePosInBytes; for (;;) { - drflac_uint64 lastTargetByte = targetByte; - if (!drflac__seek_to_byte(&pFlac->bs, targetByte)) { + ma_uint64 lastTargetByte = targetByte; + if (!ma_dr_flac__seek_to_byte(&pFlac->bs, targetByte)) { if (targetByte == 0) { - drflac__seek_to_first_frame(pFlac); - return DRFLAC_FALSE; + ma_dr_flac__seek_to_first_frame(pFlac); + return MA_FALSE; } targetByte = rangeLo + ((rangeHi - rangeLo)/2); rangeHi = targetByte; } else { - DRFLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); + MA_DR_FLAC_ZERO_MEMORY(&pFlac->currentFLACFrame, sizeof(pFlac->currentFLACFrame)); #if 1 - if (!drflac__read_and_decode_next_flac_frame(pFlac)) { + if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { targetByte = rangeLo + ((rangeHi - rangeLo)/2); rangeHi = targetByte; } else { break; } #else - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { targetByte = rangeLo + ((rangeHi - rangeLo)/2); rangeHi = targetByte; } else { @@ -85251,48 +85423,48 @@ static drflac_bool32 drflac__seek_to_approximate_flac_frame_to_byte(drflac* pFla #endif } if(targetByte == lastTargetByte) { - return DRFLAC_FALSE; + return MA_FALSE; } } - drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); - DRFLAC_ASSERT(targetByte <= rangeHi); + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); + MA_DR_FLAC_ASSERT(targetByte <= rangeHi); *pLastSuccessfulSeekOffset = targetByte; - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(drflac* pFlac, drflac_uint64 offset) +static ma_bool32 ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(ma_dr_flac* pFlac, ma_uint64 offset) { #if 0 - if (drflac__decode_flac_frame(pFlac) != DRFLAC_SUCCESS) { - if (drflac__read_and_decode_next_flac_frame(pFlac) == DRFLAC_FALSE) { - return DRFLAC_FALSE; + if (ma_dr_flac__decode_flac_frame(pFlac) != MA_SUCCESS) { + if (ma_dr_flac__read_and_decode_next_flac_frame(pFlac) == MA_FALSE) { + return MA_FALSE; } } #endif - return drflac__seek_forward_by_pcm_frames(pFlac, offset) == offset; + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, offset) == offset; } -static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* pFlac, drflac_uint64 pcmFrameIndex, drflac_uint64 byteRangeLo, drflac_uint64 byteRangeHi) +static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search_internal(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex, ma_uint64 byteRangeLo, ma_uint64 byteRangeHi) { - drflac_uint64 targetByte; - drflac_uint64 pcmRangeLo = pFlac->totalPCMFrameCount; - drflac_uint64 pcmRangeHi = 0; - drflac_uint64 lastSuccessfulSeekOffset = (drflac_uint64)-1; - drflac_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo; - drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; - targetByte = byteRangeLo + (drflac_uint64)(((drflac_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * DRFLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO); + ma_uint64 targetByte; + ma_uint64 pcmRangeLo = pFlac->totalPCMFrameCount; + ma_uint64 pcmRangeHi = 0; + ma_uint64 lastSuccessfulSeekOffset = (ma_uint64)-1; + ma_uint64 closestSeekOffsetBeforeTargetPCMFrame = byteRangeLo; + ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; + targetByte = byteRangeLo + (ma_uint64)(((ma_int64)((pcmFrameIndex - pFlac->currentPCMFrame) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * MA_DR_FLAC_BINARY_SEARCH_APPROX_COMPRESSION_RATIO); if (targetByte > byteRangeHi) { targetByte = byteRangeHi; } for (;;) { - if (drflac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) { - drflac_uint64 newPCMRangeLo; - drflac_uint64 newPCMRangeHi; - drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi); + if (ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, targetByte, byteRangeLo, byteRangeHi, &lastSuccessfulSeekOffset)) { + ma_uint64 newPCMRangeLo; + ma_uint64 newPCMRangeHi; + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &newPCMRangeLo, &newPCMRangeHi); if (pcmRangeLo == newPCMRangeLo) { - if (!drflac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) { + if (!ma_dr_flac__seek_to_approximate_flac_frame_to_byte(pFlac, closestSeekOffsetBeforeTargetPCMFrame, closestSeekOffsetBeforeTargetPCMFrame, byteRangeHi, &lastSuccessfulSeekOffset)) { break; } - if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { - return DRFLAC_TRUE; + if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { + return MA_TRUE; } else { break; } @@ -85300,13 +85472,13 @@ static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* p pcmRangeLo = newPCMRangeLo; pcmRangeHi = newPCMRangeHi; if (pcmRangeLo <= pcmFrameIndex && pcmRangeHi >= pcmFrameIndex) { - if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) { - return DRFLAC_TRUE; + if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame) ) { + return MA_TRUE; } else { break; } } else { - const float approxCompressionRatio = (drflac_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((drflac_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f); + const float approxCompressionRatio = (ma_int64)(lastSuccessfulSeekOffset - pFlac->firstFLACFramePosInBytes) / ((ma_int64)(pcmRangeLo * pFlac->channels * pFlac->bitsPerSample)/8.0f); if (pcmRangeLo > pcmFrameIndex) { byteRangeHi = lastSuccessfulSeekOffset; if (byteRangeLo > byteRangeHi) { @@ -85318,8 +85490,8 @@ static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* p } } else { if ((pcmFrameIndex - pcmRangeLo) < seekForwardThreshold) { - if (drflac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { - return DRFLAC_TRUE; + if (ma_dr_flac__decode_flac_frame_and_seek_forward_by_pcm_frames(pFlac, pcmFrameIndex - pFlac->currentPCMFrame)) { + return MA_TRUE; } else { break; } @@ -85328,7 +85500,7 @@ static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* p if (byteRangeHi < byteRangeLo) { byteRangeHi = byteRangeLo; } - targetByte = lastSuccessfulSeekOffset + (drflac_uint64)(((drflac_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio); + targetByte = lastSuccessfulSeekOffset + (ma_uint64)(((ma_int64)((pcmFrameIndex-pcmRangeLo) * pFlac->channels * pFlac->bitsPerSample)/8.0f) * approxCompressionRatio); if (targetByte > byteRangeHi) { targetByte = byteRangeHi; } @@ -85342,37 +85514,37 @@ static drflac_bool32 drflac__seek_to_pcm_frame__binary_search_internal(drflac* p break; } } - drflac__seek_to_first_frame(pFlac); - return DRFLAC_FALSE; + ma_dr_flac__seek_to_first_frame(pFlac); + return MA_FALSE; } -static drflac_bool32 drflac__seek_to_pcm_frame__binary_search(drflac* pFlac, drflac_uint64 pcmFrameIndex) +static ma_bool32 ma_dr_flac__seek_to_pcm_frame__binary_search(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) { - drflac_uint64 byteRangeLo; - drflac_uint64 byteRangeHi; - drflac_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; - if (drflac__seek_to_first_frame(pFlac) == DRFLAC_FALSE) { - return DRFLAC_FALSE; + ma_uint64 byteRangeLo; + ma_uint64 byteRangeHi; + ma_uint32 seekForwardThreshold = (pFlac->maxBlockSizeInPCMFrames != 0) ? pFlac->maxBlockSizeInPCMFrames*2 : 4096; + if (ma_dr_flac__seek_to_first_frame(pFlac) == MA_FALSE) { + return MA_FALSE; } if (pcmFrameIndex < seekForwardThreshold) { - return drflac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex; + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFrameIndex) == pcmFrameIndex; } byteRangeLo = pFlac->firstFLACFramePosInBytes; - byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); - return drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi); + byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); + return ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi); } #endif -static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac_uint64 pcmFrameIndex) +static ma_bool32 ma_dr_flac__seek_to_pcm_frame__seek_table(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) { - drflac_uint32 iClosestSeekpoint = 0; - drflac_bool32 isMidFrame = DRFLAC_FALSE; - drflac_uint64 runningPCMFrameCount; - drflac_uint32 iSeekpoint; - DRFLAC_ASSERT(pFlac != NULL); + ma_uint32 iClosestSeekpoint = 0; + ma_bool32 isMidFrame = MA_FALSE; + ma_uint64 runningPCMFrameCount; + ma_uint32 iSeekpoint; + MA_DR_FLAC_ASSERT(pFlac != NULL); if (pFlac->pSeekpoints == NULL || pFlac->seekpointCount == 0) { - return DRFLAC_FALSE; + return MA_FALSE; } if (pFlac->pSeekpoints[0].firstPCMFrame > pcmFrameIndex) { - return DRFLAC_FALSE; + return MA_FALSE; } for (iSeekpoint = 0; iSeekpoint < pFlac->seekpointCount; ++iSeekpoint) { if (pFlac->pSeekpoints[iSeekpoint].firstPCMFrame >= pcmFrameIndex) { @@ -85381,31 +85553,31 @@ static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac iClosestSeekpoint = iSeekpoint; } if (pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount == 0 || pFlac->pSeekpoints[iClosestSeekpoint].pcmFrameCount > pFlac->maxBlockSizeInPCMFrames) { - return DRFLAC_FALSE; + return MA_FALSE; } if (pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame > pFlac->totalPCMFrameCount && pFlac->totalPCMFrameCount > 0) { - return DRFLAC_FALSE; + return MA_FALSE; } -#if !defined(DR_FLAC_NO_CRC) +#if !defined(MA_DR_FLAC_NO_CRC) if (pFlac->totalPCMFrameCount > 0) { - drflac_uint64 byteRangeLo; - drflac_uint64 byteRangeHi; - byteRangeHi = pFlac->firstFLACFramePosInBytes + (drflac_uint64)((drflac_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); + ma_uint64 byteRangeLo; + ma_uint64 byteRangeHi; + byteRangeHi = pFlac->firstFLACFramePosInBytes + (ma_uint64)((ma_int64)(pFlac->totalPCMFrameCount * pFlac->channels * pFlac->bitsPerSample)/8.0f); byteRangeLo = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset; if (iClosestSeekpoint < pFlac->seekpointCount-1) { - drflac_uint32 iNextSeekpoint = iClosestSeekpoint + 1; + ma_uint32 iNextSeekpoint = iClosestSeekpoint + 1; if (pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset >= pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset || pFlac->pSeekpoints[iNextSeekpoint].pcmFrameCount == 0) { - return DRFLAC_FALSE; + return MA_FALSE; } - if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((drflac_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { + if (pFlac->pSeekpoints[iNextSeekpoint].firstPCMFrame != (((ma_uint64)0xFFFFFFFF << 32) | 0xFFFFFFFF)) { byteRangeHi = pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iNextSeekpoint].flacFrameOffset - 1; } } - if (drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { - if (drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); - if (drflac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) { - return DRFLAC_TRUE; + if (ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { + if (ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &pFlac->currentPCMFrame, NULL); + if (ma_dr_flac__seek_to_pcm_frame__binary_search_internal(pFlac, pcmFrameIndex, byteRangeLo, byteRangeHi)) { + return MA_TRUE; } } } @@ -85414,173 +85586,173 @@ static drflac_bool32 drflac__seek_to_pcm_frame__seek_table(drflac* pFlac, drflac if (pcmFrameIndex >= pFlac->currentPCMFrame && pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame <= pFlac->currentPCMFrame) { runningPCMFrameCount = pFlac->currentPCMFrame; if (pFlac->currentPCMFrame == 0 && pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; } } else { - isMidFrame = DRFLAC_TRUE; + isMidFrame = MA_TRUE; } } else { runningPCMFrameCount = pFlac->pSeekpoints[iClosestSeekpoint].firstPCMFrame; - if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes + pFlac->pSeekpoints[iClosestSeekpoint].flacFrameOffset)) { + return MA_FALSE; } - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; } } for (;;) { - drflac_uint64 pcmFrameCountInThisFLACFrame; - drflac_uint64 firstPCMFrameInFLACFrame = 0; - drflac_uint64 lastPCMFrameInFLACFrame = 0; - drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); + ma_uint64 pcmFrameCountInThisFLACFrame; + ma_uint64 firstPCMFrameInFLACFrame = 0; + ma_uint64 lastPCMFrameInFLACFrame = 0; + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); pcmFrameCountInThisFLACFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFLACFrame)) { - drflac_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; + ma_uint64 pcmFramesToDecode = pcmFrameIndex - runningPCMFrameCount; if (!isMidFrame) { - drflac_result result = drflac__decode_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + ma_result result = ma_dr_flac__decode_flac_frame(pFlac); + if (result == MA_SUCCESS) { + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } else { - if (result == DRFLAC_CRC_MISMATCH) { + if (result == MA_CRC_MISMATCH) { goto next_iteration; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } } else { - return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } } else { if (!isMidFrame) { - drflac_result result = drflac__seek_to_next_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { + ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); + if (result == MA_SUCCESS) { runningPCMFrameCount += pcmFrameCountInThisFLACFrame; } else { - if (result == DRFLAC_CRC_MISMATCH) { + if (result == MA_CRC_MISMATCH) { goto next_iteration; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } } else { runningPCMFrameCount += pFlac->currentFLACFrame.pcmFramesRemaining; pFlac->currentFLACFrame.pcmFramesRemaining = 0; - isMidFrame = DRFLAC_FALSE; + isMidFrame = MA_FALSE; } if (pcmFrameIndex == pFlac->totalPCMFrameCount && runningPCMFrameCount == pFlac->totalPCMFrameCount) { - return DRFLAC_TRUE; + return MA_TRUE; } } next_iteration: - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; } } } -#ifndef DR_FLAC_NO_OGG +#ifndef MA_DR_FLAC_NO_OGG typedef struct { - drflac_uint8 capturePattern[4]; - drflac_uint8 structureVersion; - drflac_uint8 headerType; - drflac_uint64 granulePosition; - drflac_uint32 serialNumber; - drflac_uint32 sequenceNumber; - drflac_uint32 checksum; - drflac_uint8 segmentCount; - drflac_uint8 segmentTable[255]; -} drflac_ogg_page_header; + ma_uint8 capturePattern[4]; + ma_uint8 structureVersion; + ma_uint8 headerType; + ma_uint64 granulePosition; + ma_uint32 serialNumber; + ma_uint32 sequenceNumber; + ma_uint32 checksum; + ma_uint8 segmentCount; + ma_uint8 segmentTable[255]; +} ma_dr_flac_ogg_page_header; #endif typedef struct { - drflac_read_proc onRead; - drflac_seek_proc onSeek; - drflac_meta_proc onMeta; - drflac_container container; + ma_dr_flac_read_proc onRead; + ma_dr_flac_seek_proc onSeek; + ma_dr_flac_meta_proc onMeta; + ma_dr_flac_container container; void* pUserData; void* pUserDataMD; - drflac_uint32 sampleRate; - drflac_uint8 channels; - drflac_uint8 bitsPerSample; - drflac_uint64 totalPCMFrameCount; - drflac_uint16 maxBlockSizeInPCMFrames; - drflac_uint64 runningFilePos; - drflac_bool32 hasStreamInfoBlock; - drflac_bool32 hasMetadataBlocks; - drflac_bs bs; - drflac_frame_header firstFrameHeader; -#ifndef DR_FLAC_NO_OGG - drflac_uint32 oggSerial; - drflac_uint64 oggFirstBytePos; - drflac_ogg_page_header oggBosHeader; + ma_uint32 sampleRate; + ma_uint8 channels; + ma_uint8 bitsPerSample; + ma_uint64 totalPCMFrameCount; + ma_uint16 maxBlockSizeInPCMFrames; + ma_uint64 runningFilePos; + ma_bool32 hasStreamInfoBlock; + ma_bool32 hasMetadataBlocks; + ma_dr_flac_bs bs; + ma_dr_flac_frame_header firstFrameHeader; +#ifndef MA_DR_FLAC_NO_OGG + ma_uint32 oggSerial; + ma_uint64 oggFirstBytePos; + ma_dr_flac_ogg_page_header oggBosHeader; #endif -} drflac_init_info; -static DRFLAC_INLINE void drflac__decode_block_header(drflac_uint32 blockHeader, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) +} ma_dr_flac_init_info; +static MA_INLINE void ma_dr_flac__decode_block_header(ma_uint32 blockHeader, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize) { - blockHeader = drflac__be2host_32(blockHeader); - *isLastBlock = (drflac_uint8)((blockHeader & 0x80000000UL) >> 31); - *blockType = (drflac_uint8)((blockHeader & 0x7F000000UL) >> 24); + blockHeader = ma_dr_flac__be2host_32(blockHeader); + *isLastBlock = (ma_uint8)((blockHeader & 0x80000000UL) >> 31); + *blockType = (ma_uint8)((blockHeader & 0x7F000000UL) >> 24); *blockSize = (blockHeader & 0x00FFFFFFUL); } -static DRFLAC_INLINE drflac_bool32 drflac__read_and_decode_block_header(drflac_read_proc onRead, void* pUserData, drflac_uint8* isLastBlock, drflac_uint8* blockType, drflac_uint32* blockSize) +static MA_INLINE ma_bool32 ma_dr_flac__read_and_decode_block_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_uint8* isLastBlock, ma_uint8* blockType, ma_uint32* blockSize) { - drflac_uint32 blockHeader; + ma_uint32 blockHeader; *blockSize = 0; if (onRead(pUserData, &blockHeader, 4) != 4) { - return DRFLAC_FALSE; + return MA_FALSE; } - drflac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); - return DRFLAC_TRUE; + ma_dr_flac__decode_block_header(blockHeader, isLastBlock, blockType, blockSize); + return MA_TRUE; } -static drflac_bool32 drflac__read_streaminfo(drflac_read_proc onRead, void* pUserData, drflac_streaminfo* pStreamInfo) +static ma_bool32 ma_dr_flac__read_streaminfo(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_streaminfo* pStreamInfo) { - drflac_uint32 blockSizes; - drflac_uint64 frameSizes = 0; - drflac_uint64 importantProps; - drflac_uint8 md5[16]; + ma_uint32 blockSizes; + ma_uint64 frameSizes = 0; + ma_uint64 importantProps; + ma_uint8 md5[16]; if (onRead(pUserData, &blockSizes, 4) != 4) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onRead(pUserData, &frameSizes, 6) != 6) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onRead(pUserData, &importantProps, 8) != 8) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onRead(pUserData, md5, sizeof(md5)) != sizeof(md5)) { - return DRFLAC_FALSE; + return MA_FALSE; } - blockSizes = drflac__be2host_32(blockSizes); - frameSizes = drflac__be2host_64(frameSizes); - importantProps = drflac__be2host_64(importantProps); - pStreamInfo->minBlockSizeInPCMFrames = (drflac_uint16)((blockSizes & 0xFFFF0000) >> 16); - pStreamInfo->maxBlockSizeInPCMFrames = (drflac_uint16) (blockSizes & 0x0000FFFF); - pStreamInfo->minFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 24)) >> 40); - pStreamInfo->maxFrameSizeInPCMFrames = (drflac_uint32)((frameSizes & (((drflac_uint64)0x00FFFFFF << 16) << 0)) >> 16); - pStreamInfo->sampleRate = (drflac_uint32)((importantProps & (((drflac_uint64)0x000FFFFF << 16) << 28)) >> 44); - pStreamInfo->channels = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000000E << 16) << 24)) >> 41) + 1; - pStreamInfo->bitsPerSample = (drflac_uint8 )((importantProps & (((drflac_uint64)0x0000001F << 16) << 20)) >> 36) + 1; - pStreamInfo->totalPCMFrameCount = ((importantProps & ((((drflac_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF))); - DRFLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5)); - return DRFLAC_TRUE; + blockSizes = ma_dr_flac__be2host_32(blockSizes); + frameSizes = ma_dr_flac__be2host_64(frameSizes); + importantProps = ma_dr_flac__be2host_64(importantProps); + pStreamInfo->minBlockSizeInPCMFrames = (ma_uint16)((blockSizes & 0xFFFF0000) >> 16); + pStreamInfo->maxBlockSizeInPCMFrames = (ma_uint16) (blockSizes & 0x0000FFFF); + pStreamInfo->minFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 24)) >> 40); + pStreamInfo->maxFrameSizeInPCMFrames = (ma_uint32)((frameSizes & (((ma_uint64)0x00FFFFFF << 16) << 0)) >> 16); + pStreamInfo->sampleRate = (ma_uint32)((importantProps & (((ma_uint64)0x000FFFFF << 16) << 28)) >> 44); + pStreamInfo->channels = (ma_uint8 )((importantProps & (((ma_uint64)0x0000000E << 16) << 24)) >> 41) + 1; + pStreamInfo->bitsPerSample = (ma_uint8 )((importantProps & (((ma_uint64)0x0000001F << 16) << 20)) >> 36) + 1; + pStreamInfo->totalPCMFrameCount = ((importantProps & ((((ma_uint64)0x0000000F << 16) << 16) | 0xFFFFFFFF))); + MA_DR_FLAC_COPY_MEMORY(pStreamInfo->md5, md5, sizeof(md5)); + return MA_TRUE; } -static void* drflac__malloc_default(size_t sz, void* pUserData) +static void* ma_dr_flac__malloc_default(size_t sz, void* pUserData) { (void)pUserData; - return DRFLAC_MALLOC(sz); + return MA_DR_FLAC_MALLOC(sz); } -static void* drflac__realloc_default(void* p, size_t sz, void* pUserData) +static void* ma_dr_flac__realloc_default(void* p, size_t sz, void* pUserData) { (void)pUserData; - return DRFLAC_REALLOC(p, sz); + return MA_DR_FLAC_REALLOC(p, sz); } -static void drflac__free_default(void* p, void* pUserData) +static void ma_dr_flac__free_default(void* p, void* pUserData) { (void)pUserData; - DRFLAC_FREE(p); + MA_DR_FLAC_FREE(p); } -static void* drflac__malloc_from_callbacks(size_t sz, const drflac_allocation_callbacks* pAllocationCallbacks) +static void* ma_dr_flac__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; @@ -85593,7 +85765,7 @@ static void* drflac__malloc_from_callbacks(size_t sz, const drflac_allocation_ca } return NULL; } -static void* drflac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drflac_allocation_callbacks* pAllocationCallbacks) +static void* ma_dr_flac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; @@ -85608,14 +85780,14 @@ static void* drflac__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, return NULL; } if (p != NULL) { - DRFLAC_COPY_MEMORY(p2, p, szOld); + MA_DR_FLAC_COPY_MEMORY(p2, p, szOld); pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } return p2; } return NULL; } -static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbacks* pAllocationCallbacks) +static void ma_dr_flac__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) { if (p == NULL || pAllocationCallbacks == NULL) { return; @@ -85624,18 +85796,18 @@ static void drflac__free_from_callbacks(void* p, const drflac_allocation_callbac pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } } -static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_uint64* pFirstFramePos, drflac_uint64* pSeektablePos, drflac_uint32* pSeekpointCount, drflac_allocation_callbacks* pAllocationCallbacks) +static ma_bool32 ma_dr_flac__read_and_decode_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_uint64* pFirstFramePos, ma_uint64* pSeektablePos, ma_uint32* pSeekpointCount, ma_allocation_callbacks* pAllocationCallbacks) { - drflac_uint64 runningFilePos = 42; - drflac_uint64 seektablePos = 0; - drflac_uint32 seektableSize = 0; + ma_uint64 runningFilePos = 42; + ma_uint64 seektablePos = 0; + ma_uint32 seektableSize = 0; for (;;) { - drflac_metadata metadata; - drflac_uint8 isLastBlock = 0; - drflac_uint8 blockType; - drflac_uint32 blockSize; - if (drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == DRFLAC_FALSE) { - return DRFLAC_FALSE; + ma_dr_flac_metadata metadata; + ma_uint8 isLastBlock = 0; + ma_uint8 blockType; + ma_uint32 blockSize; + if (ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize) == MA_FALSE) { + return MA_FALSE; } runningFilePos += 4; metadata.type = blockType; @@ -85643,159 +85815,159 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d metadata.rawDataSize = 0; switch (blockType) { - case DRFLAC_METADATA_BLOCK_TYPE_APPLICATION: + case MA_DR_FLAC_METADATA_BLOCK_TYPE_APPLICATION: { if (blockSize < 4) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onMeta) { - void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onRead(pUserData, pRawData, blockSize) != blockSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; - metadata.data.application.id = drflac__be2host_32(*(drflac_uint32*)pRawData); - metadata.data.application.pData = (const void*)((drflac_uint8*)pRawData + sizeof(drflac_uint32)); - metadata.data.application.dataSize = blockSize - sizeof(drflac_uint32); + metadata.data.application.id = ma_dr_flac__be2host_32(*(ma_uint32*)pRawData); + metadata.data.application.pData = (const void*)((ma_uint8*)pRawData + sizeof(ma_uint32)); + metadata.data.application.dataSize = blockSize - sizeof(ma_uint32); onMeta(pUserDataMD, &metadata); - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; - case DRFLAC_METADATA_BLOCK_TYPE_SEEKTABLE: + case MA_DR_FLAC_METADATA_BLOCK_TYPE_SEEKTABLE: { seektablePos = runningFilePos; seektableSize = blockSize; if (onMeta) { - drflac_uint32 seekpointCount; - drflac_uint32 iSeekpoint; + ma_uint32 seekpointCount; + ma_uint32 iSeekpoint; void* pRawData; - seekpointCount = blockSize/DRFLAC_SEEKPOINT_SIZE_IN_BYTES; - pRawData = drflac__malloc_from_callbacks(seekpointCount * sizeof(drflac_seekpoint), pAllocationCallbacks); + seekpointCount = blockSize/MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES; + pRawData = ma_dr_flac__malloc_from_callbacks(seekpointCount * sizeof(ma_dr_flac_seekpoint), pAllocationCallbacks); if (pRawData == NULL) { - return DRFLAC_FALSE; + return MA_FALSE; } for (iSeekpoint = 0; iSeekpoint < seekpointCount; ++iSeekpoint) { - drflac_seekpoint* pSeekpoint = (drflac_seekpoint*)pRawData + iSeekpoint; - if (onRead(pUserData, pSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) != DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + ma_dr_flac_seekpoint* pSeekpoint = (ma_dr_flac_seekpoint*)pRawData + iSeekpoint; + if (onRead(pUserData, pSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) != MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } - pSeekpoint->firstPCMFrame = drflac__be2host_64(pSeekpoint->firstPCMFrame); - pSeekpoint->flacFrameOffset = drflac__be2host_64(pSeekpoint->flacFrameOffset); - pSeekpoint->pcmFrameCount = drflac__be2host_16(pSeekpoint->pcmFrameCount); + pSeekpoint->firstPCMFrame = ma_dr_flac__be2host_64(pSeekpoint->firstPCMFrame); + pSeekpoint->flacFrameOffset = ma_dr_flac__be2host_64(pSeekpoint->flacFrameOffset); + pSeekpoint->pcmFrameCount = ma_dr_flac__be2host_16(pSeekpoint->pcmFrameCount); } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; metadata.data.seektable.seekpointCount = seekpointCount; - metadata.data.seektable.pSeekpoints = (const drflac_seekpoint*)pRawData; + metadata.data.seektable.pSeekpoints = (const ma_dr_flac_seekpoint*)pRawData; onMeta(pUserDataMD, &metadata); - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; - case DRFLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: + case MA_DR_FLAC_METADATA_BLOCK_TYPE_VORBIS_COMMENT: { if (blockSize < 8) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onMeta) { void* pRawData; const char* pRunningData; const char* pRunningDataEnd; - drflac_uint32 i; - pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + ma_uint32 i; + pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onRead(pUserData, pRawData, blockSize) != blockSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; pRunningData = (const char*)pRawData; pRunningDataEnd = (const char*)pRawData + blockSize; - metadata.data.vorbis_comment.vendorLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 4 < (drflac_int64)metadata.data.vorbis_comment.vendorLength) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + metadata.data.vorbis_comment.vendorLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if ((pRunningDataEnd - pRunningData) - 4 < (ma_int64)metadata.data.vorbis_comment.vendorLength) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } metadata.data.vorbis_comment.vendor = pRunningData; pRunningData += metadata.data.vorbis_comment.vendorLength; - metadata.data.vorbis_comment.commentCount = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) / sizeof(drflac_uint32) < metadata.data.vorbis_comment.commentCount) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + metadata.data.vorbis_comment.commentCount = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if ((pRunningDataEnd - pRunningData) / sizeof(ma_uint32) < metadata.data.vorbis_comment.commentCount) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } metadata.data.vorbis_comment.pComments = pRunningData; for (i = 0; i < metadata.data.vorbis_comment.commentCount; ++i) { - drflac_uint32 commentLength; + ma_uint32 commentLength; if (pRunningDataEnd - pRunningData < 4) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } - commentLength = drflac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if (pRunningDataEnd - pRunningData < (drflac_int64)commentLength) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + commentLength = ma_dr_flac__le2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if (pRunningDataEnd - pRunningData < (ma_int64)commentLength) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } pRunningData += commentLength; } onMeta(pUserDataMD, &metadata); - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; - case DRFLAC_METADATA_BLOCK_TYPE_CUESHEET: + case MA_DR_FLAC_METADATA_BLOCK_TYPE_CUESHEET: { if (blockSize < 396) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onMeta) { void* pRawData; const char* pRunningData; const char* pRunningDataEnd; size_t bufferSize; - drflac_uint8 iTrack; - drflac_uint8 iIndex; + ma_uint8 iTrack; + ma_uint8 iIndex; void* pTrackData; - pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onRead(pUserData, pRawData, blockSize) != blockSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; pRunningData = (const char*)pRawData; pRunningDataEnd = (const char*)pRawData + blockSize; - DRFLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; - metadata.data.cuesheet.leadInSampleCount = drflac__be2host_64(*(const drflac_uint64*)pRunningData); pRunningData += 8; + MA_DR_FLAC_COPY_MEMORY(metadata.data.cuesheet.catalog, pRunningData, 128); pRunningData += 128; + metadata.data.cuesheet.leadInSampleCount = ma_dr_flac__be2host_64(*(const ma_uint64*)pRunningData); pRunningData += 8; metadata.data.cuesheet.isCD = (pRunningData[0] & 0x80) != 0; pRunningData += 259; metadata.data.cuesheet.trackCount = pRunningData[0]; pRunningData += 1; metadata.data.cuesheet.pTrackData = NULL; { const char* pRunningDataSaved = pRunningData; - bufferSize = metadata.data.cuesheet.trackCount * DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES; + bufferSize = metadata.data.cuesheet.trackCount * MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES; for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { - drflac_uint8 indexCount; - drflac_uint32 indexPointSize; - if (pRunningDataEnd - pRunningData < DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + ma_uint8 indexCount; + ma_uint32 indexPointSize; + if (pRunningDataEnd - pRunningData < MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } pRunningData += 35; indexCount = pRunningData[0]; pRunningData += 1; - bufferSize += indexCount * sizeof(drflac_cuesheet_track_index); - indexPointSize = indexCount * DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; - if (pRunningDataEnd - pRunningData < (drflac_int64)indexPointSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + bufferSize += indexCount * sizeof(ma_dr_flac_cuesheet_track_index); + indexPointSize = indexCount * MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; + if (pRunningDataEnd - pRunningData < (ma_int64)indexPointSize) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } pRunningData += indexPointSize; } @@ -85803,125 +85975,125 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d } { char* pRunningTrackData; - pTrackData = drflac__malloc_from_callbacks(bufferSize, pAllocationCallbacks); + pTrackData = ma_dr_flac__malloc_from_callbacks(bufferSize, pAllocationCallbacks); if (pTrackData == NULL) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } pRunningTrackData = (char*)pTrackData; for (iTrack = 0; iTrack < metadata.data.cuesheet.trackCount; ++iTrack) { - drflac_uint8 indexCount; - DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES); - pRunningData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; - pRunningTrackData += DRFLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; + ma_uint8 indexCount; + MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES); + pRunningData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; + pRunningTrackData += MA_DR_FLAC_CUESHEET_TRACK_SIZE_IN_BYTES-1; indexCount = pRunningData[0]; pRunningData += 1; pRunningTrackData += 1; for (iIndex = 0; iIndex < indexCount; ++iIndex) { - drflac_cuesheet_track_index* pTrackIndex = (drflac_cuesheet_track_index*)pRunningTrackData; - DRFLAC_COPY_MEMORY(pRunningTrackData, pRunningData, DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES); - pRunningData += DRFLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; - pRunningTrackData += sizeof(drflac_cuesheet_track_index); - pTrackIndex->offset = drflac__be2host_64(pTrackIndex->offset); + ma_dr_flac_cuesheet_track_index* pTrackIndex = (ma_dr_flac_cuesheet_track_index*)pRunningTrackData; + MA_DR_FLAC_COPY_MEMORY(pRunningTrackData, pRunningData, MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES); + pRunningData += MA_DR_FLAC_CUESHEET_TRACK_INDEX_SIZE_IN_BYTES; + pRunningTrackData += sizeof(ma_dr_flac_cuesheet_track_index); + pTrackIndex->offset = ma_dr_flac__be2host_64(pTrackIndex->offset); } } metadata.data.cuesheet.pTrackData = pTrackData; } - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); pRawData = NULL; onMeta(pUserDataMD, &metadata); - drflac__free_from_callbacks(pTrackData, pAllocationCallbacks); + ma_dr_flac__free_from_callbacks(pTrackData, pAllocationCallbacks); pTrackData = NULL; } } break; - case DRFLAC_METADATA_BLOCK_TYPE_PICTURE: + case MA_DR_FLAC_METADATA_BLOCK_TYPE_PICTURE: { if (blockSize < 32) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onMeta) { void* pRawData; const char* pRunningData; const char* pRunningDataEnd; - pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onRead(pUserData, pRawData, blockSize) != blockSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; pRunningData = (const char*)pRawData; pRunningDataEnd = (const char*)pRawData + blockSize; - metadata.data.picture.type = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.mimeLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 24 < (drflac_int64)metadata.data.picture.mimeLength) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + metadata.data.picture.type = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.mimeLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if ((pRunningDataEnd - pRunningData) - 24 < (ma_int64)metadata.data.picture.mimeLength) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } metadata.data.picture.mime = pRunningData; pRunningData += metadata.data.picture.mimeLength; - metadata.data.picture.descriptionLength = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - if ((pRunningDataEnd - pRunningData) - 20 < (drflac_int64)metadata.data.picture.descriptionLength) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + metadata.data.picture.descriptionLength = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + if ((pRunningDataEnd - pRunningData) - 20 < (ma_int64)metadata.data.picture.descriptionLength) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } metadata.data.picture.description = pRunningData; pRunningData += metadata.data.picture.descriptionLength; - metadata.data.picture.width = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.height = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.colorDepth = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.indexColorCount = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.pictureDataSize = drflac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; - metadata.data.picture.pPictureData = (const drflac_uint8*)pRunningData; - if (pRunningDataEnd - pRunningData < (drflac_int64)metadata.data.picture.pictureDataSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + metadata.data.picture.width = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.height = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.colorDepth = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.indexColorCount = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.pictureDataSize = ma_dr_flac__be2host_32_ptr_unaligned(pRunningData); pRunningData += 4; + metadata.data.picture.pPictureData = (const ma_uint8*)pRunningData; + if (pRunningDataEnd - pRunningData < (ma_int64)metadata.data.picture.pictureDataSize) { + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } onMeta(pUserDataMD, &metadata); - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; - case DRFLAC_METADATA_BLOCK_TYPE_PADDING: + case MA_DR_FLAC_METADATA_BLOCK_TYPE_PADDING: { if (onMeta) { metadata.data.padding.unused = 0; - if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { - isLastBlock = DRFLAC_TRUE; + if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { + isLastBlock = MA_TRUE; } else { onMeta(pUserDataMD, &metadata); } } } break; - case DRFLAC_METADATA_BLOCK_TYPE_INVALID: + case MA_DR_FLAC_METADATA_BLOCK_TYPE_INVALID: { if (onMeta) { - if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { - isLastBlock = DRFLAC_TRUE; + if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { + isLastBlock = MA_TRUE; } } } break; default: { if (onMeta) { - void* pRawData = drflac__malloc_from_callbacks(blockSize, pAllocationCallbacks); + void* pRawData = ma_dr_flac__malloc_from_callbacks(blockSize, pAllocationCallbacks); if (pRawData == NULL) { - return DRFLAC_FALSE; + return MA_FALSE; } if (onRead(pUserData, pRawData, blockSize) != blockSize) { - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); - return DRFLAC_FALSE; + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); + return MA_FALSE; } metadata.pRawData = pRawData; metadata.rawDataSize = blockSize; onMeta(pUserDataMD, &metadata); - drflac__free_from_callbacks(pRawData, pAllocationCallbacks); + ma_dr_flac__free_from_callbacks(pRawData, pAllocationCallbacks); } } break; } if (onMeta == NULL && blockSize > 0) { - if (!onSeek(pUserData, blockSize, drflac_seek_origin_current)) { - isLastBlock = DRFLAC_TRUE; + if (!onSeek(pUserData, blockSize, ma_dr_flac_seek_origin_current)) { + isLastBlock = MA_TRUE; } } runningFilePos += blockSize; @@ -85930,44 +86102,44 @@ static drflac_bool32 drflac__read_and_decode_metadata(drflac_read_proc onRead, d } } *pSeektablePos = seektablePos; - *pSeekpointCount = seektableSize / DRFLAC_SEEKPOINT_SIZE_IN_BYTES; + *pSeekpointCount = seektableSize / MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES; *pFirstFramePos = runningFilePos; - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) +static ma_bool32 ma_dr_flac__init_private__native(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed) { - drflac_uint8 isLastBlock; - drflac_uint8 blockType; - drflac_uint32 blockSize; + ma_uint8 isLastBlock; + ma_uint8 blockType; + ma_uint32 blockSize; (void)onSeek; - pInit->container = drflac_container_native; - if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { - return DRFLAC_FALSE; + pInit->container = ma_dr_flac_container_native; + if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + return MA_FALSE; } - if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { + if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { if (!relaxed) { - return DRFLAC_FALSE; + return MA_FALSE; } else { - pInit->hasStreamInfoBlock = DRFLAC_FALSE; - pInit->hasMetadataBlocks = DRFLAC_FALSE; - if (!drflac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { - return DRFLAC_FALSE; + pInit->hasStreamInfoBlock = MA_FALSE; + pInit->hasMetadataBlocks = MA_FALSE; + if (!ma_dr_flac__read_next_flac_frame_header(&pInit->bs, 0, &pInit->firstFrameHeader)) { + return MA_FALSE; } if (pInit->firstFrameHeader.bitsPerSample == 0) { - return DRFLAC_FALSE; + return MA_FALSE; } pInit->sampleRate = pInit->firstFrameHeader.sampleRate; - pInit->channels = drflac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); + pInit->channels = ma_dr_flac__get_channel_count_from_channel_assignment(pInit->firstFrameHeader.channelAssignment); pInit->bitsPerSample = pInit->firstFrameHeader.bitsPerSample; pInit->maxBlockSizeInPCMFrames = 65535; - return DRFLAC_TRUE; + return MA_TRUE; } } else { - drflac_streaminfo streaminfo; - if (!drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { - return DRFLAC_FALSE; + ma_dr_flac_streaminfo streaminfo; + if (!ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) { + return MA_FALSE; } - pInit->hasStreamInfoBlock = DRFLAC_TRUE; + pInit->hasStreamInfoBlock = MA_TRUE; pInit->sampleRate = streaminfo.sampleRate; pInit->channels = streaminfo.channels; pInit->bitsPerSample = streaminfo.bitsPerSample; @@ -85975,26 +86147,26 @@ static drflac_bool32 drflac__init_private__native(drflac_init_info* pInit, drfla pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; pInit->hasMetadataBlocks = !isLastBlock; if (onMeta) { - drflac_metadata metadata; - metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; + ma_dr_flac_metadata metadata; + metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO; metadata.pRawData = NULL; metadata.rawDataSize = 0; metadata.data.streaminfo = streaminfo; onMeta(pUserDataMD, &metadata); } - return DRFLAC_TRUE; + return MA_TRUE; } } -#ifndef DR_FLAC_NO_OGG -#define DRFLAC_OGG_MAX_PAGE_SIZE 65307 -#define DRFLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 +#ifndef MA_DR_FLAC_NO_OGG +#define MA_DR_FLAC_OGG_MAX_PAGE_SIZE 65307 +#define MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32 1605413199 typedef enum { - drflac_ogg_recover_on_crc_mismatch, - drflac_ogg_fail_on_crc_mismatch -} drflac_ogg_crc_mismatch_recovery; -#ifndef DR_FLAC_NO_CRC -static drflac_uint32 drflac__crc32_table[] = { + ma_dr_flac_ogg_recover_on_crc_mismatch, + ma_dr_flac_ogg_fail_on_crc_mismatch +} ma_dr_flac_ogg_crc_mismatch_recovery; +#ifndef MA_DR_FLAC_NO_CRC +static ma_uint32 ma_dr_flac__crc32_table[] = { 0x00000000L, 0x04C11DB7L, 0x09823B6EL, 0x0D4326D9L, 0x130476DCL, 0x17C56B6BL, 0x1A864DB2L, 0x1E475005L, 0x2608EDB8L, 0x22C9F00FL, 0x2F8AD6D6L, 0x2B4BCB61L, @@ -86061,63 +86233,63 @@ static drflac_uint32 drflac__crc32_table[] = { 0xBCB4666DL, 0xB8757BDAL, 0xB5365D03L, 0xB1F740B4L }; #endif -static DRFLAC_INLINE drflac_uint32 drflac_crc32_byte(drflac_uint32 crc32, drflac_uint8 data) +static MA_INLINE ma_uint32 ma_dr_flac_crc32_byte(ma_uint32 crc32, ma_uint8 data) { -#ifndef DR_FLAC_NO_CRC - return (crc32 << 8) ^ drflac__crc32_table[(drflac_uint8)((crc32 >> 24) & 0xFF) ^ data]; +#ifndef MA_DR_FLAC_NO_CRC + return (crc32 << 8) ^ ma_dr_flac__crc32_table[(ma_uint8)((crc32 >> 24) & 0xFF) ^ data]; #else (void)data; return crc32; #endif } #if 0 -static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint32(drflac_uint32 crc32, drflac_uint32 data) +static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint32(ma_uint32 crc32, ma_uint32 data) { - crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 24) & 0xFF)); - crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 16) & 0xFF)); - crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 8) & 0xFF)); - crc32 = drflac_crc32_byte(crc32, (drflac_uint8)((data >> 0) & 0xFF)); + crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 24) & 0xFF)); + crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 16) & 0xFF)); + crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 8) & 0xFF)); + crc32 = ma_dr_flac_crc32_byte(crc32, (ma_uint8)((data >> 0) & 0xFF)); return crc32; } -static DRFLAC_INLINE drflac_uint32 drflac_crc32_uint64(drflac_uint32 crc32, drflac_uint64 data) +static MA_INLINE ma_uint32 ma_dr_flac_crc32_uint64(ma_uint32 crc32, ma_uint64 data) { - crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 32) & 0xFFFFFFFF)); - crc32 = drflac_crc32_uint32(crc32, (drflac_uint32)((data >> 0) & 0xFFFFFFFF)); + crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 32) & 0xFFFFFFFF)); + crc32 = ma_dr_flac_crc32_uint32(crc32, (ma_uint32)((data >> 0) & 0xFFFFFFFF)); return crc32; } #endif -static DRFLAC_INLINE drflac_uint32 drflac_crc32_buffer(drflac_uint32 crc32, drflac_uint8* pData, drflac_uint32 dataSize) +static MA_INLINE ma_uint32 ma_dr_flac_crc32_buffer(ma_uint32 crc32, ma_uint8* pData, ma_uint32 dataSize) { - drflac_uint32 i; + ma_uint32 i; for (i = 0; i < dataSize; ++i) { - crc32 = drflac_crc32_byte(crc32, pData[i]); + crc32 = ma_dr_flac_crc32_byte(crc32, pData[i]); } return crc32; } -static DRFLAC_INLINE drflac_bool32 drflac_ogg__is_capture_pattern(drflac_uint8 pattern[4]) +static MA_INLINE ma_bool32 ma_dr_flac_ogg__is_capture_pattern(ma_uint8 pattern[4]) { return pattern[0] == 'O' && pattern[1] == 'g' && pattern[2] == 'g' && pattern[3] == 'S'; } -static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_header_size(drflac_ogg_page_header* pHeader) +static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_header_size(ma_dr_flac_ogg_page_header* pHeader) { return 27 + pHeader->segmentCount; } -static DRFLAC_INLINE drflac_uint32 drflac_ogg__get_page_body_size(drflac_ogg_page_header* pHeader) +static MA_INLINE ma_uint32 ma_dr_flac_ogg__get_page_body_size(ma_dr_flac_ogg_page_header* pHeader) { - drflac_uint32 pageBodySize = 0; + ma_uint32 pageBodySize = 0; int i; for (i = 0; i < pHeader->segmentCount; ++i) { pageBodySize += pHeader->segmentTable[i]; } return pageBodySize; } -static drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) +static ma_result ma_dr_flac_ogg__read_page_header_after_capture_pattern(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32) { - drflac_uint8 data[23]; - drflac_uint32 i; - DRFLAC_ASSERT(*pCRC32 == DRFLAC_OGG_CAPTURE_PATTERN_CRC32); + ma_uint8 data[23]; + ma_uint32 i; + MA_DR_FLAC_ASSERT(*pCRC32 == MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32); if (onRead(pUserData, data, 23) != 23) { - return DRFLAC_AT_END; + return MA_AT_END; } *pBytesRead += 23; pHeader->capturePattern[0] = 'O'; @@ -86126,44 +86298,44 @@ static drflac_result drflac_ogg__read_page_header_after_capture_pattern(drflac_r pHeader->capturePattern[3] = 'S'; pHeader->structureVersion = data[0]; pHeader->headerType = data[1]; - DRFLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8); - DRFLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4); - DRFLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4); - DRFLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4); + MA_DR_FLAC_COPY_MEMORY(&pHeader->granulePosition, &data[ 2], 8); + MA_DR_FLAC_COPY_MEMORY(&pHeader->serialNumber, &data[10], 4); + MA_DR_FLAC_COPY_MEMORY(&pHeader->sequenceNumber, &data[14], 4); + MA_DR_FLAC_COPY_MEMORY(&pHeader->checksum, &data[18], 4); pHeader->segmentCount = data[22]; data[18] = 0; data[19] = 0; data[20] = 0; data[21] = 0; for (i = 0; i < 23; ++i) { - *pCRC32 = drflac_crc32_byte(*pCRC32, data[i]); + *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, data[i]); } if (onRead(pUserData, pHeader->segmentTable, pHeader->segmentCount) != pHeader->segmentCount) { - return DRFLAC_AT_END; + return MA_AT_END; } *pBytesRead += pHeader->segmentCount; for (i = 0; i < pHeader->segmentCount; ++i) { - *pCRC32 = drflac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); + *pCRC32 = ma_dr_flac_crc32_byte(*pCRC32, pHeader->segmentTable[i]); } - return DRFLAC_SUCCESS; + return MA_SUCCESS; } -static drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* pUserData, drflac_ogg_page_header* pHeader, drflac_uint32* pBytesRead, drflac_uint32* pCRC32) +static ma_result ma_dr_flac_ogg__read_page_header(ma_dr_flac_read_proc onRead, void* pUserData, ma_dr_flac_ogg_page_header* pHeader, ma_uint32* pBytesRead, ma_uint32* pCRC32) { - drflac_uint8 id[4]; + ma_uint8 id[4]; *pBytesRead = 0; if (onRead(pUserData, id, 4) != 4) { - return DRFLAC_AT_END; + return MA_AT_END; } *pBytesRead += 4; for (;;) { - if (drflac_ogg__is_capture_pattern(id)) { - drflac_result result; - *pCRC32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; - result = drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); - if (result == DRFLAC_SUCCESS) { - return DRFLAC_SUCCESS; + if (ma_dr_flac_ogg__is_capture_pattern(id)) { + ma_result result; + *pCRC32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32; + result = ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, pHeader, pBytesRead, pCRC32); + if (result == MA_SUCCESS) { + return MA_SUCCESS; } else { - if (result == DRFLAC_CRC_MISMATCH) { + if (result == MA_CRC_MISMATCH) { continue; } else { return result; @@ -86174,7 +86346,7 @@ static drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* id[1] = id[2]; id[2] = id[3]; if (onRead(pUserData, &id[3], 1) != 1) { - return DRFLAC_AT_END; + return MA_AT_END; } *pBytesRead += 1; } @@ -86182,91 +86354,91 @@ static drflac_result drflac_ogg__read_page_header(drflac_read_proc onRead, void* } typedef struct { - drflac_read_proc onRead; - drflac_seek_proc onSeek; + ma_dr_flac_read_proc onRead; + ma_dr_flac_seek_proc onSeek; void* pUserData; - drflac_uint64 currentBytePos; - drflac_uint64 firstBytePos; - drflac_uint32 serialNumber; - drflac_ogg_page_header bosPageHeader; - drflac_ogg_page_header currentPageHeader; - drflac_uint32 bytesRemainingInPage; - drflac_uint32 pageDataSize; - drflac_uint8 pageData[DRFLAC_OGG_MAX_PAGE_SIZE]; -} drflac_oggbs; -static size_t drflac_oggbs__read_physical(drflac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) + ma_uint64 currentBytePos; + ma_uint64 firstBytePos; + ma_uint32 serialNumber; + ma_dr_flac_ogg_page_header bosPageHeader; + ma_dr_flac_ogg_page_header currentPageHeader; + ma_uint32 bytesRemainingInPage; + ma_uint32 pageDataSize; + ma_uint8 pageData[MA_DR_FLAC_OGG_MAX_PAGE_SIZE]; +} ma_dr_flac_oggbs; +static size_t ma_dr_flac_oggbs__read_physical(ma_dr_flac_oggbs* oggbs, void* bufferOut, size_t bytesToRead) { size_t bytesActuallyRead = oggbs->onRead(oggbs->pUserData, bufferOut, bytesToRead); oggbs->currentBytePos += bytesActuallyRead; return bytesActuallyRead; } -static drflac_bool32 drflac_oggbs__seek_physical(drflac_oggbs* oggbs, drflac_uint64 offset, drflac_seek_origin origin) +static ma_bool32 ma_dr_flac_oggbs__seek_physical(ma_dr_flac_oggbs* oggbs, ma_uint64 offset, ma_dr_flac_seek_origin origin) { - if (origin == drflac_seek_origin_start) { + if (origin == ma_dr_flac_seek_origin_start) { if (offset <= 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_start)) { - return DRFLAC_FALSE; + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; } oggbs->currentBytePos = offset; - return DRFLAC_TRUE; + return MA_TRUE; } else { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_start)) { - return DRFLAC_FALSE; + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; } oggbs->currentBytePos = offset; - return drflac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, drflac_seek_origin_current); + return ma_dr_flac_oggbs__seek_physical(oggbs, offset - 0x7FFFFFFF, ma_dr_flac_seek_origin_current); } } else { while (offset > 0x7FFFFFFF) { - if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, drflac_seek_origin_current)) { - return DRFLAC_FALSE; + if (!oggbs->onSeek(oggbs->pUserData, 0x7FFFFFFF, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; } oggbs->currentBytePos += 0x7FFFFFFF; offset -= 0x7FFFFFFF; } - if (!oggbs->onSeek(oggbs->pUserData, (int)offset, drflac_seek_origin_current)) { - return DRFLAC_FALSE; + if (!oggbs->onSeek(oggbs->pUserData, (int)offset, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; } oggbs->currentBytePos += offset; - return DRFLAC_TRUE; + return MA_TRUE; } } -static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_ogg_crc_mismatch_recovery recoveryMethod) +static ma_bool32 ma_dr_flac_oggbs__goto_next_page(ma_dr_flac_oggbs* oggbs, ma_dr_flac_ogg_crc_mismatch_recovery recoveryMethod) { - drflac_ogg_page_header header; + ma_dr_flac_ogg_page_header header; for (;;) { - drflac_uint32 crc32 = 0; - drflac_uint32 bytesRead; - drflac_uint32 pageBodySize; -#ifndef DR_FLAC_NO_CRC - drflac_uint32 actualCRC32; + ma_uint32 crc32 = 0; + ma_uint32 bytesRead; + ma_uint32 pageBodySize; +#ifndef MA_DR_FLAC_NO_CRC + ma_uint32 actualCRC32; #endif - if (drflac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { - return DRFLAC_FALSE; + if (ma_dr_flac_ogg__read_page_header(oggbs->onRead, oggbs->pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { + return MA_FALSE; } oggbs->currentBytePos += bytesRead; - pageBodySize = drflac_ogg__get_page_body_size(&header); - if (pageBodySize > DRFLAC_OGG_MAX_PAGE_SIZE) { + pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header); + if (pageBodySize > MA_DR_FLAC_OGG_MAX_PAGE_SIZE) { continue; } if (header.serialNumber != oggbs->serialNumber) { - if (pageBodySize > 0 && !drflac_oggbs__seek_physical(oggbs, pageBodySize, drflac_seek_origin_current)) { - return DRFLAC_FALSE; + if (pageBodySize > 0 && !ma_dr_flac_oggbs__seek_physical(oggbs, pageBodySize, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; } continue; } - if (drflac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { - return DRFLAC_FALSE; + if (ma_dr_flac_oggbs__read_physical(oggbs, oggbs->pageData, pageBodySize) != pageBodySize) { + return MA_FALSE; } oggbs->pageDataSize = pageBodySize; -#ifndef DR_FLAC_NO_CRC - actualCRC32 = drflac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); +#ifndef MA_DR_FLAC_NO_CRC + actualCRC32 = ma_dr_flac_crc32_buffer(crc32, oggbs->pageData, oggbs->pageDataSize); if (actualCRC32 != header.checksum) { - if (recoveryMethod == drflac_ogg_recover_on_crc_mismatch) { + if (recoveryMethod == ma_dr_flac_ogg_recover_on_crc_mismatch) { continue; } else { - drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch); - return DRFLAC_FALSE; + ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch); + return MA_FALSE; } } #else @@ -86274,17 +86446,17 @@ static drflac_bool32 drflac_oggbs__goto_next_page(drflac_oggbs* oggbs, drflac_og #endif oggbs->currentPageHeader = header; oggbs->bytesRemainingInPage = pageBodySize; - return DRFLAC_TRUE; + return MA_TRUE; } } #if 0 -static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, drflac_uint8* pBytesRemainingInSeg) +static ma_uint8 ma_dr_flac_oggbs__get_current_segment_index(ma_dr_flac_oggbs* oggbs, ma_uint8* pBytesRemainingInSeg) { - drflac_uint32 bytesConsumedInPage = drflac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; - drflac_uint8 iSeg = 0; - drflac_uint32 iByte = 0; + ma_uint32 bytesConsumedInPage = ma_dr_flac_ogg__get_page_body_size(&oggbs->currentPageHeader) - oggbs->bytesRemainingInPage; + ma_uint8 iSeg = 0; + ma_uint32 iByte = 0; while (iByte < bytesConsumedInPage) { - drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; if (iByte + segmentSize > bytesConsumedInPage) { break; } else { @@ -86292,92 +86464,92 @@ static drflac_uint8 drflac_oggbs__get_current_segment_index(drflac_oggbs* oggbs, iByte += segmentSize; } } - *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (drflac_uint8)(bytesConsumedInPage - iByte); + *pBytesRemainingInSeg = oggbs->currentPageHeader.segmentTable[iSeg] - (ma_uint8)(bytesConsumedInPage - iByte); return iSeg; } -static drflac_bool32 drflac_oggbs__seek_to_next_packet(drflac_oggbs* oggbs) +static ma_bool32 ma_dr_flac_oggbs__seek_to_next_packet(ma_dr_flac_oggbs* oggbs) { for (;;) { - drflac_bool32 atEndOfPage = DRFLAC_FALSE; - drflac_uint8 bytesRemainingInSeg; - drflac_uint8 iFirstSeg = drflac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); - drflac_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; - for (drflac_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { - drflac_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; + ma_bool32 atEndOfPage = MA_FALSE; + ma_uint8 bytesRemainingInSeg; + ma_uint8 iFirstSeg = ma_dr_flac_oggbs__get_current_segment_index(oggbs, &bytesRemainingInSeg); + ma_uint32 bytesToEndOfPacketOrPage = bytesRemainingInSeg; + for (ma_uint8 iSeg = iFirstSeg; iSeg < oggbs->currentPageHeader.segmentCount; ++iSeg) { + ma_uint8 segmentSize = oggbs->currentPageHeader.segmentTable[iSeg]; if (segmentSize < 255) { if (iSeg == oggbs->currentPageHeader.segmentCount-1) { - atEndOfPage = DRFLAC_TRUE; + atEndOfPage = MA_TRUE; } break; } bytesToEndOfPacketOrPage += segmentSize; } - drflac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, drflac_seek_origin_current); + ma_dr_flac_oggbs__seek_physical(oggbs, bytesToEndOfPacketOrPage, ma_dr_flac_seek_origin_current); oggbs->bytesRemainingInPage -= bytesToEndOfPacketOrPage; if (atEndOfPage) { - if (!drflac_oggbs__goto_next_page(oggbs)) { - return DRFLAC_FALSE; + if (!ma_dr_flac_oggbs__goto_next_page(oggbs)) { + return MA_FALSE; } if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { - return DRFLAC_TRUE; + return MA_TRUE; } } else { - return DRFLAC_TRUE; + return MA_TRUE; } } } -static drflac_bool32 drflac_oggbs__seek_to_next_frame(drflac_oggbs* oggbs) +static ma_bool32 ma_dr_flac_oggbs__seek_to_next_frame(ma_dr_flac_oggbs* oggbs) { - return drflac_oggbs__seek_to_next_packet(oggbs); + return ma_dr_flac_oggbs__seek_to_next_packet(oggbs); } #endif -static size_t drflac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) +static size_t ma_dr_flac__on_read_ogg(void* pUserData, void* bufferOut, size_t bytesToRead) { - drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; - drflac_uint8* pRunningBufferOut = (drflac_uint8*)bufferOut; + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData; + ma_uint8* pRunningBufferOut = (ma_uint8*)bufferOut; size_t bytesRead = 0; - DRFLAC_ASSERT(oggbs != NULL); - DRFLAC_ASSERT(pRunningBufferOut != NULL); + MA_DR_FLAC_ASSERT(oggbs != NULL); + MA_DR_FLAC_ASSERT(pRunningBufferOut != NULL); while (bytesRead < bytesToRead) { size_t bytesRemainingToRead = bytesToRead - bytesRead; if (oggbs->bytesRemainingInPage >= bytesRemainingToRead) { - DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); + MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), bytesRemainingToRead); bytesRead += bytesRemainingToRead; - oggbs->bytesRemainingInPage -= (drflac_uint32)bytesRemainingToRead; + oggbs->bytesRemainingInPage -= (ma_uint32)bytesRemainingToRead; break; } if (oggbs->bytesRemainingInPage > 0) { - DRFLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); + MA_DR_FLAC_COPY_MEMORY(pRunningBufferOut, oggbs->pageData + (oggbs->pageDataSize - oggbs->bytesRemainingInPage), oggbs->bytesRemainingInPage); bytesRead += oggbs->bytesRemainingInPage; pRunningBufferOut += oggbs->bytesRemainingInPage; oggbs->bytesRemainingInPage = 0; } - DRFLAC_ASSERT(bytesRemainingToRead > 0); - if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { + MA_DR_FLAC_ASSERT(bytesRemainingToRead > 0); + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { break; } } return bytesRead; } -static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_seek_origin origin) +static ma_bool32 ma_dr_flac__on_seek_ogg(void* pUserData, int offset, ma_dr_flac_seek_origin origin) { - drflac_oggbs* oggbs = (drflac_oggbs*)pUserData; + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pUserData; int bytesSeeked = 0; - DRFLAC_ASSERT(oggbs != NULL); - DRFLAC_ASSERT(offset >= 0); - if (origin == drflac_seek_origin_start) { - if (!drflac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, drflac_seek_origin_start)) { - return DRFLAC_FALSE; + MA_DR_FLAC_ASSERT(oggbs != NULL); + MA_DR_FLAC_ASSERT(offset >= 0); + if (origin == ma_dr_flac_seek_origin_start) { + if (!ma_dr_flac_oggbs__seek_physical(oggbs, (int)oggbs->firstBytePos, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; } - if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { - return DRFLAC_FALSE; + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { + return MA_FALSE; } - return drflac__on_seek_ogg(pUserData, offset, drflac_seek_origin_current); + return ma_dr_flac__on_seek_ogg(pUserData, offset, ma_dr_flac_seek_origin_current); } - DRFLAC_ASSERT(origin == drflac_seek_origin_current); + MA_DR_FLAC_ASSERT(origin == ma_dr_flac_seek_origin_current); while (bytesSeeked < offset) { int bytesRemainingToSeek = offset - bytesSeeked; - DRFLAC_ASSERT(bytesRemainingToSeek >= 0); + MA_DR_FLAC_ASSERT(bytesRemainingToSeek >= 0); if (oggbs->bytesRemainingInPage >= (size_t)bytesRemainingToSeek) { bytesSeeked += bytesRemainingToSeek; (void)bytesSeeked; @@ -86388,39 +86560,39 @@ static drflac_bool32 drflac__on_seek_ogg(void* pUserData, int offset, drflac_see bytesSeeked += (int)oggbs->bytesRemainingInPage; oggbs->bytesRemainingInPage = 0; } - DRFLAC_ASSERT(bytesRemainingToSeek > 0); - if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_fail_on_crc_mismatch)) { - return DRFLAC_FALSE; + MA_DR_FLAC_ASSERT(bytesRemainingToSeek > 0); + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_fail_on_crc_mismatch)) { + return MA_FALSE; } } - return DRFLAC_TRUE; + return MA_TRUE; } -static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) +static ma_bool32 ma_dr_flac_ogg__seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) { - drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; - drflac_uint64 originalBytePos; - drflac_uint64 runningGranulePosition; - drflac_uint64 runningFrameBytePos; - drflac_uint64 runningPCMFrameCount; - DRFLAC_ASSERT(oggbs != NULL); + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; + ma_uint64 originalBytePos; + ma_uint64 runningGranulePosition; + ma_uint64 runningFrameBytePos; + ma_uint64 runningPCMFrameCount; + MA_DR_FLAC_ASSERT(oggbs != NULL); originalBytePos = oggbs->currentBytePos; - if (!drflac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) { - return DRFLAC_FALSE; + if (!ma_dr_flac__seek_to_byte(&pFlac->bs, pFlac->firstFLACFramePosInBytes)) { + return MA_FALSE; } oggbs->bytesRemainingInPage = 0; runningGranulePosition = 0; for (;;) { - if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { - drflac_oggbs__seek_physical(oggbs, originalBytePos, drflac_seek_origin_start); - return DRFLAC_FALSE; + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { + ma_dr_flac_oggbs__seek_physical(oggbs, originalBytePos, ma_dr_flac_seek_origin_start); + return MA_FALSE; } - runningFrameBytePos = oggbs->currentBytePos - drflac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; + runningFrameBytePos = oggbs->currentBytePos - ma_dr_flac_ogg__get_page_header_size(&oggbs->currentPageHeader) - oggbs->pageDataSize; if (oggbs->currentPageHeader.granulePosition >= pcmFrameIndex) { break; } if ((oggbs->currentPageHeader.headerType & 0x01) == 0) { if (oggbs->currentPageHeader.segmentTable[0] >= 2) { - drflac_uint8 firstBytesInPage[2]; + ma_uint8 firstBytesInPage[2]; firstBytesInPage[0] = oggbs->pageData[0]; firstBytesInPage[1] = oggbs->pageData[1]; if ((firstBytesInPage[0] == 0xFF) && (firstBytesInPage[1] & 0xFC) == 0xF8) { @@ -86430,120 +86602,120 @@ static drflac_bool32 drflac_ogg__seek_to_pcm_frame(drflac* pFlac, drflac_uint64 } } } - if (!drflac_oggbs__seek_physical(oggbs, runningFrameBytePos, drflac_seek_origin_start)) { - return DRFLAC_FALSE; + if (!ma_dr_flac_oggbs__seek_physical(oggbs, runningFrameBytePos, ma_dr_flac_seek_origin_start)) { + return MA_FALSE; } - if (!drflac_oggbs__goto_next_page(oggbs, drflac_ogg_recover_on_crc_mismatch)) { - return DRFLAC_FALSE; + if (!ma_dr_flac_oggbs__goto_next_page(oggbs, ma_dr_flac_ogg_recover_on_crc_mismatch)) { + return MA_FALSE; } runningPCMFrameCount = runningGranulePosition; for (;;) { - drflac_uint64 firstPCMFrameInFLACFrame = 0; - drflac_uint64 lastPCMFrameInFLACFrame = 0; - drflac_uint64 pcmFrameCountInThisFrame; - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - return DRFLAC_FALSE; + ma_uint64 firstPCMFrameInFLACFrame = 0; + ma_uint64 lastPCMFrameInFLACFrame = 0; + ma_uint64 pcmFrameCountInThisFrame; + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + return MA_FALSE; } - drflac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); + ma_dr_flac__get_pcm_frame_range_of_current_flac_frame(pFlac, &firstPCMFrameInFLACFrame, &lastPCMFrameInFLACFrame); pcmFrameCountInThisFrame = (lastPCMFrameInFLACFrame - firstPCMFrameInFLACFrame) + 1; if (pcmFrameIndex == pFlac->totalPCMFrameCount && (runningPCMFrameCount + pcmFrameCountInThisFrame) == pFlac->totalPCMFrameCount) { - drflac_result result = drflac__decode_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { + ma_result result = ma_dr_flac__decode_flac_frame(pFlac); + if (result == MA_SUCCESS) { pFlac->currentPCMFrame = pcmFrameIndex; pFlac->currentFLACFrame.pcmFramesRemaining = 0; - return DRFLAC_TRUE; + return MA_TRUE; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } if (pcmFrameIndex < (runningPCMFrameCount + pcmFrameCountInThisFrame)) { - drflac_result result = drflac__decode_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { - drflac_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount); + ma_result result = ma_dr_flac__decode_flac_frame(pFlac); + if (result == MA_SUCCESS) { + ma_uint64 pcmFramesToDecode = (size_t)(pcmFrameIndex - runningPCMFrameCount); if (pcmFramesToDecode == 0) { - return DRFLAC_TRUE; + return MA_TRUE; } pFlac->currentPCMFrame = runningPCMFrameCount; - return drflac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, pcmFramesToDecode) == pcmFramesToDecode; } else { - if (result == DRFLAC_CRC_MISMATCH) { + if (result == MA_CRC_MISMATCH) { continue; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } } else { - drflac_result result = drflac__seek_to_next_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { + ma_result result = ma_dr_flac__seek_to_next_flac_frame(pFlac); + if (result == MA_SUCCESS) { runningPCMFrameCount += pcmFrameCountInThisFrame; } else { - if (result == DRFLAC_CRC_MISMATCH) { + if (result == MA_CRC_MISMATCH) { continue; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } } } } -static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, void* pUserDataMD, drflac_bool32 relaxed) +static ma_bool32 ma_dr_flac__init_private__ogg(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, void* pUserDataMD, ma_bool32 relaxed) { - drflac_ogg_page_header header; - drflac_uint32 crc32 = DRFLAC_OGG_CAPTURE_PATTERN_CRC32; - drflac_uint32 bytesRead = 0; + ma_dr_flac_ogg_page_header header; + ma_uint32 crc32 = MA_DR_FLAC_OGG_CAPTURE_PATTERN_CRC32; + ma_uint32 bytesRead = 0; (void)relaxed; - pInit->container = drflac_container_ogg; + pInit->container = ma_dr_flac_container_ogg; pInit->oggFirstBytePos = 0; - if (drflac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { - return DRFLAC_FALSE; + if (ma_dr_flac_ogg__read_page_header_after_capture_pattern(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { + return MA_FALSE; } pInit->runningFilePos += bytesRead; for (;;) { int pageBodySize; if ((header.headerType & 0x02) == 0) { - return DRFLAC_FALSE; + return MA_FALSE; } - pageBodySize = drflac_ogg__get_page_body_size(&header); + pageBodySize = ma_dr_flac_ogg__get_page_body_size(&header); if (pageBodySize == 51) { - drflac_uint32 bytesRemainingInPage = pageBodySize; - drflac_uint8 packetType; + ma_uint32 bytesRemainingInPage = pageBodySize; + ma_uint8 packetType; if (onRead(pUserData, &packetType, 1) != 1) { - return DRFLAC_FALSE; + return MA_FALSE; } bytesRemainingInPage -= 1; if (packetType == 0x7F) { - drflac_uint8 sig[4]; + ma_uint8 sig[4]; if (onRead(pUserData, sig, 4) != 4) { - return DRFLAC_FALSE; + return MA_FALSE; } bytesRemainingInPage -= 4; if (sig[0] == 'F' && sig[1] == 'L' && sig[2] == 'A' && sig[3] == 'C') { - drflac_uint8 mappingVersion[2]; + ma_uint8 mappingVersion[2]; if (onRead(pUserData, mappingVersion, 2) != 2) { - return DRFLAC_FALSE; + return MA_FALSE; } if (mappingVersion[0] != 1) { - return DRFLAC_FALSE; + return MA_FALSE; } - if (!onSeek(pUserData, 2, drflac_seek_origin_current)) { - return DRFLAC_FALSE; + if (!onSeek(pUserData, 2, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; } if (onRead(pUserData, sig, 4) != 4) { - return DRFLAC_FALSE; + return MA_FALSE; } if (sig[0] == 'f' && sig[1] == 'L' && sig[2] == 'a' && sig[3] == 'C') { - drflac_streaminfo streaminfo; - drflac_uint8 isLastBlock; - drflac_uint8 blockType; - drflac_uint32 blockSize; - if (!drflac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { - return DRFLAC_FALSE; + ma_dr_flac_streaminfo streaminfo; + ma_uint8 isLastBlock; + ma_uint8 blockType; + ma_uint32 blockSize; + if (!ma_dr_flac__read_and_decode_block_header(onRead, pUserData, &isLastBlock, &blockType, &blockSize)) { + return MA_FALSE; } - if (blockType != DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { - return DRFLAC_FALSE; + if (blockType != MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO || blockSize != 34) { + return MA_FALSE; } - if (drflac__read_streaminfo(onRead, pUserData, &streaminfo)) { - pInit->hasStreamInfoBlock = DRFLAC_TRUE; + if (ma_dr_flac__read_streaminfo(onRead, pUserData, &streaminfo)) { + pInit->hasStreamInfoBlock = MA_TRUE; pInit->sampleRate = streaminfo.sampleRate; pInit->channels = streaminfo.channels; pInit->bitsPerSample = streaminfo.bitsPerSample; @@ -86551,8 +86723,8 @@ static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_r pInit->maxBlockSizeInPCMFrames = streaminfo.maxBlockSizeInPCMFrames; pInit->hasMetadataBlocks = !isLastBlock; if (onMeta) { - drflac_metadata metadata; - metadata.type = DRFLAC_METADATA_BLOCK_TYPE_STREAMINFO; + ma_dr_flac_metadata metadata; + metadata.type = MA_DR_FLAC_METADATA_BLOCK_TYPE_STREAMINFO; metadata.pRawData = NULL; metadata.rawDataSize = 0; metadata.data.streaminfo = streaminfo; @@ -86564,44 +86736,44 @@ static drflac_bool32 drflac__init_private__ogg(drflac_init_info* pInit, drflac_r pInit->oggBosHeader = header; break; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } else { - return DRFLAC_FALSE; + return MA_FALSE; } } else { - if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { - return DRFLAC_FALSE; + if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; } } } else { - if (!onSeek(pUserData, bytesRemainingInPage, drflac_seek_origin_current)) { - return DRFLAC_FALSE; + if (!onSeek(pUserData, bytesRemainingInPage, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; } } } else { - if (!onSeek(pUserData, pageBodySize, drflac_seek_origin_current)) { - return DRFLAC_FALSE; + if (!onSeek(pUserData, pageBodySize, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; } } pInit->runningFilePos += pageBodySize; - if (drflac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != DRFLAC_SUCCESS) { - return DRFLAC_FALSE; + if (ma_dr_flac_ogg__read_page_header(onRead, pUserData, &header, &bytesRead, &crc32) != MA_SUCCESS) { + return MA_FALSE; } pInit->runningFilePos += bytesRead; } - pInit->hasMetadataBlocks = DRFLAC_TRUE; - return DRFLAC_TRUE; + pInit->hasMetadataBlocks = MA_TRUE; + return MA_TRUE; } #endif -static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD) +static ma_bool32 ma_dr_flac__init_private(ma_dr_flac_init_info* pInit, ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD) { - drflac_bool32 relaxed; - drflac_uint8 id[4]; + ma_bool32 relaxed; + ma_uint8 id[4]; if (pInit == NULL || onRead == NULL || onSeek == NULL) { - return DRFLAC_FALSE; + return MA_FALSE; } - DRFLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); + MA_DR_FLAC_ZERO_MEMORY(pInit, sizeof(*pInit)); pInit->onRead = onRead; pInit->onSeek = onSeek; pInit->onMeta = onMeta; @@ -86611,29 +86783,29 @@ static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_p pInit->bs.onRead = onRead; pInit->bs.onSeek = onSeek; pInit->bs.pUserData = pUserData; - drflac__reset_cache(&pInit->bs); - relaxed = container != drflac_container_unknown; + ma_dr_flac__reset_cache(&pInit->bs); + relaxed = container != ma_dr_flac_container_unknown; for (;;) { if (onRead(pUserData, id, 4) != 4) { - return DRFLAC_FALSE; + return MA_FALSE; } pInit->runningFilePos += 4; if (id[0] == 'I' && id[1] == 'D' && id[2] == '3') { - drflac_uint8 header[6]; - drflac_uint8 flags; - drflac_uint32 headerSize; + ma_uint8 header[6]; + ma_uint8 flags; + ma_uint32 headerSize; if (onRead(pUserData, header, 6) != 6) { - return DRFLAC_FALSE; + return MA_FALSE; } pInit->runningFilePos += 6; flags = header[1]; - DRFLAC_COPY_MEMORY(&headerSize, header+2, 4); - headerSize = drflac__unsynchsafe_32(drflac__be2host_32(headerSize)); + MA_DR_FLAC_COPY_MEMORY(&headerSize, header+2, 4); + headerSize = ma_dr_flac__unsynchsafe_32(ma_dr_flac__be2host_32(headerSize)); if (flags & 0x10) { headerSize += 10; } - if (!onSeek(pUserData, headerSize, drflac_seek_origin_current)) { - return DRFLAC_FALSE; + if (!onSeek(pUserData, headerSize, ma_dr_flac_seek_origin_current)) { + return MA_FALSE; } pInit->runningFilePos += headerSize; } else { @@ -86641,56 +86813,56 @@ static drflac_bool32 drflac__init_private(drflac_init_info* pInit, drflac_read_p } } if (id[0] == 'f' && id[1] == 'L' && id[2] == 'a' && id[3] == 'C') { - return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } -#ifndef DR_FLAC_NO_OGG +#ifndef MA_DR_FLAC_NO_OGG if (id[0] == 'O' && id[1] == 'g' && id[2] == 'g' && id[3] == 'S') { - return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } #endif if (relaxed) { - if (container == drflac_container_native) { - return drflac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); + if (container == ma_dr_flac_container_native) { + return ma_dr_flac__init_private__native(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } -#ifndef DR_FLAC_NO_OGG - if (container == drflac_container_ogg) { - return drflac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); +#ifndef MA_DR_FLAC_NO_OGG + if (container == ma_dr_flac_container_ogg) { + return ma_dr_flac__init_private__ogg(pInit, onRead, onSeek, onMeta, pUserData, pUserDataMD, relaxed); } #endif } - return DRFLAC_FALSE; + return MA_FALSE; } -static void drflac__init_from_info(drflac* pFlac, const drflac_init_info* pInit) +static void ma_dr_flac__init_from_info(ma_dr_flac* pFlac, const ma_dr_flac_init_info* pInit) { - DRFLAC_ASSERT(pFlac != NULL); - DRFLAC_ASSERT(pInit != NULL); - DRFLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac)); + MA_DR_FLAC_ASSERT(pFlac != NULL); + MA_DR_FLAC_ASSERT(pInit != NULL); + MA_DR_FLAC_ZERO_MEMORY(pFlac, sizeof(*pFlac)); pFlac->bs = pInit->bs; pFlac->onMeta = pInit->onMeta; pFlac->pUserDataMD = pInit->pUserDataMD; pFlac->maxBlockSizeInPCMFrames = pInit->maxBlockSizeInPCMFrames; pFlac->sampleRate = pInit->sampleRate; - pFlac->channels = (drflac_uint8)pInit->channels; - pFlac->bitsPerSample = (drflac_uint8)pInit->bitsPerSample; + pFlac->channels = (ma_uint8)pInit->channels; + pFlac->bitsPerSample = (ma_uint8)pInit->bitsPerSample; pFlac->totalPCMFrameCount = pInit->totalPCMFrameCount; pFlac->container = pInit->container; } -static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, void* pUserDataMD, const drflac_allocation_callbacks* pAllocationCallbacks) +static ma_dr_flac* ma_dr_flac_open_with_metadata_private(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, void* pUserDataMD, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac_init_info init; - drflac_uint32 allocationSize; - drflac_uint32 wholeSIMDVectorCountPerChannel; - drflac_uint32 decodedSamplesAllocationSize; -#ifndef DR_FLAC_NO_OGG - drflac_oggbs* pOggbs = NULL; + ma_dr_flac_init_info init; + ma_uint32 allocationSize; + ma_uint32 wholeSIMDVectorCountPerChannel; + ma_uint32 decodedSamplesAllocationSize; +#ifndef MA_DR_FLAC_NO_OGG + ma_dr_flac_oggbs* pOggbs = NULL; #endif - drflac_uint64 firstFramePos; - drflac_uint64 seektablePos; - drflac_uint32 seekpointCount; - drflac_allocation_callbacks allocationCallbacks; - drflac* pFlac; - drflac__init_cpu_caps(); - if (!drflac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { + ma_uint64 firstFramePos; + ma_uint64 seektablePos; + ma_uint32 seekpointCount; + ma_allocation_callbacks allocationCallbacks; + ma_dr_flac* pFlac; + ma_dr_flac__init_cpu_caps(); + if (!ma_dr_flac__init_private(&init, onRead, onSeek, onMeta, container, pUserData, pUserDataMD)) { return NULL; } if (pAllocationCallbacks != NULL) { @@ -86700,27 +86872,27 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac } } else { allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = drflac__malloc_default; - allocationCallbacks.onRealloc = drflac__realloc_default; - allocationCallbacks.onFree = drflac__free_default; + allocationCallbacks.onMalloc = ma_dr_flac__malloc_default; + allocationCallbacks.onRealloc = ma_dr_flac__realloc_default; + allocationCallbacks.onFree = ma_dr_flac__free_default; } - allocationSize = sizeof(drflac); - if ((init.maxBlockSizeInPCMFrames % (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) == 0) { - wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))); + allocationSize = sizeof(ma_dr_flac); + if ((init.maxBlockSizeInPCMFrames % (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) == 0) { + wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))); } else { - wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (DRFLAC_MAX_SIMD_VECTOR_SIZE / sizeof(drflac_int32))) + 1; + wholeSIMDVectorCountPerChannel = (init.maxBlockSizeInPCMFrames / (MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE / sizeof(ma_int32))) + 1; } - decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * DRFLAC_MAX_SIMD_VECTOR_SIZE * init.channels; + decodedSamplesAllocationSize = wholeSIMDVectorCountPerChannel * MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE * init.channels; allocationSize += decodedSamplesAllocationSize; - allocationSize += DRFLAC_MAX_SIMD_VECTOR_SIZE; -#ifndef DR_FLAC_NO_OGG - if (init.container == drflac_container_ogg) { - allocationSize += sizeof(drflac_oggbs); - pOggbs = (drflac_oggbs*)drflac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks); + allocationSize += MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE; +#ifndef MA_DR_FLAC_NO_OGG + if (init.container == ma_dr_flac_container_ogg) { + allocationSize += sizeof(ma_dr_flac_oggbs); + pOggbs = (ma_dr_flac_oggbs*)ma_dr_flac__malloc_from_callbacks(sizeof(*pOggbs), &allocationCallbacks); if (pOggbs == NULL) { return NULL; } - DRFLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); + MA_DR_FLAC_ZERO_MEMORY(pOggbs, sizeof(*pOggbs)); pOggbs->onRead = onRead; pOggbs->onSeek = onSeek; pOggbs->pUserData = pUserData; @@ -86735,49 +86907,49 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac seektablePos = 0; seekpointCount = 0; if (init.hasMetadataBlocks) { - drflac_read_proc onReadOverride = onRead; - drflac_seek_proc onSeekOverride = onSeek; + ma_dr_flac_read_proc onReadOverride = onRead; + ma_dr_flac_seek_proc onSeekOverride = onSeek; void* pUserDataOverride = pUserData; -#ifndef DR_FLAC_NO_OGG - if (init.container == drflac_container_ogg) { - onReadOverride = drflac__on_read_ogg; - onSeekOverride = drflac__on_seek_ogg; +#ifndef MA_DR_FLAC_NO_OGG + if (init.container == ma_dr_flac_container_ogg) { + onReadOverride = ma_dr_flac__on_read_ogg; + onSeekOverride = ma_dr_flac__on_seek_ogg; pUserDataOverride = (void*)pOggbs; } #endif - if (!drflac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { - #ifndef DR_FLAC_NO_OGG - drflac__free_from_callbacks(pOggbs, &allocationCallbacks); + if (!ma_dr_flac__read_and_decode_metadata(onReadOverride, onSeekOverride, onMeta, pUserDataOverride, pUserDataMD, &firstFramePos, &seektablePos, &seekpointCount, &allocationCallbacks)) { + #ifndef MA_DR_FLAC_NO_OGG + ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); #endif return NULL; } - allocationSize += seekpointCount * sizeof(drflac_seekpoint); + allocationSize += seekpointCount * sizeof(ma_dr_flac_seekpoint); } - pFlac = (drflac*)drflac__malloc_from_callbacks(allocationSize, &allocationCallbacks); + pFlac = (ma_dr_flac*)ma_dr_flac__malloc_from_callbacks(allocationSize, &allocationCallbacks); if (pFlac == NULL) { - #ifndef DR_FLAC_NO_OGG - drflac__free_from_callbacks(pOggbs, &allocationCallbacks); + #ifndef MA_DR_FLAC_NO_OGG + ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); #endif return NULL; } - drflac__init_from_info(pFlac, &init); + ma_dr_flac__init_from_info(pFlac, &init); pFlac->allocationCallbacks = allocationCallbacks; - pFlac->pDecodedSamples = (drflac_int32*)drflac_align((size_t)pFlac->pExtraData, DRFLAC_MAX_SIMD_VECTOR_SIZE); -#ifndef DR_FLAC_NO_OGG - if (init.container == drflac_container_ogg) { - drflac_oggbs* pInternalOggbs = (drflac_oggbs*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(drflac_seekpoint))); - DRFLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs)); - drflac__free_from_callbacks(pOggbs, &allocationCallbacks); + pFlac->pDecodedSamples = (ma_int32*)ma_dr_flac_align((size_t)pFlac->pExtraData, MA_DR_FLAC_MAX_SIMD_VECTOR_SIZE); +#ifndef MA_DR_FLAC_NO_OGG + if (init.container == ma_dr_flac_container_ogg) { + ma_dr_flac_oggbs* pInternalOggbs = (ma_dr_flac_oggbs*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize + (seekpointCount * sizeof(ma_dr_flac_seekpoint))); + MA_DR_FLAC_COPY_MEMORY(pInternalOggbs, pOggbs, sizeof(*pOggbs)); + ma_dr_flac__free_from_callbacks(pOggbs, &allocationCallbacks); pOggbs = NULL; - pFlac->bs.onRead = drflac__on_read_ogg; - pFlac->bs.onSeek = drflac__on_seek_ogg; + pFlac->bs.onRead = ma_dr_flac__on_read_ogg; + pFlac->bs.onSeek = ma_dr_flac__on_seek_ogg; pFlac->bs.pUserData = (void*)pInternalOggbs; pFlac->_oggbs = (void*)pInternalOggbs; } #endif pFlac->firstFLACFramePosInBytes = firstFramePos; -#ifndef DR_FLAC_NO_OGG - if (init.container == drflac_container_ogg) +#ifndef MA_DR_FLAC_NO_OGG + if (init.container == ma_dr_flac_container_ogg) { pFlac->pSeekpoints = NULL; pFlac->seekpointCount = 0; @@ -86787,24 +86959,24 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac { if (seektablePos != 0) { pFlac->seekpointCount = seekpointCount; - pFlac->pSeekpoints = (drflac_seekpoint*)((drflac_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); - DRFLAC_ASSERT(pFlac->bs.onSeek != NULL); - DRFLAC_ASSERT(pFlac->bs.onRead != NULL); - if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, drflac_seek_origin_start)) { - drflac_uint32 iSeekpoint; + pFlac->pSeekpoints = (ma_dr_flac_seekpoint*)((ma_uint8*)pFlac->pDecodedSamples + decodedSamplesAllocationSize); + MA_DR_FLAC_ASSERT(pFlac->bs.onSeek != NULL); + MA_DR_FLAC_ASSERT(pFlac->bs.onRead != NULL); + if (pFlac->bs.onSeek(pFlac->bs.pUserData, (int)seektablePos, ma_dr_flac_seek_origin_start)) { + ma_uint32 iSeekpoint; for (iSeekpoint = 0; iSeekpoint < seekpointCount; iSeekpoint += 1) { - if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, DRFLAC_SEEKPOINT_SIZE_IN_BYTES) == DRFLAC_SEEKPOINT_SIZE_IN_BYTES) { - pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); - pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = drflac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); - pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = drflac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); + if (pFlac->bs.onRead(pFlac->bs.pUserData, pFlac->pSeekpoints + iSeekpoint, MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) == MA_DR_FLAC_SEEKPOINT_SIZE_IN_BYTES) { + pFlac->pSeekpoints[iSeekpoint].firstPCMFrame = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].firstPCMFrame); + pFlac->pSeekpoints[iSeekpoint].flacFrameOffset = ma_dr_flac__be2host_64(pFlac->pSeekpoints[iSeekpoint].flacFrameOffset); + pFlac->pSeekpoints[iSeekpoint].pcmFrameCount = ma_dr_flac__be2host_16(pFlac->pSeekpoints[iSeekpoint].pcmFrameCount); } else { pFlac->pSeekpoints = NULL; pFlac->seekpointCount = 0; break; } } - if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, drflac_seek_origin_start)) { - drflac__free_from_callbacks(pFlac, &allocationCallbacks); + if (!pFlac->bs.onSeek(pFlac->bs.pUserData, (int)pFlac->firstFLACFramePosInBytes, ma_dr_flac_seek_origin_start)) { + ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); return NULL; } } else { @@ -86816,18 +86988,18 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac if (!init.hasStreamInfoBlock) { pFlac->currentFLACFrame.header = init.firstFrameHeader; for (;;) { - drflac_result result = drflac__decode_flac_frame(pFlac); - if (result == DRFLAC_SUCCESS) { + ma_result result = ma_dr_flac__decode_flac_frame(pFlac); + if (result == MA_SUCCESS) { break; } else { - if (result == DRFLAC_CRC_MISMATCH) { - if (!drflac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { - drflac__free_from_callbacks(pFlac, &allocationCallbacks); + if (result == MA_CRC_MISMATCH) { + if (!ma_dr_flac__read_next_flac_frame_header(&pFlac->bs, pFlac->bitsPerSample, &pFlac->currentFLACFrame.header)) { + ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); return NULL; } continue; } else { - drflac__free_from_callbacks(pFlac, &allocationCallbacks); + ma_dr_flac__free_from_callbacks(pFlac, &allocationCallbacks); return NULL; } } @@ -86835,555 +87007,43 @@ static drflac* drflac_open_with_metadata_private(drflac_read_proc onRead, drflac } return pFlac; } -#ifndef DR_FLAC_NO_STDIO +#ifndef MA_DR_FLAC_NO_STDIO #include -#ifndef DR_FLAC_NO_WCHAR +#ifndef MA_DR_FLAC_NO_WCHAR #include #endif -#include -static drflac_result drflac_result_from_errno(int e) -{ - switch (e) - { - case 0: return DRFLAC_SUCCESS; - #ifdef EPERM - case EPERM: return DRFLAC_INVALID_OPERATION; - #endif - #ifdef ENOENT - case ENOENT: return DRFLAC_DOES_NOT_EXIST; - #endif - #ifdef ESRCH - case ESRCH: return DRFLAC_DOES_NOT_EXIST; - #endif - #ifdef EINTR - case EINTR: return DRFLAC_INTERRUPT; - #endif - #ifdef EIO - case EIO: return DRFLAC_IO_ERROR; - #endif - #ifdef ENXIO - case ENXIO: return DRFLAC_DOES_NOT_EXIST; - #endif - #ifdef E2BIG - case E2BIG: return DRFLAC_INVALID_ARGS; - #endif - #ifdef ENOEXEC - case ENOEXEC: return DRFLAC_INVALID_FILE; - #endif - #ifdef EBADF - case EBADF: return DRFLAC_INVALID_FILE; - #endif - #ifdef ECHILD - case ECHILD: return DRFLAC_ERROR; - #endif - #ifdef EAGAIN - case EAGAIN: return DRFLAC_UNAVAILABLE; - #endif - #ifdef ENOMEM - case ENOMEM: return DRFLAC_OUT_OF_MEMORY; - #endif - #ifdef EACCES - case EACCES: return DRFLAC_ACCESS_DENIED; - #endif - #ifdef EFAULT - case EFAULT: return DRFLAC_BAD_ADDRESS; - #endif - #ifdef ENOTBLK - case ENOTBLK: return DRFLAC_ERROR; - #endif - #ifdef EBUSY - case EBUSY: return DRFLAC_BUSY; - #endif - #ifdef EEXIST - case EEXIST: return DRFLAC_ALREADY_EXISTS; - #endif - #ifdef EXDEV - case EXDEV: return DRFLAC_ERROR; - #endif - #ifdef ENODEV - case ENODEV: return DRFLAC_DOES_NOT_EXIST; - #endif - #ifdef ENOTDIR - case ENOTDIR: return DRFLAC_NOT_DIRECTORY; - #endif - #ifdef EISDIR - case EISDIR: return DRFLAC_IS_DIRECTORY; - #endif - #ifdef EINVAL - case EINVAL: return DRFLAC_INVALID_ARGS; - #endif - #ifdef ENFILE - case ENFILE: return DRFLAC_TOO_MANY_OPEN_FILES; - #endif - #ifdef EMFILE - case EMFILE: return DRFLAC_TOO_MANY_OPEN_FILES; - #endif - #ifdef ENOTTY - case ENOTTY: return DRFLAC_INVALID_OPERATION; - #endif - #ifdef ETXTBSY - case ETXTBSY: return DRFLAC_BUSY; - #endif - #ifdef EFBIG - case EFBIG: return DRFLAC_TOO_BIG; - #endif - #ifdef ENOSPC - case ENOSPC: return DRFLAC_NO_SPACE; - #endif - #ifdef ESPIPE - case ESPIPE: return DRFLAC_BAD_SEEK; - #endif - #ifdef EROFS - case EROFS: return DRFLAC_ACCESS_DENIED; - #endif - #ifdef EMLINK - case EMLINK: return DRFLAC_TOO_MANY_LINKS; - #endif - #ifdef EPIPE - case EPIPE: return DRFLAC_BAD_PIPE; - #endif - #ifdef EDOM - case EDOM: return DRFLAC_OUT_OF_RANGE; - #endif - #ifdef ERANGE - case ERANGE: return DRFLAC_OUT_OF_RANGE; - #endif - #ifdef EDEADLK - case EDEADLK: return DRFLAC_DEADLOCK; - #endif - #ifdef ENAMETOOLONG - case ENAMETOOLONG: return DRFLAC_PATH_TOO_LONG; - #endif - #ifdef ENOLCK - case ENOLCK: return DRFLAC_ERROR; - #endif - #ifdef ENOSYS - case ENOSYS: return DRFLAC_NOT_IMPLEMENTED; - #endif - #ifdef ENOTEMPTY - case ENOTEMPTY: return DRFLAC_DIRECTORY_NOT_EMPTY; - #endif - #ifdef ELOOP - case ELOOP: return DRFLAC_TOO_MANY_LINKS; - #endif - #ifdef ENOMSG - case ENOMSG: return DRFLAC_NO_MESSAGE; - #endif - #ifdef EIDRM - case EIDRM: return DRFLAC_ERROR; - #endif - #ifdef ECHRNG - case ECHRNG: return DRFLAC_ERROR; - #endif - #ifdef EL2NSYNC - case EL2NSYNC: return DRFLAC_ERROR; - #endif - #ifdef EL3HLT - case EL3HLT: return DRFLAC_ERROR; - #endif - #ifdef EL3RST - case EL3RST: return DRFLAC_ERROR; - #endif - #ifdef ELNRNG - case ELNRNG: return DRFLAC_OUT_OF_RANGE; - #endif - #ifdef EUNATCH - case EUNATCH: return DRFLAC_ERROR; - #endif - #ifdef ENOCSI - case ENOCSI: return DRFLAC_ERROR; - #endif - #ifdef EL2HLT - case EL2HLT: return DRFLAC_ERROR; - #endif - #ifdef EBADE - case EBADE: return DRFLAC_ERROR; - #endif - #ifdef EBADR - case EBADR: return DRFLAC_ERROR; - #endif - #ifdef EXFULL - case EXFULL: return DRFLAC_ERROR; - #endif - #ifdef ENOANO - case ENOANO: return DRFLAC_ERROR; - #endif - #ifdef EBADRQC - case EBADRQC: return DRFLAC_ERROR; - #endif - #ifdef EBADSLT - case EBADSLT: return DRFLAC_ERROR; - #endif - #ifdef EBFONT - case EBFONT: return DRFLAC_INVALID_FILE; - #endif - #ifdef ENOSTR - case ENOSTR: return DRFLAC_ERROR; - #endif - #ifdef ENODATA - case ENODATA: return DRFLAC_NO_DATA_AVAILABLE; - #endif - #ifdef ETIME - case ETIME: return DRFLAC_TIMEOUT; - #endif - #ifdef ENOSR - case ENOSR: return DRFLAC_NO_DATA_AVAILABLE; - #endif - #ifdef ENONET - case ENONET: return DRFLAC_NO_NETWORK; - #endif - #ifdef ENOPKG - case ENOPKG: return DRFLAC_ERROR; - #endif - #ifdef EREMOTE - case EREMOTE: return DRFLAC_ERROR; - #endif - #ifdef ENOLINK - case ENOLINK: return DRFLAC_ERROR; - #endif - #ifdef EADV - case EADV: return DRFLAC_ERROR; - #endif - #ifdef ESRMNT - case ESRMNT: return DRFLAC_ERROR; - #endif - #ifdef ECOMM - case ECOMM: return DRFLAC_ERROR; - #endif - #ifdef EPROTO - case EPROTO: return DRFLAC_ERROR; - #endif - #ifdef EMULTIHOP - case EMULTIHOP: return DRFLAC_ERROR; - #endif - #ifdef EDOTDOT - case EDOTDOT: return DRFLAC_ERROR; - #endif - #ifdef EBADMSG - case EBADMSG: return DRFLAC_BAD_MESSAGE; - #endif - #ifdef EOVERFLOW - case EOVERFLOW: return DRFLAC_TOO_BIG; - #endif - #ifdef ENOTUNIQ - case ENOTUNIQ: return DRFLAC_NOT_UNIQUE; - #endif - #ifdef EBADFD - case EBADFD: return DRFLAC_ERROR; - #endif - #ifdef EREMCHG - case EREMCHG: return DRFLAC_ERROR; - #endif - #ifdef ELIBACC - case ELIBACC: return DRFLAC_ACCESS_DENIED; - #endif - #ifdef ELIBBAD - case ELIBBAD: return DRFLAC_INVALID_FILE; - #endif - #ifdef ELIBSCN - case ELIBSCN: return DRFLAC_INVALID_FILE; - #endif - #ifdef ELIBMAX - case ELIBMAX: return DRFLAC_ERROR; - #endif - #ifdef ELIBEXEC - case ELIBEXEC: return DRFLAC_ERROR; - #endif - #ifdef EILSEQ - case EILSEQ: return DRFLAC_INVALID_DATA; - #endif - #ifdef ERESTART - case ERESTART: return DRFLAC_ERROR; - #endif - #ifdef ESTRPIPE - case ESTRPIPE: return DRFLAC_ERROR; - #endif - #ifdef EUSERS - case EUSERS: return DRFLAC_ERROR; - #endif - #ifdef ENOTSOCK - case ENOTSOCK: return DRFLAC_NOT_SOCKET; - #endif - #ifdef EDESTADDRREQ - case EDESTADDRREQ: return DRFLAC_NO_ADDRESS; - #endif - #ifdef EMSGSIZE - case EMSGSIZE: return DRFLAC_TOO_BIG; - #endif - #ifdef EPROTOTYPE - case EPROTOTYPE: return DRFLAC_BAD_PROTOCOL; - #endif - #ifdef ENOPROTOOPT - case ENOPROTOOPT: return DRFLAC_PROTOCOL_UNAVAILABLE; - #endif - #ifdef EPROTONOSUPPORT - case EPROTONOSUPPORT: return DRFLAC_PROTOCOL_NOT_SUPPORTED; - #endif - #ifdef ESOCKTNOSUPPORT - case ESOCKTNOSUPPORT: return DRFLAC_SOCKET_NOT_SUPPORTED; - #endif - #ifdef EOPNOTSUPP - case EOPNOTSUPP: return DRFLAC_INVALID_OPERATION; - #endif - #ifdef EPFNOSUPPORT - case EPFNOSUPPORT: return DRFLAC_PROTOCOL_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EAFNOSUPPORT - case EAFNOSUPPORT: return DRFLAC_ADDRESS_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EADDRINUSE - case EADDRINUSE: return DRFLAC_ALREADY_IN_USE; - #endif - #ifdef EADDRNOTAVAIL - case EADDRNOTAVAIL: return DRFLAC_ERROR; - #endif - #ifdef ENETDOWN - case ENETDOWN: return DRFLAC_NO_NETWORK; - #endif - #ifdef ENETUNREACH - case ENETUNREACH: return DRFLAC_NO_NETWORK; - #endif - #ifdef ENETRESET - case ENETRESET: return DRFLAC_NO_NETWORK; - #endif - #ifdef ECONNABORTED - case ECONNABORTED: return DRFLAC_NO_NETWORK; - #endif - #ifdef ECONNRESET - case ECONNRESET: return DRFLAC_CONNECTION_RESET; - #endif - #ifdef ENOBUFS - case ENOBUFS: return DRFLAC_NO_SPACE; - #endif - #ifdef EISCONN - case EISCONN: return DRFLAC_ALREADY_CONNECTED; - #endif - #ifdef ENOTCONN - case ENOTCONN: return DRFLAC_NOT_CONNECTED; - #endif - #ifdef ESHUTDOWN - case ESHUTDOWN: return DRFLAC_ERROR; - #endif - #ifdef ETOOMANYREFS - case ETOOMANYREFS: return DRFLAC_ERROR; - #endif - #ifdef ETIMEDOUT - case ETIMEDOUT: return DRFLAC_TIMEOUT; - #endif - #ifdef ECONNREFUSED - case ECONNREFUSED: return DRFLAC_CONNECTION_REFUSED; - #endif - #ifdef EHOSTDOWN - case EHOSTDOWN: return DRFLAC_NO_HOST; - #endif - #ifdef EHOSTUNREACH - case EHOSTUNREACH: return DRFLAC_NO_HOST; - #endif - #ifdef EALREADY - case EALREADY: return DRFLAC_IN_PROGRESS; - #endif - #ifdef EINPROGRESS - case EINPROGRESS: return DRFLAC_IN_PROGRESS; - #endif - #ifdef ESTALE - case ESTALE: return DRFLAC_INVALID_FILE; - #endif - #ifdef EUCLEAN - case EUCLEAN: return DRFLAC_ERROR; - #endif - #ifdef ENOTNAM - case ENOTNAM: return DRFLAC_ERROR; - #endif - #ifdef ENAVAIL - case ENAVAIL: return DRFLAC_ERROR; - #endif - #ifdef EISNAM - case EISNAM: return DRFLAC_ERROR; - #endif - #ifdef EREMOTEIO - case EREMOTEIO: return DRFLAC_IO_ERROR; - #endif - #ifdef EDQUOT - case EDQUOT: return DRFLAC_NO_SPACE; - #endif - #ifdef ENOMEDIUM - case ENOMEDIUM: return DRFLAC_DOES_NOT_EXIST; - #endif - #ifdef EMEDIUMTYPE - case EMEDIUMTYPE: return DRFLAC_ERROR; - #endif - #ifdef ECANCELED - case ECANCELED: return DRFLAC_CANCELLED; - #endif - #ifdef ENOKEY - case ENOKEY: return DRFLAC_ERROR; - #endif - #ifdef EKEYEXPIRED - case EKEYEXPIRED: return DRFLAC_ERROR; - #endif - #ifdef EKEYREVOKED - case EKEYREVOKED: return DRFLAC_ERROR; - #endif - #ifdef EKEYREJECTED - case EKEYREJECTED: return DRFLAC_ERROR; - #endif - #ifdef EOWNERDEAD - case EOWNERDEAD: return DRFLAC_ERROR; - #endif - #ifdef ENOTRECOVERABLE - case ENOTRECOVERABLE: return DRFLAC_ERROR; - #endif - #ifdef ERFKILL - case ERFKILL: return DRFLAC_ERROR; - #endif - #ifdef EHWPOISON - case EHWPOISON: return DRFLAC_ERROR; - #endif - default: return DRFLAC_ERROR; - } -} -static drflac_result drflac_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) -{ -#if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err; -#endif - if (ppFile != NULL) { - *ppFile = NULL; - } - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return DRFLAC_INVALID_ARGS; - } -#if defined(_MSC_VER) && _MSC_VER >= 1400 - err = fopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return drflac_result_from_errno(err); - } -#else -#if defined(_WIN32) || defined(__APPLE__) - *ppFile = fopen(pFilePath, pOpenMode); -#else - #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) - *ppFile = fopen64(pFilePath, pOpenMode); - #else - *ppFile = fopen(pFilePath, pOpenMode); - #endif -#endif - if (*ppFile == NULL) { - drflac_result result = drflac_result_from_errno(errno); - if (result == DRFLAC_SUCCESS) { - result = DRFLAC_ERROR; - } - return result; - } -#endif - return DRFLAC_SUCCESS; -} -#if defined(_WIN32) - #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) - #define DRFLAC_HAS_WFOPEN - #endif -#endif -#ifndef DR_FLAC_NO_WCHAR -static drflac_result drflac_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drflac_allocation_callbacks* pAllocationCallbacks) -{ - if (ppFile != NULL) { - *ppFile = NULL; - } - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return DRFLAC_INVALID_ARGS; - } -#if defined(DRFLAC_HAS_WFOPEN) - { - #if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return drflac_result_from_errno(err); - } - #else - *ppFile = _wfopen(pFilePath, pOpenMode); - if (*ppFile == NULL) { - return drflac_result_from_errno(errno); - } - #endif - (void)pAllocationCallbacks; - } -#else - #if defined(__DJGPP__) - { - } - #else - { - mbstate_t mbs; - size_t lenMB; - const wchar_t* pFilePathTemp = pFilePath; - char* pFilePathMB = NULL; - char pOpenModeMB[32] = {0}; - DRFLAC_ZERO_OBJECT(&mbs); - lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); - if (lenMB == (size_t)-1) { - return drflac_result_from_errno(errno); - } - pFilePathMB = (char*)drflac__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); - if (pFilePathMB == NULL) { - return DRFLAC_OUT_OF_MEMORY; - } - pFilePathTemp = pFilePath; - DRFLAC_ZERO_OBJECT(&mbs); - wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); - { - size_t i = 0; - for (;;) { - if (pOpenMode[i] == 0) { - pOpenModeMB[i] = '\0'; - break; - } - pOpenModeMB[i] = (char)pOpenMode[i]; - i += 1; - } - } - *ppFile = fopen(pFilePathMB, pOpenModeMB); - drflac__free_from_callbacks(pFilePathMB, pAllocationCallbacks); - } - #endif - if (*ppFile == NULL) { - return DRFLAC_ERROR; - } -#endif - return DRFLAC_SUCCESS; -} -#endif -static size_t drflac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) +static size_t ma_dr_flac__on_read_stdio(void* pUserData, void* bufferOut, size_t bytesToRead) { return fread(bufferOut, 1, bytesToRead, (FILE*)pUserData); } -static drflac_bool32 drflac__on_seek_stdio(void* pUserData, int offset, drflac_seek_origin origin) +static ma_bool32 ma_dr_flac__on_seek_stdio(void* pUserData, int offset, ma_dr_flac_seek_origin origin) { - DRFLAC_ASSERT(offset >= 0); - return fseek((FILE*)pUserData, offset, (origin == drflac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; + MA_DR_FLAC_ASSERT(offset >= 0); + return fseek((FILE*)pUserData, offset, (origin == ma_dr_flac_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; } -DRFLAC_API drflac* drflac_open_file(const char* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API ma_dr_flac* ma_dr_flac_open_file(const char* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; FILE* pFile; - if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) { + if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { return NULL; } - pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return NULL; } return pFlac; } -#ifndef DR_FLAC_NO_WCHAR -DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_allocation_callbacks* pAllocationCallbacks) +#ifndef MA_DR_FLAC_NO_WCHAR +MA_API ma_dr_flac* ma_dr_flac_open_file_w(const wchar_t* pFileName, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; FILE* pFile; - if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) { + if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { return NULL; } - pFlac = drflac_open(drflac__on_read_stdio, drflac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + pFlac = ma_dr_flac_open(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, (void*)pFile, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return NULL; @@ -87391,29 +87051,29 @@ DRFLAC_API drflac* drflac_open_file_w(const wchar_t* pFileName, const drflac_all return pFlac; } #endif -DRFLAC_API drflac* drflac_open_file_with_metadata(const char* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata(const char* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; FILE* pFile; - if (drflac_fopen(&pFile, pFileName, "rb") != DRFLAC_SUCCESS) { + if (ma_fopen(&pFile, pFileName, "rb") != MA_SUCCESS) { return NULL; } - pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); + pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return pFlac; } return pFlac; } -#ifndef DR_FLAC_NO_WCHAR -DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +#ifndef MA_DR_FLAC_NO_WCHAR +MA_API ma_dr_flac* ma_dr_flac_open_file_with_metadata_w(const wchar_t* pFileName, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; FILE* pFile; - if (drflac_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != DRFLAC_SUCCESS) { + if (ma_wfopen(&pFile, pFileName, L"rb", pAllocationCallbacks) != MA_SUCCESS) { return NULL; } - pFlac = drflac_open_with_metadata_private(drflac__on_read_stdio, drflac__on_seek_stdio, onMeta, drflac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); + pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_stdio, ma_dr_flac__on_seek_stdio, onMeta, ma_dr_flac_container_unknown, (void*)pFile, pUserData, pAllocationCallbacks); if (pFlac == NULL) { fclose(pFile); return pFlac; @@ -87422,61 +87082,61 @@ DRFLAC_API drflac* drflac_open_file_with_metadata_w(const wchar_t* pFileName, dr } #endif #endif -static size_t drflac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) +static size_t ma_dr_flac__on_read_memory(void* pUserData, void* bufferOut, size_t bytesToRead) { - drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; + ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; size_t bytesRemaining; - DRFLAC_ASSERT(memoryStream != NULL); - DRFLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos); + MA_DR_FLAC_ASSERT(memoryStream != NULL); + MA_DR_FLAC_ASSERT(memoryStream->dataSize >= memoryStream->currentReadPos); bytesRemaining = memoryStream->dataSize - memoryStream->currentReadPos; if (bytesToRead > bytesRemaining) { bytesToRead = bytesRemaining; } if (bytesToRead > 0) { - DRFLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); + MA_DR_FLAC_COPY_MEMORY(bufferOut, memoryStream->data + memoryStream->currentReadPos, bytesToRead); memoryStream->currentReadPos += bytesToRead; } return bytesToRead; } -static drflac_bool32 drflac__on_seek_memory(void* pUserData, int offset, drflac_seek_origin origin) +static ma_bool32 ma_dr_flac__on_seek_memory(void* pUserData, int offset, ma_dr_flac_seek_origin origin) { - drflac__memory_stream* memoryStream = (drflac__memory_stream*)pUserData; - DRFLAC_ASSERT(memoryStream != NULL); - DRFLAC_ASSERT(offset >= 0); - if (offset > (drflac_int64)memoryStream->dataSize) { - return DRFLAC_FALSE; + ma_dr_flac__memory_stream* memoryStream = (ma_dr_flac__memory_stream*)pUserData; + MA_DR_FLAC_ASSERT(memoryStream != NULL); + MA_DR_FLAC_ASSERT(offset >= 0); + if (offset > (ma_int64)memoryStream->dataSize) { + return MA_FALSE; } - if (origin == drflac_seek_origin_current) { + if (origin == ma_dr_flac_seek_origin_current) { if (memoryStream->currentReadPos + offset <= memoryStream->dataSize) { memoryStream->currentReadPos += offset; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } else { - if ((drflac_uint32)offset <= memoryStream->dataSize) { + if ((ma_uint32)offset <= memoryStream->dataSize) { memoryStream->currentReadPos = offset; } else { - return DRFLAC_FALSE; + return MA_FALSE; } } - return DRFLAC_TRUE; + return MA_TRUE; } -DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API ma_dr_flac* ma_dr_flac_open_memory(const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac__memory_stream memoryStream; - drflac* pFlac; - memoryStream.data = (const drflac_uint8*)pData; + ma_dr_flac__memory_stream memoryStream; + ma_dr_flac* pFlac; + memoryStream.data = (const ma_uint8*)pData; memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; - pFlac = drflac_open(drflac__on_read_memory, drflac__on_seek_memory, &memoryStream, pAllocationCallbacks); + pFlac = ma_dr_flac_open(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, &memoryStream, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } pFlac->memoryStream = memoryStream; -#ifndef DR_FLAC_NO_OGG - if (pFlac->container == drflac_container_ogg) +#ifndef MA_DR_FLAC_NO_OGG + if (pFlac->container == ma_dr_flac_container_ogg) { - drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; oggbs->pUserData = &pFlac->memoryStream; } else @@ -87486,22 +87146,22 @@ DRFLAC_API drflac* drflac_open_memory(const void* pData, size_t dataSize, const } return pFlac; } -DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t dataSize, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API ma_dr_flac* ma_dr_flac_open_memory_with_metadata(const void* pData, size_t dataSize, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac__memory_stream memoryStream; - drflac* pFlac; - memoryStream.data = (const drflac_uint8*)pData; + ma_dr_flac__memory_stream memoryStream; + ma_dr_flac* pFlac; + memoryStream.data = (const ma_uint8*)pData; memoryStream.dataSize = dataSize; memoryStream.currentReadPos = 0; - pFlac = drflac_open_with_metadata_private(drflac__on_read_memory, drflac__on_seek_memory, onMeta, drflac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); + pFlac = ma_dr_flac_open_with_metadata_private(ma_dr_flac__on_read_memory, ma_dr_flac__on_seek_memory, onMeta, ma_dr_flac_container_unknown, &memoryStream, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } pFlac->memoryStream = memoryStream; -#ifndef DR_FLAC_NO_OGG - if (pFlac->container == drflac_container_ogg) +#ifndef MA_DR_FLAC_NO_OGG + if (pFlac->container == ma_dr_flac_container_ogg) { - drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; oggbs->pUserData = &pFlac->memoryStream; } else @@ -87511,104 +87171,104 @@ DRFLAC_API drflac* drflac_open_memory_with_metadata(const void* pData, size_t da } return pFlac; } -DRFLAC_API drflac* drflac_open(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API ma_dr_flac* ma_dr_flac_open(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - return drflac_open_with_metadata_private(onRead, onSeek, NULL, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); } -DRFLAC_API drflac* drflac_open_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API ma_dr_flac* ma_dr_flac_open_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - return drflac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, NULL, container, pUserData, pUserData, pAllocationCallbacks); } -DRFLAC_API drflac* drflac_open_with_metadata(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - return drflac_open_with_metadata_private(onRead, onSeek, onMeta, drflac_container_unknown, pUserData, pUserData, pAllocationCallbacks); + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, ma_dr_flac_container_unknown, pUserData, pUserData, pAllocationCallbacks); } -DRFLAC_API drflac* drflac_open_with_metadata_relaxed(drflac_read_proc onRead, drflac_seek_proc onSeek, drflac_meta_proc onMeta, drflac_container container, void* pUserData, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API ma_dr_flac* ma_dr_flac_open_with_metadata_relaxed(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, ma_dr_flac_meta_proc onMeta, ma_dr_flac_container container, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - return drflac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); + return ma_dr_flac_open_with_metadata_private(onRead, onSeek, onMeta, container, pUserData, pUserData, pAllocationCallbacks); } -DRFLAC_API void drflac_close(drflac* pFlac) +MA_API void ma_dr_flac_close(ma_dr_flac* pFlac) { if (pFlac == NULL) { return; } -#ifndef DR_FLAC_NO_STDIO - if (pFlac->bs.onRead == drflac__on_read_stdio) { +#ifndef MA_DR_FLAC_NO_STDIO + if (pFlac->bs.onRead == ma_dr_flac__on_read_stdio) { fclose((FILE*)pFlac->bs.pUserData); } -#ifndef DR_FLAC_NO_OGG - if (pFlac->container == drflac_container_ogg) { - drflac_oggbs* oggbs = (drflac_oggbs*)pFlac->_oggbs; - DRFLAC_ASSERT(pFlac->bs.onRead == drflac__on_read_ogg); - if (oggbs->onRead == drflac__on_read_stdio) { +#ifndef MA_DR_FLAC_NO_OGG + if (pFlac->container == ma_dr_flac_container_ogg) { + ma_dr_flac_oggbs* oggbs = (ma_dr_flac_oggbs*)pFlac->_oggbs; + MA_DR_FLAC_ASSERT(pFlac->bs.onRead == ma_dr_flac__on_read_ogg); + if (oggbs->onRead == ma_dr_flac__on_read_stdio) { fclose((FILE*)oggbs->pUserData); } } #endif #endif - drflac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks); + ma_dr_flac__free_from_callbacks(pFlac, &pFlac->allocationCallbacks); } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; + ma_uint64 i; for (i = 0; i < frameCount; ++i) { - drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; + ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - drflac_uint32 right0 = left0 - side0; - drflac_uint32 right1 = left1 - side1; - drflac_uint32 right2 = left2 - side2; - drflac_uint32 right3 = left3 - side3; - pOutputSamples[i*8+0] = (drflac_int32)left0; - pOutputSamples[i*8+1] = (drflac_int32)right0; - pOutputSamples[i*8+2] = (drflac_int32)left1; - pOutputSamples[i*8+3] = (drflac_int32)right1; - pOutputSamples[i*8+4] = (drflac_int32)left2; - pOutputSamples[i*8+5] = (drflac_int32)right2; - pOutputSamples[i*8+6] = (drflac_int32)left3; - pOutputSamples[i*8+7] = (drflac_int32)right3; + ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 right0 = left0 - side0; + ma_uint32 right1 = left1 - side1; + ma_uint32 right2 = left2 - side2; + ma_uint32 right3 = left3 - side3; + pOutputSamples[i*8+0] = (ma_int32)left0; + pOutputSamples[i*8+1] = (ma_int32)right0; + pOutputSamples[i*8+2] = (ma_int32)left1; + pOutputSamples[i*8+3] = (ma_int32)right1; + pOutputSamples[i*8+4] = (ma_int32)left2; + pOutputSamples[i*8+5] = (ma_int32)right2; + pOutputSamples[i*8+6] = (ma_int32)left3; + pOutputSamples[i*8+7] = (ma_int32)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); @@ -87617,26 +87277,26 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__sse2(drf _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { @@ -87646,97 +87306,97 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side__neon(drf left = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); side = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); right = vsubq_u32(left, side); - drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); + ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; + ma_uint64 i; for (i = 0; i < frameCount; ++i) { - drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; + ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - drflac_uint32 left0 = right0 + side0; - drflac_uint32 left1 = right1 + side1; - drflac_uint32 left2 = right2 + side2; - drflac_uint32 left3 = right3 + side3; - pOutputSamples[i*8+0] = (drflac_int32)left0; - pOutputSamples[i*8+1] = (drflac_int32)right0; - pOutputSamples[i*8+2] = (drflac_int32)left1; - pOutputSamples[i*8+3] = (drflac_int32)right1; - pOutputSamples[i*8+4] = (drflac_int32)left2; - pOutputSamples[i*8+5] = (drflac_int32)right2; - pOutputSamples[i*8+6] = (drflac_int32)left3; - pOutputSamples[i*8+7] = (drflac_int32)right3; + ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 left0 = right0 + side0; + ma_uint32 left1 = right1 + side1; + ma_uint32 left2 = right2 + side2; + ma_uint32 left3 = right3 + side3; + pOutputSamples[i*8+0] = (ma_int32)left0; + pOutputSamples[i*8+1] = (ma_int32)right0; + pOutputSamples[i*8+2] = (ma_int32)left1; + pOutputSamples[i*8+3] = (ma_int32)right1; + pOutputSamples[i*8+4] = (ma_int32)left2; + pOutputSamples[i*8+5] = (ma_int32)right2; + pOutputSamples[i*8+6] = (ma_int32)left3; + pOutputSamples[i*8+7] = (ma_int32)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); for (i = 0; i < frameCount4; ++i) { __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); @@ -87745,26 +87405,26 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__sse2(dr _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { @@ -87774,74 +87434,74 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side__neon(dr side = vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift0_4); right = vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4); left = vaddq_u32(right, side); - drflac__vst2q_u32((drflac_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); + ma_dr_flac__vst2q_u32((ma_uint32*)pOutputSamples + i*8, vzipq_u32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (drflac_int32)left; - pOutputSamples[i*2+1] = (drflac_int32)right; + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left; + pOutputSamples[i*2+1] = (ma_int32)right; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - for (drflac_uint64 i = 0; i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (ma_uint64 i = 0; i < frameCount; ++i) { + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample); - pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_int32 shift = unusedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_int32 shift = unusedBitsPerSample; if (shift > 0) { shift -= 1; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 temp0L; - drflac_uint32 temp1L; - drflac_uint32 temp2L; - drflac_uint32 temp3L; - drflac_uint32 temp0R; - drflac_uint32 temp1R; - drflac_uint32 temp2R; - drflac_uint32 temp3R; - drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); @@ -87854,72 +87514,72 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__scalar(dr temp1R = (mid1 - side1) << shift; temp2R = (mid2 - side2) << shift; temp3R = (mid3 - side3) << shift; - pOutputSamples[i*8+0] = (drflac_int32)temp0L; - pOutputSamples[i*8+1] = (drflac_int32)temp0R; - pOutputSamples[i*8+2] = (drflac_int32)temp1L; - pOutputSamples[i*8+3] = (drflac_int32)temp1R; - pOutputSamples[i*8+4] = (drflac_int32)temp2L; - pOutputSamples[i*8+5] = (drflac_int32)temp2R; - pOutputSamples[i*8+6] = (drflac_int32)temp3L; - pOutputSamples[i*8+7] = (drflac_int32)temp3R; + pOutputSamples[i*8+0] = (ma_int32)temp0L; + pOutputSamples[i*8+1] = (ma_int32)temp0R; + pOutputSamples[i*8+2] = (ma_int32)temp1L; + pOutputSamples[i*8+3] = (ma_int32)temp1R; + pOutputSamples[i*8+4] = (ma_int32)temp2L; + pOutputSamples[i*8+5] = (ma_int32)temp2R; + pOutputSamples[i*8+6] = (ma_int32)temp3L; + pOutputSamples[i*8+7] = (ma_int32)temp3R; } } else { for (i = 0; i < frameCount4; ++i) { - drflac_uint32 temp0L; - drflac_uint32 temp1L; - drflac_uint32 temp2L; - drflac_uint32 temp3L; - drflac_uint32 temp0R; - drflac_uint32 temp1R; - drflac_uint32 temp2R; - drflac_uint32 temp3R; - drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1); - temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1); - temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1); - temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1); - temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1); - temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1); - temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1); - temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1); - pOutputSamples[i*8+0] = (drflac_int32)temp0L; - pOutputSamples[i*8+1] = (drflac_int32)temp0R; - pOutputSamples[i*8+2] = (drflac_int32)temp1L; - pOutputSamples[i*8+3] = (drflac_int32)temp1R; - pOutputSamples[i*8+4] = (drflac_int32)temp2L; - pOutputSamples[i*8+5] = (drflac_int32)temp2R; - pOutputSamples[i*8+6] = (drflac_int32)temp3L; - pOutputSamples[i*8+7] = (drflac_int32)temp3R; + temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1); + temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1); + temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1); + temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1); + temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1); + temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1); + temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1); + temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1); + pOutputSamples[i*8+0] = (ma_int32)temp0L; + pOutputSamples[i*8+1] = (ma_int32)temp0R; + pOutputSamples[i*8+2] = (ma_int32)temp1L; + pOutputSamples[i*8+3] = (ma_int32)temp1R; + pOutputSamples[i*8+4] = (ma_int32)temp2L; + pOutputSamples[i*8+5] = (ma_int32)temp2R; + pOutputSamples[i*8+6] = (ma_int32)temp3L; + pOutputSamples[i*8+7] = (ma_int32)temp3R; } } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample); - pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample); + pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample); } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_int32 shift = unusedBitsPerSample; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_int32 shift = unusedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); if (shift == 0) { for (i = 0; i < frameCount4; ++i) { __m128i mid; @@ -87935,11 +87595,11 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__sse2(drfl _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1; - pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1; + pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1; + pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1; } } else { shift -= 1; @@ -87957,27 +87617,27 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__sse2(drfl _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift); - pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift); + pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift); + pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift); } } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_int32 shift = unusedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_int32 shift = unusedBitsPerSample; int32x4_t wbpsShift0_4; int32x4_t wbpsShift1_4; uint32x4_t one4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); one4 = vdupq_n_u32(1); @@ -87992,14 +87652,14 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__neon(drfl mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); left = vshrq_n_s32(vreinterpretq_s32_u32(vaddq_u32(mid, side)), 1); right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); - drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)(mid + side) >> 1; - pOutputSamples[i*2+1] = (drflac_int32)(mid - side) >> 1; + pOutputSamples[i*2+0] = (ma_int32)(mid + side) >> 1; + pOutputSamples[i*2+1] = (ma_int32)(mid - side) >> 1; } } else { int32x4_t shift4; @@ -88015,86 +87675,86 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side__neon(drfl mid = vorrq_u32(vshlq_n_u32(mid, 1), vandq_u32(side, one4)); left = vreinterpretq_s32_u32(vshlq_u32(vaddq_u32(mid, side), shift4)); right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); - drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift); - pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift); + pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift); + pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift); } } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - for (drflac_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)); - pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)); + for (ma_uint64 i = 0; i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)); + pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; - pOutputSamples[i*8+0] = (drflac_int32)tempL0; - pOutputSamples[i*8+1] = (drflac_int32)tempR0; - pOutputSamples[i*8+2] = (drflac_int32)tempL1; - pOutputSamples[i*8+3] = (drflac_int32)tempR1; - pOutputSamples[i*8+4] = (drflac_int32)tempL2; - pOutputSamples[i*8+5] = (drflac_int32)tempR2; - pOutputSamples[i*8+6] = (drflac_int32)tempL3; - pOutputSamples[i*8+7] = (drflac_int32)tempR3; + ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + pOutputSamples[i*8+0] = (ma_int32)tempL0; + pOutputSamples[i*8+1] = (ma_int32)tempR0; + pOutputSamples[i*8+2] = (ma_int32)tempL1; + pOutputSamples[i*8+3] = (ma_int32)tempR1; + pOutputSamples[i*8+4] = (ma_int32)tempL2; + pOutputSamples[i*8+5] = (ma_int32)tempR2; + pOutputSamples[i*8+6] = (ma_int32)tempL3; + pOutputSamples[i*8+7] = (ma_int32)tempR3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); @@ -88102,20 +87762,20 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo_ _mm_storeu_si128((__m128i*)(pOutputSamples + i*8 + 4), _mm_unpackhi_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift4_0 = vdupq_n_s32(shift0); int32x4_t shift4_1 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { @@ -88123,87 +87783,87 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo_ int32x4_t right; left = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples0U32 + i*4), shift4_0)); right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift4_1)); - drflac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); + ma_dr_flac__vst2q_s32(pOutputSamples + i*8, vzipq_s32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0); - pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1); + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0); + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int32* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int32* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } -DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 framesToRead, drflac_int32* pBufferOut) +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s32(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int32* pBufferOut) { - drflac_uint64 framesRead; - drflac_uint32 unusedBitsPerSample; + ma_uint64 framesRead; + ma_uint32 unusedBitsPerSample; if (pFlac == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { - return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); } - DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); unusedBitsPerSample = 32 - pFlac->bitsPerSample; framesRead = 0; while (framesToRead > 0) { if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!drflac__read_and_decode_next_flac_frame(pFlac)) { + if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { break; } } else { - unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - drflac_uint64 frameCountThisIteration = framesToRead; + unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; + ma_uint64 frameCountThisIteration = framesToRead; if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; } if (channelCount == 2) { - const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; + const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; + const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; switch (pFlac->currentFLACFrame.header.channelAssignment) { - case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: { - drflac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_s32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: { - drflac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_s32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: { - drflac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_s32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: default: { - drflac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_s32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; } } else { - drflac_uint64 i; + ma_uint64 i; for (i = 0; i < frameCountThisIteration; ++i) { unsigned int j; for (j = 0; j < channelCount; ++j) { - pBufferOut[(i*channelCount)+j] = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + pBufferOut[(i*channelCount)+j] = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); } } } @@ -88211,47 +87871,47 @@ DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s32(drflac* pFlac, drflac_uint64 pBufferOut += frameCountThisIteration * channelCount; framesToRead -= frameCountThisIteration; pFlac->currentPCMFrame += frameCountThisIteration; - pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration; + pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration; } } return framesRead; } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; + ma_uint64 i; for (i = 0; i < frameCount; ++i) { - drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_uint32 right = left - side; + ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 right = left - side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - drflac_uint32 right0 = left0 - side0; - drflac_uint32 right1 = left1 - side1; - drflac_uint32 right2 = left2 - side2; - drflac_uint32 right3 = left3 - side3; + ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 right0 = left0 - side0; + ma_uint32 right1 = left1 - side1; + ma_uint32 right2 = left2 - side2; + ma_uint32 right3 = left3 - side3; left0 >>= 16; left1 >>= 16; left2 >>= 16; @@ -88260,66 +87920,66 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__scalar(d right1 >>= 16; right2 >>= 16; right3 >>= 16; - pOutputSamples[i*8+0] = (drflac_int16)left0; - pOutputSamples[i*8+1] = (drflac_int16)right0; - pOutputSamples[i*8+2] = (drflac_int16)left1; - pOutputSamples[i*8+3] = (drflac_int16)right1; - pOutputSamples[i*8+4] = (drflac_int16)left2; - pOutputSamples[i*8+5] = (drflac_int16)right2; - pOutputSamples[i*8+6] = (drflac_int16)left3; - pOutputSamples[i*8+7] = (drflac_int16)right3; + pOutputSamples[i*8+0] = (ma_int16)left0; + pOutputSamples[i*8+1] = (ma_int16)right0; + pOutputSamples[i*8+2] = (ma_int16)left1; + pOutputSamples[i*8+3] = (ma_int16)right1; + pOutputSamples[i*8+4] = (ma_int16)left2; + pOutputSamples[i*8+5] = (ma_int16)right2; + pOutputSamples[i*8+6] = (ma_int16)left3; + pOutputSamples[i*8+7] = (ma_int16)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); __m128i right = _mm_sub_epi32(left, side); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { @@ -88331,74 +87991,74 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side__neon(drf right = vsubq_u32(left, side); left = vshrq_n_u32(left, 16); right = vshrq_n_u32(right, 16); - drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); + ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s16__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s16__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; + ma_uint64 i; for (i = 0; i < frameCount; ++i) { - drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_uint32 left = right + side; + ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 left = right + side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - drflac_uint32 left0 = right0 + side0; - drflac_uint32 left1 = right1 + side1; - drflac_uint32 left2 = right2 + side2; - drflac_uint32 left3 = right3 + side3; + ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 left0 = right0 + side0; + ma_uint32 left1 = right1 + side1; + ma_uint32 left2 = right2 + side2; + ma_uint32 left3 = right3 + side3; left0 >>= 16; left1 >>= 16; left2 >>= 16; @@ -88407,66 +88067,66 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__scalar( right1 >>= 16; right2 >>= 16; right3 >>= 16; - pOutputSamples[i*8+0] = (drflac_int16)left0; - pOutputSamples[i*8+1] = (drflac_int16)right0; - pOutputSamples[i*8+2] = (drflac_int16)left1; - pOutputSamples[i*8+3] = (drflac_int16)right1; - pOutputSamples[i*8+4] = (drflac_int16)left2; - pOutputSamples[i*8+5] = (drflac_int16)right2; - pOutputSamples[i*8+6] = (drflac_int16)left3; - pOutputSamples[i*8+7] = (drflac_int16)right3; + pOutputSamples[i*8+0] = (ma_int16)left0; + pOutputSamples[i*8+1] = (ma_int16)right0; + pOutputSamples[i*8+2] = (ma_int16)left1; + pOutputSamples[i*8+3] = (ma_int16)right1; + pOutputSamples[i*8+4] = (ma_int16)left2; + pOutputSamples[i*8+5] = (ma_int16)right2; + pOutputSamples[i*8+6] = (ma_int16)left3; + pOutputSamples[i*8+7] = (ma_int16)right3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); for (i = 0; i < frameCount4; ++i) { __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); __m128i left = _mm_add_epi32(right, side); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4; int32x4_t shift1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { @@ -88478,76 +88138,76 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side__neon(dr left = vaddq_u32(right, side); left = vshrq_n_u32(left, 16); right = vshrq_n_u32(right, 16); - drflac__vst2q_u16((drflac_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); + ma_dr_flac__vst2q_u16((ma_uint16*)pOutputSamples + i*8, vzip_u16(vmovn_u32(left), vmovn_u32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; left >>= 16; right >>= 16; - pOutputSamples[i*2+0] = (drflac_int16)left; - pOutputSamples[i*2+1] = (drflac_int16)right; + pOutputSamples[i*2+0] = (ma_int16)left; + pOutputSamples[i*2+1] = (ma_int16)right; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s16__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s16__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - for (drflac_uint64 i = 0; i < frameCount; ++i) { - drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (ma_uint64 i = 0; i < frameCount; ++i) { + ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift = unusedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample; if (shift > 0) { shift -= 1; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 temp0L; - drflac_uint32 temp1L; - drflac_uint32 temp2L; - drflac_uint32 temp3L; - drflac_uint32 temp0R; - drflac_uint32 temp1R; - drflac_uint32 temp2R; - drflac_uint32 temp3R; - drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); @@ -88568,45 +88228,45 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__scalar(dr temp1R >>= 16; temp2R >>= 16; temp3R >>= 16; - pOutputSamples[i*8+0] = (drflac_int16)temp0L; - pOutputSamples[i*8+1] = (drflac_int16)temp0R; - pOutputSamples[i*8+2] = (drflac_int16)temp1L; - pOutputSamples[i*8+3] = (drflac_int16)temp1R; - pOutputSamples[i*8+4] = (drflac_int16)temp2L; - pOutputSamples[i*8+5] = (drflac_int16)temp2R; - pOutputSamples[i*8+6] = (drflac_int16)temp3L; - pOutputSamples[i*8+7] = (drflac_int16)temp3R; + pOutputSamples[i*8+0] = (ma_int16)temp0L; + pOutputSamples[i*8+1] = (ma_int16)temp0R; + pOutputSamples[i*8+2] = (ma_int16)temp1L; + pOutputSamples[i*8+3] = (ma_int16)temp1R; + pOutputSamples[i*8+4] = (ma_int16)temp2L; + pOutputSamples[i*8+5] = (ma_int16)temp2R; + pOutputSamples[i*8+6] = (ma_int16)temp3L; + pOutputSamples[i*8+7] = (ma_int16)temp3R; } } else { for (i = 0; i < frameCount4; ++i) { - drflac_uint32 temp0L; - drflac_uint32 temp1L; - drflac_uint32 temp2L; - drflac_uint32 temp3L; - drflac_uint32 temp0R; - drflac_uint32 temp1R; - drflac_uint32 temp2R; - drflac_uint32 temp3R; - drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = ((drflac_int32)(mid0 + side0) >> 1); - temp1L = ((drflac_int32)(mid1 + side1) >> 1); - temp2L = ((drflac_int32)(mid2 + side2) >> 1); - temp3L = ((drflac_int32)(mid3 + side3) >> 1); - temp0R = ((drflac_int32)(mid0 - side0) >> 1); - temp1R = ((drflac_int32)(mid1 - side1) >> 1); - temp2R = ((drflac_int32)(mid2 - side2) >> 1); - temp3R = ((drflac_int32)(mid3 - side3) >> 1); + temp0L = ((ma_int32)(mid0 + side0) >> 1); + temp1L = ((ma_int32)(mid1 + side1) >> 1); + temp2L = ((ma_int32)(mid2 + side2) >> 1); + temp3L = ((ma_int32)(mid3 + side3) >> 1); + temp0R = ((ma_int32)(mid0 - side0) >> 1); + temp1R = ((ma_int32)(mid1 - side1) >> 1); + temp2R = ((ma_int32)(mid2 - side2) >> 1); + temp3R = ((ma_int32)(mid3 - side3) >> 1); temp0L >>= 16; temp1L >>= 16; temp2L >>= 16; @@ -88615,33 +88275,33 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__scalar(dr temp1R >>= 16; temp2R >>= 16; temp3R >>= 16; - pOutputSamples[i*8+0] = (drflac_int16)temp0L; - pOutputSamples[i*8+1] = (drflac_int16)temp0R; - pOutputSamples[i*8+2] = (drflac_int16)temp1L; - pOutputSamples[i*8+3] = (drflac_int16)temp1R; - pOutputSamples[i*8+4] = (drflac_int16)temp2L; - pOutputSamples[i*8+5] = (drflac_int16)temp2R; - pOutputSamples[i*8+6] = (drflac_int16)temp3L; - pOutputSamples[i*8+7] = (drflac_int16)temp3R; + pOutputSamples[i*8+0] = (ma_int16)temp0L; + pOutputSamples[i*8+1] = (ma_int16)temp0R; + pOutputSamples[i*8+2] = (ma_int16)temp1L; + pOutputSamples[i*8+3] = (ma_int16)temp1R; + pOutputSamples[i*8+4] = (ma_int16)temp2L; + pOutputSamples[i*8+5] = (ma_int16)temp2R; + pOutputSamples[i*8+6] = (ma_int16)temp3L; + pOutputSamples[i*8+7] = (ma_int16)temp3R; } } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)(((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+0] = (ma_int16)(((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) >> 16); } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift = unusedBitsPerSample; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample; + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); if (shift == 0) { for (i = 0; i < frameCount4; ++i) { __m128i mid; @@ -88655,14 +88315,14 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drfl right = _mm_srai_epi32(_mm_sub_epi32(mid, side), 1); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16); + pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16); } } else { shift -= 1; @@ -88678,29 +88338,29 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__sse2(drfl right = _mm_slli_epi32(_mm_sub_epi32(mid, side), shift); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16); + pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16); } } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift = unusedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample; int32x4_t wbpsShift0_4; int32x4_t wbpsShift1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); wbpsShift0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); wbpsShift1_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); if (shift == 0) { @@ -88716,14 +88376,14 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drfl right = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); left = vshrq_n_s32(left, 16); right = vshrq_n_s32(right, 16); - drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)(((drflac_int32)(mid + side) >> 1) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)(((drflac_int32)(mid - side) >> 1) >> 16); + pOutputSamples[i*2+0] = (ma_int16)(((ma_int32)(mid + side) >> 1) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((ma_int32)(mid - side) >> 1) >> 16); } } else { int32x4_t shift4; @@ -88741,63 +88401,63 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side__neon(drfl right = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); left = vshrq_n_s32(left, 16); right = vshrq_n_s32(right, 16); - drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int16)(((mid + side) << shift) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)(((mid - side) << shift) >> 16); + pOutputSamples[i*2+0] = (ma_int16)(((mid + side) << shift) >> 16); + pOutputSamples[i*2+1] = (ma_int16)(((mid - side) << shift) >> 16); } } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s16__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s16__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - for (drflac_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16); + for (ma_uint64 i = 0; i < frameCount; ++i) { + pOutputSamples[i*2+0] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) >> 16); + pOutputSamples[i*2+1] = (ma_int16)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) >> 16); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; tempL0 >>= 16; tempL1 >>= 16; tempL2 >>= 16; @@ -88806,51 +88466,51 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo_ tempR1 >>= 16; tempR2 >>= 16; tempR3 >>= 16; - pOutputSamples[i*8+0] = (drflac_int16)tempL0; - pOutputSamples[i*8+1] = (drflac_int16)tempR0; - pOutputSamples[i*8+2] = (drflac_int16)tempL1; - pOutputSamples[i*8+3] = (drflac_int16)tempR1; - pOutputSamples[i*8+4] = (drflac_int16)tempL2; - pOutputSamples[i*8+5] = (drflac_int16)tempR2; - pOutputSamples[i*8+6] = (drflac_int16)tempL3; - pOutputSamples[i*8+7] = (drflac_int16)tempR3; + pOutputSamples[i*8+0] = (ma_int16)tempL0; + pOutputSamples[i*8+1] = (ma_int16)tempR0; + pOutputSamples[i*8+2] = (ma_int16)tempL1; + pOutputSamples[i*8+3] = (ma_int16)tempR1; + pOutputSamples[i*8+4] = (ma_int16)tempL2; + pOutputSamples[i*8+5] = (ma_int16)tempR2; + pOutputSamples[i*8+6] = (ma_int16)tempL3; + pOutputSamples[i*8+7] = (ma_int16)tempR3; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); + pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); __m128i right = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples1 + i), shift1); left = _mm_srai_epi32(left, 16); right = _mm_srai_epi32(right, 16); - _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), drflac__mm_packs_interleaved_epi32(left, right)); + _mm_storeu_si128((__m128i*)(pOutputSamples + i*8), ma_dr_flac__mm_packs_interleaved_epi32(left, right)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); + pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; int32x4_t shift0_4 = vdupq_n_s32(shift0); int32x4_t shift1_4 = vdupq_n_s32(shift1); for (i = 0; i < frameCount4; ++i) { @@ -88860,88 +88520,88 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo_ right = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); left = vshrq_n_s32(left, 16); right = vshrq_n_s32(right, 16); - drflac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); + ma_dr_flac__vst2q_s16(pOutputSamples + i*8, vzip_s16(vmovn_s32(left), vmovn_s32(right))); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int16)((pInputSamples0U32[i] << shift0) >> 16); - pOutputSamples[i*2+1] = (drflac_int16)((pInputSamples1U32[i] << shift1) >> 16); + pOutputSamples[i*2+0] = (ma_int16)((pInputSamples0U32[i] << shift0) >> 16); + pOutputSamples[i*2+1] = (ma_int16)((pInputSamples1U32[i] << shift1) >> 16); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_s16__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, drflac_int16* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, ma_int16* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } -DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 framesToRead, drflac_int16* pBufferOut) +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_s16(ma_dr_flac* pFlac, ma_uint64 framesToRead, ma_int16* pBufferOut) { - drflac_uint64 framesRead; - drflac_uint32 unusedBitsPerSample; + ma_uint64 framesRead; + ma_uint32 unusedBitsPerSample; if (pFlac == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { - return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); } - DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); unusedBitsPerSample = 32 - pFlac->bitsPerSample; framesRead = 0; while (framesToRead > 0) { if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!drflac__read_and_decode_next_flac_frame(pFlac)) { + if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { break; } } else { - unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - drflac_uint64 frameCountThisIteration = framesToRead; + unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; + ma_uint64 frameCountThisIteration = framesToRead; if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; } if (channelCount == 2) { - const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; + const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; + const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; switch (pFlac->currentFLACFrame.header.channelAssignment) { - case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: { - drflac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_s16__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: { - drflac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_s16__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: { - drflac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_s16__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: default: { - drflac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_s16__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; } } else { - drflac_uint64 i; + ma_uint64 i; for (i = 0; i < frameCountThisIteration; ++i) { unsigned int j; for (j = 0; j < channelCount; ++j) { - drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); - pBufferOut[(i*channelCount)+j] = (drflac_int16)(sampleS32 >> 16); + ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + pBufferOut[(i*channelCount)+j] = (ma_int16)(sampleS32 >> 16); } } } @@ -88949,74 +88609,74 @@ DRFLAC_API drflac_uint64 drflac_read_pcm_frames_s16(drflac* pFlac, drflac_uint64 pBufferOut += frameCountThisIteration * channelCount; framesToRead -= frameCountThisIteration; pFlac->currentPCMFrame += frameCountThisIteration; - pFlac->currentFLACFrame.pcmFramesRemaining -= (drflac_uint32)frameCountThisIteration; + pFlac->currentFLACFrame.pcmFramesRemaining -= (ma_uint32)frameCountThisIteration; } } return framesRead; } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; + ma_uint64 i; for (i = 0; i < frameCount; ++i) { - drflac_uint32 left = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0); - pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0); + ma_uint32 left = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 side = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0); + pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; float factor = 1 / 2147483648.0; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; - drflac_uint32 right0 = left0 - side0; - drflac_uint32 right1 = left1 - side1; - drflac_uint32 right2 = left2 - side2; - drflac_uint32 right3 = left3 - side3; - pOutputSamples[i*8+0] = (drflac_int32)left0 * factor; - pOutputSamples[i*8+1] = (drflac_int32)right0 * factor; - pOutputSamples[i*8+2] = (drflac_int32)left1 * factor; - pOutputSamples[i*8+3] = (drflac_int32)right1 * factor; - pOutputSamples[i*8+4] = (drflac_int32)left2 * factor; - pOutputSamples[i*8+5] = (drflac_int32)right2 * factor; - pOutputSamples[i*8+6] = (drflac_int32)left3 * factor; - pOutputSamples[i*8+7] = (drflac_int32)right3 * factor; + ma_uint32 left0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 left1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 left2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 left3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 right0 = left0 - side0; + ma_uint32 right1 = left1 - side1; + ma_uint32 right2 = left2 - side2; + ma_uint32 right3 = left3 - side3; + pOutputSamples[i*8+0] = (ma_int32)left0 * factor; + pOutputSamples[i*8+1] = (ma_int32)right0 * factor; + pOutputSamples[i*8+2] = (ma_int32)left1 * factor; + pOutputSamples[i*8+3] = (ma_int32)right1 * factor; + pOutputSamples[i*8+4] = (ma_int32)left2 * factor; + pOutputSamples[i*8+5] = (ma_int32)right2 * factor; + pOutputSamples[i*8+6] = (ma_int32)left3 * factor; + pOutputSamples[i*8+7] = (ma_int32)right3 * factor; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (drflac_int32)left * factor; - pOutputSamples[i*2+1] = (drflac_int32)right * factor; + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left * factor; + pOutputSamples[i*2+1] = (ma_int32)right * factor; } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; __m128 factor; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); factor = _mm_set1_ps(1.0f / 8388608.0f); for (i = 0; i < frameCount4; ++i) { __m128i left = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); @@ -89028,27 +88688,27 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__sse2(drf _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float32x4_t factor4; int32x4_t shift0_4; int32x4_t shift1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); factor4 = vdupq_n_f32(1.0f / 8388608.0f); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); @@ -89063,99 +88723,99 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side__neon(drf right = vsubq_u32(left, side); leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); - drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 left = pInputSamples0U32[i] << shift0; - drflac_uint32 side = pInputSamples1U32[i] << shift1; - drflac_uint32 right = left - side; - pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; + ma_uint32 left = pInputSamples0U32[i] << shift0; + ma_uint32 side = pInputSamples1U32[i] << shift1; + ma_uint32 right = left - side; + pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_left_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_left_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_left_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_left_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_f32__decode_left_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_f32__decode_left_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; + ma_uint64 i; for (i = 0; i < frameCount; ++i) { - drflac_uint32 side = (drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); - drflac_uint32 right = (drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (float)((drflac_int32)left / 2147483648.0); - pOutputSamples[i*2+1] = (float)((drflac_int32)right / 2147483648.0); + ma_uint32 side = (ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); + ma_uint32 right = (ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample); + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (float)((ma_int32)left / 2147483648.0); + pOutputSamples[i*2+1] = (float)((ma_int32)right / 2147483648.0); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; float factor = 1 / 2147483648.0; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; - drflac_uint32 left0 = right0 + side0; - drflac_uint32 left1 = right1 + side1; - drflac_uint32 left2 = right2 + side2; - drflac_uint32 left3 = right3 + side3; - pOutputSamples[i*8+0] = (drflac_int32)left0 * factor; - pOutputSamples[i*8+1] = (drflac_int32)right0 * factor; - pOutputSamples[i*8+2] = (drflac_int32)left1 * factor; - pOutputSamples[i*8+3] = (drflac_int32)right1 * factor; - pOutputSamples[i*8+4] = (drflac_int32)left2 * factor; - pOutputSamples[i*8+5] = (drflac_int32)right2 * factor; - pOutputSamples[i*8+6] = (drflac_int32)left3 * factor; - pOutputSamples[i*8+7] = (drflac_int32)right3 * factor; + ma_uint32 side0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 side1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 side2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 side3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 right0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 right1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 right2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 right3 = pInputSamples1U32[i*4+3] << shift1; + ma_uint32 left0 = right0 + side0; + ma_uint32 left1 = right1 + side1; + ma_uint32 left2 = right2 + side2; + ma_uint32 left3 = right3 + side3; + pOutputSamples[i*8+0] = (ma_int32)left0 * factor; + pOutputSamples[i*8+1] = (ma_int32)right0 * factor; + pOutputSamples[i*8+2] = (ma_int32)left1 * factor; + pOutputSamples[i*8+3] = (ma_int32)right1 * factor; + pOutputSamples[i*8+4] = (ma_int32)left2 * factor; + pOutputSamples[i*8+5] = (ma_int32)right2 * factor; + pOutputSamples[i*8+6] = (ma_int32)left3 * factor; + pOutputSamples[i*8+7] = (ma_int32)right3 * factor; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (drflac_int32)left * factor; - pOutputSamples[i*2+1] = (drflac_int32)right * factor; + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left * factor; + pOutputSamples[i*2+1] = (ma_int32)right * factor; } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; __m128 factor; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); factor = _mm_set1_ps(1.0f / 8388608.0f); for (i = 0; i < frameCount4; ++i) { __m128i side = _mm_slli_epi32(_mm_loadu_si128((const __m128i*)pInputSamples0 + i), shift0); @@ -89167,27 +88827,27 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__sse2(dr _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float32x4_t factor4; int32x4_t shift0_4; int32x4_t shift1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); factor4 = vdupq_n_f32(1.0f / 8388608.0f); shift0_4 = vdupq_n_s32(shift0); shift1_4 = vdupq_n_s32(shift1); @@ -89202,75 +88862,75 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side__neon(dr left = vaddq_u32(right, side); leftf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(left)), factor4); rightf = vmulq_f32(vcvtq_f32_s32(vreinterpretq_s32_u32(right)), factor4); - drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 side = pInputSamples0U32[i] << shift0; - drflac_uint32 right = pInputSamples1U32[i] << shift1; - drflac_uint32 left = right + side; - pOutputSamples[i*2+0] = (drflac_int32)left / 8388608.0f; - pOutputSamples[i*2+1] = (drflac_int32)right / 8388608.0f; + ma_uint32 side = pInputSamples0U32[i] << shift0; + ma_uint32 right = pInputSamples1U32[i] << shift1; + ma_uint32 left = right + side; + pOutputSamples[i*2+0] = (ma_int32)left / 8388608.0f; + pOutputSamples[i*2+1] = (ma_int32)right / 8388608.0f; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_right_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_right_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_right_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_right_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_f32__decode_right_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_f32__decode_right_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - for (drflac_uint64 i = 0; i < frameCount; ++i) { - drflac_uint32 mid = (drflac_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = (drflac_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + for (ma_uint64 i = 0; i < frameCount; ++i) { + ma_uint32 mid = (ma_uint32)pInputSamples0[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = (ma_uint32)pInputSamples1[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (float)((((drflac_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); - pOutputSamples[i*2+1] = (float)((((drflac_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); + pOutputSamples[i*2+0] = (float)((((ma_int32)(mid + side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); + pOutputSamples[i*2+1] = (float)((((ma_int32)(mid - side) >> 1) << (unusedBitsPerSample)) / 2147483648.0); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift = unusedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample; float factor = 1 / 2147483648.0; if (shift > 0) { shift -= 1; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 temp0L; - drflac_uint32 temp1L; - drflac_uint32 temp2L; - drflac_uint32 temp3L; - drflac_uint32 temp0R; - drflac_uint32 temp1R; - drflac_uint32 temp2R; - drflac_uint32 temp3R; - drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); @@ -89283,74 +88943,74 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__scalar(dr temp1R = (mid1 - side1) << shift; temp2R = (mid2 - side2) << shift; temp3R = (mid3 - side3) << shift; - pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor; - pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor; - pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor; - pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor; - pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor; - pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor; - pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor; - pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor; + pOutputSamples[i*8+0] = (ma_int32)temp0L * factor; + pOutputSamples[i*8+1] = (ma_int32)temp0R * factor; + pOutputSamples[i*8+2] = (ma_int32)temp1L * factor; + pOutputSamples[i*8+3] = (ma_int32)temp1R * factor; + pOutputSamples[i*8+4] = (ma_int32)temp2L * factor; + pOutputSamples[i*8+5] = (ma_int32)temp2R * factor; + pOutputSamples[i*8+6] = (ma_int32)temp3L * factor; + pOutputSamples[i*8+7] = (ma_int32)temp3R * factor; } } else { for (i = 0; i < frameCount4; ++i) { - drflac_uint32 temp0L; - drflac_uint32 temp1L; - drflac_uint32 temp2L; - drflac_uint32 temp3L; - drflac_uint32 temp0R; - drflac_uint32 temp1R; - drflac_uint32 temp2R; - drflac_uint32 temp3R; - drflac_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; - drflac_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 temp0L; + ma_uint32 temp1L; + ma_uint32 temp2L; + ma_uint32 temp3L; + ma_uint32 temp0R; + ma_uint32 temp1R; + ma_uint32 temp2R; + ma_uint32 temp3R; + ma_uint32 mid0 = pInputSamples0U32[i*4+0] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid1 = pInputSamples0U32[i*4+1] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid2 = pInputSamples0U32[i*4+2] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 mid3 = pInputSamples0U32[i*4+3] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side0 = pInputSamples1U32[i*4+0] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side1 = pInputSamples1U32[i*4+1] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side2 = pInputSamples1U32[i*4+2] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 side3 = pInputSamples1U32[i*4+3] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid0 = (mid0 << 1) | (side0 & 0x01); mid1 = (mid1 << 1) | (side1 & 0x01); mid2 = (mid2 << 1) | (side2 & 0x01); mid3 = (mid3 << 1) | (side3 & 0x01); - temp0L = (drflac_uint32)((drflac_int32)(mid0 + side0) >> 1); - temp1L = (drflac_uint32)((drflac_int32)(mid1 + side1) >> 1); - temp2L = (drflac_uint32)((drflac_int32)(mid2 + side2) >> 1); - temp3L = (drflac_uint32)((drflac_int32)(mid3 + side3) >> 1); - temp0R = (drflac_uint32)((drflac_int32)(mid0 - side0) >> 1); - temp1R = (drflac_uint32)((drflac_int32)(mid1 - side1) >> 1); - temp2R = (drflac_uint32)((drflac_int32)(mid2 - side2) >> 1); - temp3R = (drflac_uint32)((drflac_int32)(mid3 - side3) >> 1); - pOutputSamples[i*8+0] = (drflac_int32)temp0L * factor; - pOutputSamples[i*8+1] = (drflac_int32)temp0R * factor; - pOutputSamples[i*8+2] = (drflac_int32)temp1L * factor; - pOutputSamples[i*8+3] = (drflac_int32)temp1R * factor; - pOutputSamples[i*8+4] = (drflac_int32)temp2L * factor; - pOutputSamples[i*8+5] = (drflac_int32)temp2R * factor; - pOutputSamples[i*8+6] = (drflac_int32)temp3L * factor; - pOutputSamples[i*8+7] = (drflac_int32)temp3R * factor; + temp0L = (ma_uint32)((ma_int32)(mid0 + side0) >> 1); + temp1L = (ma_uint32)((ma_int32)(mid1 + side1) >> 1); + temp2L = (ma_uint32)((ma_int32)(mid2 + side2) >> 1); + temp3L = (ma_uint32)((ma_int32)(mid3 + side3) >> 1); + temp0R = (ma_uint32)((ma_int32)(mid0 - side0) >> 1); + temp1R = (ma_uint32)((ma_int32)(mid1 - side1) >> 1); + temp2R = (ma_uint32)((ma_int32)(mid2 - side2) >> 1); + temp3R = (ma_uint32)((ma_int32)(mid3 - side3) >> 1); + pOutputSamples[i*8+0] = (ma_int32)temp0L * factor; + pOutputSamples[i*8+1] = (ma_int32)temp0R * factor; + pOutputSamples[i*8+2] = (ma_int32)temp1L * factor; + pOutputSamples[i*8+3] = (ma_int32)temp1R * factor; + pOutputSamples[i*8+4] = (ma_int32)temp2L * factor; + pOutputSamples[i*8+5] = (ma_int32)temp2R * factor; + pOutputSamples[i*8+6] = (ma_int32)temp3L * factor; + pOutputSamples[i*8+7] = (ma_int32)temp3R * factor; } } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor; - pOutputSamples[i*2+1] = (drflac_int32)((drflac_uint32)((drflac_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor; + pOutputSamples[i*2+0] = (ma_int32)((ma_uint32)((ma_int32)(mid + side) >> 1) << unusedBitsPerSample) * factor; + pOutputSamples[i*2+1] = (ma_int32)((ma_uint32)((ma_int32)(mid - side) >> 1) << unusedBitsPerSample) * factor; } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift = unusedBitsPerSample - 8; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample - 8; float factor; __m128 factor128; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); factor = 1.0f / 8388608.0f; factor128 = _mm_set1_ps(factor); if (shift == 0) { @@ -89372,11 +89032,11 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drfl _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor; - pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor; + pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor; + pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor; } } else { shift -= 1; @@ -89398,29 +89058,29 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__sse2(drfl _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor; - pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor; + pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor; + pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor; } } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift = unusedBitsPerSample - 8; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift = unusedBitsPerSample - 8; float factor; float32x4_t factor4; int32x4_t shift4; int32x4_t wbps0_4; int32x4_t wbps1_4; - DRFLAC_ASSERT(pFlac->bitsPerSample <= 24); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 24); factor = 1.0f / 8388608.0f; factor4 = vdupq_n_f32(factor); wbps0_4 = vdupq_n_s32(pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample); @@ -89438,14 +89098,14 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drfl righti = vshrq_n_s32(vreinterpretq_s32_u32(vsubq_u32(mid, side)), 1); leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = ((drflac_int32)(mid + side) >> 1) * factor; - pOutputSamples[i*2+1] = ((drflac_int32)(mid - side) >> 1) * factor; + pOutputSamples[i*2+0] = ((ma_int32)(mid + side) >> 1) * factor; + pOutputSamples[i*2+1] = ((ma_int32)(mid - side) >> 1) * factor; } } else { shift -= 1; @@ -89464,87 +89124,87 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side__neon(drfl righti = vreinterpretq_s32_u32(vshlq_u32(vsubq_u32(mid, side), shift4)); leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - drflac_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint32 mid = pInputSamples0U32[i] << pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 side = pInputSamples1U32[i] << pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; mid = (mid << 1) | (side & 0x01); - pOutputSamples[i*2+0] = (drflac_int32)((mid + side) << shift) * factor; - pOutputSamples[i*2+1] = (drflac_int32)((mid - side) << shift) * factor; + pOutputSamples[i*2+0] = (ma_int32)((mid + side) << shift) * factor; + pOutputSamples[i*2+1] = (ma_int32)((mid - side) << shift) * factor; } } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_mid_side(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_mid_side(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_mid_side__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_mid_side__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_f32__decode_mid_side__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_f32__decode_mid_side__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } #if 0 -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__reference(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - for (drflac_uint64 i = 0; i < frameCount; ++i) { - pOutputSamples[i*2+0] = (float)((drflac_int32)((drflac_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0); - pOutputSamples[i*2+1] = (float)((drflac_int32)((drflac_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0); + for (ma_uint64 i = 0; i < frameCount; ++i) { + pOutputSamples[i*2+0] = (float)((ma_int32)((ma_uint32)pInputSamples0[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample)) / 2147483648.0); + pOutputSamples[i*2+1] = (float)((ma_int32)((ma_uint32)pInputSamples1[i] << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample)) / 2147483648.0); } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; - drflac_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample; + ma_uint32 shift1 = unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample; float factor = 1 / 2147483648.0; for (i = 0; i < frameCount4; ++i) { - drflac_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; - drflac_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; - drflac_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; - drflac_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; - drflac_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; - drflac_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; - drflac_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; - drflac_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; - pOutputSamples[i*8+0] = (drflac_int32)tempL0 * factor; - pOutputSamples[i*8+1] = (drflac_int32)tempR0 * factor; - pOutputSamples[i*8+2] = (drflac_int32)tempL1 * factor; - pOutputSamples[i*8+3] = (drflac_int32)tempR1 * factor; - pOutputSamples[i*8+4] = (drflac_int32)tempL2 * factor; - pOutputSamples[i*8+5] = (drflac_int32)tempR2 * factor; - pOutputSamples[i*8+6] = (drflac_int32)tempL3 * factor; - pOutputSamples[i*8+7] = (drflac_int32)tempR3 * factor; + ma_uint32 tempL0 = pInputSamples0U32[i*4+0] << shift0; + ma_uint32 tempL1 = pInputSamples0U32[i*4+1] << shift0; + ma_uint32 tempL2 = pInputSamples0U32[i*4+2] << shift0; + ma_uint32 tempL3 = pInputSamples0U32[i*4+3] << shift0; + ma_uint32 tempR0 = pInputSamples1U32[i*4+0] << shift1; + ma_uint32 tempR1 = pInputSamples1U32[i*4+1] << shift1; + ma_uint32 tempR2 = pInputSamples1U32[i*4+2] << shift1; + ma_uint32 tempR3 = pInputSamples1U32[i*4+3] << shift1; + pOutputSamples[i*8+0] = (ma_int32)tempL0 * factor; + pOutputSamples[i*8+1] = (ma_int32)tempR0 * factor; + pOutputSamples[i*8+2] = (ma_int32)tempL1 * factor; + pOutputSamples[i*8+3] = (ma_int32)tempR1 * factor; + pOutputSamples[i*8+4] = (ma_int32)tempL2 * factor; + pOutputSamples[i*8+5] = (ma_int32)tempR2 * factor; + pOutputSamples[i*8+6] = (ma_int32)tempL3 * factor; + pOutputSamples[i*8+7] = (ma_int32)tempR3 * factor; } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; } } -#if defined(DRFLAC_SUPPORT_SSE2) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_SSE2) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float factor = 1.0f / 8388608.0f; __m128 factor128 = _mm_set1_ps(factor); for (i = 0; i < frameCount4; ++i) { @@ -89560,20 +89220,20 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo_ _mm_storeu_ps(pOutputSamples + i*8 + 4, _mm_unpackhi_ps(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; } } #endif -#if defined(DRFLAC_SUPPORT_NEON) -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo__neon(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +#if defined(MA_DR_FLAC_SUPPORT_NEON) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { - drflac_uint64 i; - drflac_uint64 frameCount4 = frameCount >> 2; - const drflac_uint32* pInputSamples0U32 = (const drflac_uint32*)pInputSamples0; - const drflac_uint32* pInputSamples1U32 = (const drflac_uint32*)pInputSamples1; - drflac_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; - drflac_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; + ma_uint64 i; + ma_uint64 frameCount4 = frameCount >> 2; + const ma_uint32* pInputSamples0U32 = (const ma_uint32*)pInputSamples0; + const ma_uint32* pInputSamples1U32 = (const ma_uint32*)pInputSamples1; + ma_uint32 shift0 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[0].wastedBitsPerSample) - 8; + ma_uint32 shift1 = (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[1].wastedBitsPerSample) - 8; float factor = 1.0f / 8388608.0f; float32x4_t factor4 = vdupq_n_f32(factor); int32x4_t shift0_4 = vdupq_n_s32(shift0); @@ -89587,87 +89247,87 @@ static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo_ righti = vreinterpretq_s32_u32(vshlq_u32(vld1q_u32(pInputSamples1U32 + i*4), shift1_4)); leftf = vmulq_f32(vcvtq_f32_s32(lefti), factor4); rightf = vmulq_f32(vcvtq_f32_s32(righti), factor4); - drflac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); + ma_dr_flac__vst2q_f32(pOutputSamples + i*8, vzipq_f32(leftf, rightf)); } for (i = (frameCount4 << 2); i < frameCount; ++i) { - pOutputSamples[i*2+0] = (drflac_int32)(pInputSamples0U32[i] << shift0) * factor; - pOutputSamples[i*2+1] = (drflac_int32)(pInputSamples1U32[i] << shift1) * factor; + pOutputSamples[i*2+0] = (ma_int32)(pInputSamples0U32[i] << shift0) * factor; + pOutputSamples[i*2+1] = (ma_int32)(pInputSamples1U32[i] << shift1) * factor; } } #endif -static DRFLAC_INLINE void drflac_read_pcm_frames_f32__decode_independent_stereo(drflac* pFlac, drflac_uint64 frameCount, drflac_uint32 unusedBitsPerSample, const drflac_int32* pInputSamples0, const drflac_int32* pInputSamples1, float* pOutputSamples) +static MA_INLINE void ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(ma_dr_flac* pFlac, ma_uint64 frameCount, ma_uint32 unusedBitsPerSample, const ma_int32* pInputSamples0, const ma_int32* pInputSamples1, float* pOutputSamples) { -#if defined(DRFLAC_SUPPORT_SSE2) - if (drflac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#if defined(MA_DR_FLAC_SUPPORT_SSE2) + if (ma_dr_flac__gIsSSE2Supported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__sse2(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else -#elif defined(DRFLAC_SUPPORT_NEON) - if (drflac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { - drflac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); +#elif defined(MA_DR_FLAC_SUPPORT_NEON) + if (ma_dr_flac__gIsNEONSupported && pFlac->bitsPerSample <= 24) { + ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__neon(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); } else #endif { #if 0 - drflac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__reference(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #else - drflac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); + ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo__scalar(pFlac, frameCount, unusedBitsPerSample, pInputSamples0, pInputSamples1, pOutputSamples); #endif } } -DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 framesToRead, float* pBufferOut) +MA_API ma_uint64 ma_dr_flac_read_pcm_frames_f32(ma_dr_flac* pFlac, ma_uint64 framesToRead, float* pBufferOut) { - drflac_uint64 framesRead; - drflac_uint32 unusedBitsPerSample; + ma_uint64 framesRead; + ma_uint32 unusedBitsPerSample; if (pFlac == NULL || framesToRead == 0) { return 0; } if (pBufferOut == NULL) { - return drflac__seek_forward_by_pcm_frames(pFlac, framesToRead); + return ma_dr_flac__seek_forward_by_pcm_frames(pFlac, framesToRead); } - DRFLAC_ASSERT(pFlac->bitsPerSample <= 32); + MA_DR_FLAC_ASSERT(pFlac->bitsPerSample <= 32); unusedBitsPerSample = 32 - pFlac->bitsPerSample; framesRead = 0; while (framesToRead > 0) { if (pFlac->currentFLACFrame.pcmFramesRemaining == 0) { - if (!drflac__read_and_decode_next_flac_frame(pFlac)) { + if (!ma_dr_flac__read_and_decode_next_flac_frame(pFlac)) { break; } } else { - unsigned int channelCount = drflac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); - drflac_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; - drflac_uint64 frameCountThisIteration = framesToRead; + unsigned int channelCount = ma_dr_flac__get_channel_count_from_channel_assignment(pFlac->currentFLACFrame.header.channelAssignment); + ma_uint64 iFirstPCMFrame = pFlac->currentFLACFrame.header.blockSizeInPCMFrames - pFlac->currentFLACFrame.pcmFramesRemaining; + ma_uint64 frameCountThisIteration = framesToRead; if (frameCountThisIteration > pFlac->currentFLACFrame.pcmFramesRemaining) { frameCountThisIteration = pFlac->currentFLACFrame.pcmFramesRemaining; } if (channelCount == 2) { - const drflac_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; - const drflac_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; + const ma_int32* pDecodedSamples0 = pFlac->currentFLACFrame.subframes[0].pSamplesS32 + iFirstPCMFrame; + const ma_int32* pDecodedSamples1 = pFlac->currentFLACFrame.subframes[1].pSamplesS32 + iFirstPCMFrame; switch (pFlac->currentFLACFrame.header.channelAssignment) { - case DRFLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_LEFT_SIDE: { - drflac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_f32__decode_left_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case DRFLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_RIGHT_SIDE: { - drflac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_f32__decode_right_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case DRFLAC_CHANNEL_ASSIGNMENT_MID_SIDE: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_MID_SIDE: { - drflac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_f32__decode_mid_side(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; - case DRFLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: + case MA_DR_FLAC_CHANNEL_ASSIGNMENT_INDEPENDENT: default: { - drflac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); + ma_dr_flac_read_pcm_frames_f32__decode_independent_stereo(pFlac, frameCountThisIteration, unusedBitsPerSample, pDecodedSamples0, pDecodedSamples1, pBufferOut); } break; } } else { - drflac_uint64 i; + ma_uint64 i; for (i = 0; i < frameCountThisIteration; ++i) { unsigned int j; for (j = 0; j < channelCount; ++j) { - drflac_int32 sampleS32 = (drflac_int32)((drflac_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); + ma_int32 sampleS32 = (ma_int32)((ma_uint32)(pFlac->currentFLACFrame.subframes[j].pSamplesS32[iFirstPCMFrame + i]) << (unusedBitsPerSample + pFlac->currentFLACFrame.subframes[j].wastedBitsPerSample)); pBufferOut[(i*channelCount)+j] = (float)(sampleS32 / 2147483648.0); } } @@ -89681,111 +89341,102 @@ DRFLAC_API drflac_uint64 drflac_read_pcm_frames_f32(drflac* pFlac, drflac_uint64 } return framesRead; } -DRFLAC_API drflac_bool32 drflac_seek_to_pcm_frame(drflac* pFlac, drflac_uint64 pcmFrameIndex) +MA_API ma_bool32 ma_dr_flac_seek_to_pcm_frame(ma_dr_flac* pFlac, ma_uint64 pcmFrameIndex) { if (pFlac == NULL) { - return DRFLAC_FALSE; + return MA_FALSE; } if (pFlac->currentPCMFrame == pcmFrameIndex) { - return DRFLAC_TRUE; + return MA_TRUE; } if (pFlac->firstFLACFramePosInBytes == 0) { - return DRFLAC_FALSE; + return MA_FALSE; } if (pcmFrameIndex == 0) { pFlac->currentPCMFrame = 0; - return drflac__seek_to_first_frame(pFlac); + return ma_dr_flac__seek_to_first_frame(pFlac); } else { - drflac_bool32 wasSuccessful = DRFLAC_FALSE; - drflac_uint64 originalPCMFrame = pFlac->currentPCMFrame; + ma_bool32 wasSuccessful = MA_FALSE; + ma_uint64 originalPCMFrame = pFlac->currentPCMFrame; if (pcmFrameIndex > pFlac->totalPCMFrameCount) { pcmFrameIndex = pFlac->totalPCMFrameCount; } if (pcmFrameIndex > pFlac->currentPCMFrame) { - drflac_uint32 offset = (drflac_uint32)(pcmFrameIndex - pFlac->currentPCMFrame); + ma_uint32 offset = (ma_uint32)(pcmFrameIndex - pFlac->currentPCMFrame); if (pFlac->currentFLACFrame.pcmFramesRemaining > offset) { pFlac->currentFLACFrame.pcmFramesRemaining -= offset; pFlac->currentPCMFrame = pcmFrameIndex; - return DRFLAC_TRUE; + return MA_TRUE; } } else { - drflac_uint32 offsetAbs = (drflac_uint32)(pFlac->currentPCMFrame - pcmFrameIndex); - drflac_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; - drflac_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining; + ma_uint32 offsetAbs = (ma_uint32)(pFlac->currentPCMFrame - pcmFrameIndex); + ma_uint32 currentFLACFramePCMFrameCount = pFlac->currentFLACFrame.header.blockSizeInPCMFrames; + ma_uint32 currentFLACFramePCMFramesConsumed = currentFLACFramePCMFrameCount - pFlac->currentFLACFrame.pcmFramesRemaining; if (currentFLACFramePCMFramesConsumed > offsetAbs) { pFlac->currentFLACFrame.pcmFramesRemaining += offsetAbs; pFlac->currentPCMFrame = pcmFrameIndex; - return DRFLAC_TRUE; + return MA_TRUE; } } -#ifndef DR_FLAC_NO_OGG - if (pFlac->container == drflac_container_ogg) +#ifndef MA_DR_FLAC_NO_OGG + if (pFlac->container == ma_dr_flac_container_ogg) { - wasSuccessful = drflac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex); + wasSuccessful = ma_dr_flac_ogg__seek_to_pcm_frame(pFlac, pcmFrameIndex); } else #endif { if (!pFlac->_noSeekTableSeek) { - wasSuccessful = drflac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex); + wasSuccessful = ma_dr_flac__seek_to_pcm_frame__seek_table(pFlac, pcmFrameIndex); } -#if !defined(DR_FLAC_NO_CRC) +#if !defined(MA_DR_FLAC_NO_CRC) if (!wasSuccessful && !pFlac->_noBinarySearchSeek && pFlac->totalPCMFrameCount > 0) { - wasSuccessful = drflac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex); + wasSuccessful = ma_dr_flac__seek_to_pcm_frame__binary_search(pFlac, pcmFrameIndex); } #endif if (!wasSuccessful && !pFlac->_noBruteForceSeek) { - wasSuccessful = drflac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex); + wasSuccessful = ma_dr_flac__seek_to_pcm_frame__brute_force(pFlac, pcmFrameIndex); } } if (wasSuccessful) { pFlac->currentPCMFrame = pcmFrameIndex; } else { - if (drflac_seek_to_pcm_frame(pFlac, originalPCMFrame) == DRFLAC_FALSE) { - drflac_seek_to_pcm_frame(pFlac, 0); + if (ma_dr_flac_seek_to_pcm_frame(pFlac, originalPCMFrame) == MA_FALSE) { + ma_dr_flac_seek_to_pcm_frame(pFlac, 0); } } return wasSuccessful; } } -#if defined(SIZE_MAX) - #define DRFLAC_SIZE_MAX SIZE_MAX -#else - #if defined(DRFLAC_64BIT) - #define DRFLAC_SIZE_MAX ((drflac_uint64)0xFFFFFFFFFFFFFFFF) - #else - #define DRFLAC_SIZE_MAX 0xFFFFFFFF - #endif -#endif -#define DRFLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \ -static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut)\ +#define MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(extension, type) \ +static type* ma_dr_flac__full_read_and_close_ ## extension (ma_dr_flac* pFlac, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut)\ { \ type* pSampleData = NULL; \ - drflac_uint64 totalPCMFrameCount; \ + ma_uint64 totalPCMFrameCount; \ \ - DRFLAC_ASSERT(pFlac != NULL); \ + MA_DR_FLAC_ASSERT(pFlac != NULL); \ \ totalPCMFrameCount = pFlac->totalPCMFrameCount; \ \ if (totalPCMFrameCount == 0) { \ type buffer[4096]; \ - drflac_uint64 pcmFramesRead; \ + ma_uint64 pcmFramesRead; \ size_t sampleDataBufferSize = sizeof(buffer); \ \ - pSampleData = (type*)drflac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \ + pSampleData = (type*)ma_dr_flac__malloc_from_callbacks(sampleDataBufferSize, &pFlac->allocationCallbacks); \ if (pSampleData == NULL) { \ goto on_error; \ } \ \ - while ((pcmFramesRead = (drflac_uint64)drflac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \ + while ((pcmFramesRead = (ma_uint64)ma_dr_flac_read_pcm_frames_##extension(pFlac, sizeof(buffer)/sizeof(buffer[0])/pFlac->channels, buffer)) > 0) { \ if (((totalPCMFrameCount + pcmFramesRead) * pFlac->channels * sizeof(type)) > sampleDataBufferSize) { \ type* pNewSampleData; \ size_t newSampleDataBufferSize; \ \ newSampleDataBufferSize = sampleDataBufferSize * 2; \ - pNewSampleData = (type*)drflac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \ + pNewSampleData = (type*)ma_dr_flac__realloc_from_callbacks(pSampleData, newSampleDataBufferSize, sampleDataBufferSize, &pFlac->allocationCallbacks); \ if (pNewSampleData == NULL) { \ - drflac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \ + ma_dr_flac__free_from_callbacks(pSampleData, &pFlac->allocationCallbacks); \ goto on_error; \ } \ \ @@ -89793,43 +89444,43 @@ static type* drflac__full_read_and_close_ ## extension (drflac* pFlac, unsigned pSampleData = pNewSampleData; \ } \ \ - DRFLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ + MA_DR_FLAC_COPY_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), buffer, (size_t)(pcmFramesRead*pFlac->channels*sizeof(type))); \ totalPCMFrameCount += pcmFramesRead; \ } \ \ \ - DRFLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \ + MA_DR_FLAC_ZERO_MEMORY(pSampleData + (totalPCMFrameCount*pFlac->channels), (size_t)(sampleDataBufferSize - totalPCMFrameCount*pFlac->channels*sizeof(type))); \ } else { \ - drflac_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \ - if (dataSize > (drflac_uint64)DRFLAC_SIZE_MAX) { \ + ma_uint64 dataSize = totalPCMFrameCount*pFlac->channels*sizeof(type); \ + if (dataSize > (ma_uint64)MA_SIZE_MAX) { \ goto on_error; \ } \ \ - pSampleData = (type*)drflac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); \ + pSampleData = (type*)ma_dr_flac__malloc_from_callbacks((size_t)dataSize, &pFlac->allocationCallbacks); \ if (pSampleData == NULL) { \ goto on_error; \ } \ \ - totalPCMFrameCount = drflac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \ + totalPCMFrameCount = ma_dr_flac_read_pcm_frames_##extension(pFlac, pFlac->totalPCMFrameCount, pSampleData); \ } \ \ if (sampleRateOut) *sampleRateOut = pFlac->sampleRate; \ if (channelsOut) *channelsOut = pFlac->channels; \ if (totalPCMFrameCountOut) *totalPCMFrameCountOut = totalPCMFrameCount; \ \ - drflac_close(pFlac); \ + ma_dr_flac_close(pFlac); \ return pSampleData; \ \ on_error: \ - drflac_close(pFlac); \ + ma_dr_flac_close(pFlac); \ return NULL; \ } -DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s32, drflac_int32) -DRFLAC_DEFINE_FULL_READ_AND_CLOSE(s16, drflac_int16) -DRFLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) -DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s32, ma_int32) +MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(s16, ma_int16) +MA_DR_FLAC_DEFINE_FULL_READ_AND_CLOSE(f32, float) +MA_API ma_int32* ma_dr_flac_open_and_read_pcm_frames_s32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; if (channelsOut) { *channelsOut = 0; } @@ -89839,15 +89490,15 @@ DRFLAC_API drflac_int32* drflac_open_and_read_pcm_frames_s32(drflac_read_proc on if (totalPCMFrameCountOut) { *totalPCMFrameCountOut = 0; } - pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return drflac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); + return ma_dr_flac__full_read_and_close_s32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); } -DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int16* ma_dr_flac_open_and_read_pcm_frames_s16(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; if (channelsOut) { *channelsOut = 0; } @@ -89857,15 +89508,15 @@ DRFLAC_API drflac_int16* drflac_open_and_read_pcm_frames_s16(drflac_read_proc on if (totalPCMFrameCountOut) { *totalPCMFrameCountOut = 0; } - pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return drflac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); + return ma_dr_flac__full_read_and_close_s16(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); } -DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, drflac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, drflac_uint64* totalPCMFrameCountOut, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API float* ma_dr_flac_open_and_read_pcm_frames_f32(ma_dr_flac_read_proc onRead, ma_dr_flac_seek_proc onSeek, void* pUserData, unsigned int* channelsOut, unsigned int* sampleRateOut, ma_uint64* totalPCMFrameCountOut, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; if (channelsOut) { *channelsOut = 0; } @@ -89875,16 +89526,16 @@ DRFLAC_API float* drflac_open_and_read_pcm_frames_f32(drflac_read_proc onRead, d if (totalPCMFrameCountOut) { *totalPCMFrameCountOut = 0; } - pFlac = drflac_open(onRead, onSeek, pUserData, pAllocationCallbacks); + pFlac = ma_dr_flac_open(onRead, onSeek, pUserData, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return drflac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); + return ma_dr_flac__full_read_and_close_f32(pFlac, channelsOut, sampleRateOut, totalPCMFrameCountOut); } -#ifndef DR_FLAC_NO_STDIO -DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +#ifndef MA_DR_FLAC_NO_STDIO +MA_API ma_int32* ma_dr_flac_open_file_and_read_pcm_frames_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; if (sampleRate) { *sampleRate = 0; } @@ -89894,15 +89545,15 @@ DRFLAC_API drflac_int32* drflac_open_file_and_read_pcm_frames_s32(const char* fi if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } - pFlac = drflac_open_file(filename, pAllocationCallbacks); + pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); + return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); } -DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int16* ma_dr_flac_open_file_and_read_pcm_frames_s16(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; if (sampleRate) { *sampleRate = 0; } @@ -89912,15 +89563,15 @@ DRFLAC_API drflac_int16* drflac_open_file_and_read_pcm_frames_s16(const char* fi if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } - pFlac = drflac_open_file(filename, pAllocationCallbacks); + pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); + return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); } -DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API float* ma_dr_flac_open_file_and_read_pcm_frames_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; if (sampleRate) { *sampleRate = 0; } @@ -89930,16 +89581,16 @@ DRFLAC_API float* drflac_open_file_and_read_pcm_frames_f32(const char* filename, if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } - pFlac = drflac_open_file(filename, pAllocationCallbacks); + pFlac = ma_dr_flac_open_file(filename, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); + return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); } #endif -DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int32* ma_dr_flac_open_memory_and_read_pcm_frames_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; if (sampleRate) { *sampleRate = 0; } @@ -89949,15 +89600,15 @@ DRFLAC_API drflac_int32* drflac_open_memory_and_read_pcm_frames_s32(const void* if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } - pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); + pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return drflac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); + return ma_dr_flac__full_read_and_close_s32(pFlac, channels, sampleRate, totalPCMFrameCount); } -DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int16* ma_dr_flac_open_memory_and_read_pcm_frames_s16(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; if (sampleRate) { *sampleRate = 0; } @@ -89967,15 +89618,15 @@ DRFLAC_API drflac_int16* drflac_open_memory_and_read_pcm_frames_s16(const void* if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } - pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); + pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return drflac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); + return ma_dr_flac__full_read_and_close_s16(pFlac, channels, sampleRate, totalPCMFrameCount); } -DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, drflac_uint64* totalPCMFrameCount, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API float* ma_dr_flac_open_memory_and_read_pcm_frames_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, ma_uint64* totalPCMFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drflac* pFlac; + ma_dr_flac* pFlac; if (sampleRate) { *sampleRate = 0; } @@ -89985,21 +89636,21 @@ DRFLAC_API float* drflac_open_memory_and_read_pcm_frames_f32(const void* data, s if (totalPCMFrameCount) { *totalPCMFrameCount = 0; } - pFlac = drflac_open_memory(data, dataSize, pAllocationCallbacks); + pFlac = ma_dr_flac_open_memory(data, dataSize, pAllocationCallbacks); if (pFlac == NULL) { return NULL; } - return drflac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); + return ma_dr_flac__full_read_and_close_f32(pFlac, channels, sampleRate, totalPCMFrameCount); } -DRFLAC_API void drflac_free(void* p, const drflac_allocation_callbacks* pAllocationCallbacks) +MA_API void ma_dr_flac_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { - drflac__free_from_callbacks(p, pAllocationCallbacks); + ma_dr_flac__free_from_callbacks(p, pAllocationCallbacks); } else { - drflac__free_default(p, NULL); + ma_dr_flac__free_default(p, NULL); } } -DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterator* pIter, drflac_uint32 commentCount, const void* pComments) +MA_API void ma_dr_flac_init_vorbis_comment_iterator(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32 commentCount, const void* pComments) { if (pIter == NULL) { return; @@ -90007,9 +89658,9 @@ DRFLAC_API void drflac_init_vorbis_comment_iterator(drflac_vorbis_comment_iterat pIter->countRemaining = commentCount; pIter->pRunningData = (const char*)pComments; } -DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator* pIter, drflac_uint32* pCommentLengthOut) +MA_API const char* ma_dr_flac_next_vorbis_comment(ma_dr_flac_vorbis_comment_iterator* pIter, ma_uint32* pCommentLengthOut) { - drflac_int32 length; + ma_int32 length; const char* pComment; if (pCommentLengthOut) { *pCommentLengthOut = 0; @@ -90017,7 +89668,7 @@ DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { return NULL; } - length = drflac__le2host_32_ptr_unaligned(pIter->pRunningData); + length = ma_dr_flac__le2host_32_ptr_unaligned(pIter->pRunningData); pIter->pRunningData += 4; pComment = pIter->pRunningData; pIter->pRunningData += length; @@ -90027,7 +89678,7 @@ DRFLAC_API const char* drflac_next_vorbis_comment(drflac_vorbis_comment_iterator } return pComment; } -DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterator* pIter, drflac_uint32 trackCount, const void* pTrackData) +MA_API void ma_dr_flac_init_cuesheet_track_iterator(ma_dr_flac_cuesheet_track_iterator* pIter, ma_uint32 trackCount, const void* pTrackData) { if (pIter == NULL) { return; @@ -90035,127 +89686,127 @@ DRFLAC_API void drflac_init_cuesheet_track_iterator(drflac_cuesheet_track_iterat pIter->countRemaining = trackCount; pIter->pRunningData = (const char*)pTrackData; } -DRFLAC_API drflac_bool32 drflac_next_cuesheet_track(drflac_cuesheet_track_iterator* pIter, drflac_cuesheet_track* pCuesheetTrack) +MA_API ma_bool32 ma_dr_flac_next_cuesheet_track(ma_dr_flac_cuesheet_track_iterator* pIter, ma_dr_flac_cuesheet_track* pCuesheetTrack) { - drflac_cuesheet_track cuesheetTrack; + ma_dr_flac_cuesheet_track cuesheetTrack; const char* pRunningData; - drflac_uint64 offsetHi; - drflac_uint64 offsetLo; + ma_uint64 offsetHi; + ma_uint64 offsetLo; if (pIter == NULL || pIter->countRemaining == 0 || pIter->pRunningData == NULL) { - return DRFLAC_FALSE; + return MA_FALSE; } pRunningData = pIter->pRunningData; - offsetHi = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; - offsetLo = drflac__be2host_32(*(const drflac_uint32*)pRunningData); pRunningData += 4; + offsetHi = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4; + offsetLo = ma_dr_flac__be2host_32(*(const ma_uint32*)pRunningData); pRunningData += 4; cuesheetTrack.offset = offsetLo | (offsetHi << 32); cuesheetTrack.trackNumber = pRunningData[0]; pRunningData += 1; - DRFLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12; + MA_DR_FLAC_COPY_MEMORY(cuesheetTrack.ISRC, pRunningData, sizeof(cuesheetTrack.ISRC)); pRunningData += 12; cuesheetTrack.isAudio = (pRunningData[0] & 0x80) != 0; cuesheetTrack.preEmphasis = (pRunningData[0] & 0x40) != 0; pRunningData += 14; cuesheetTrack.indexCount = pRunningData[0]; pRunningData += 1; - cuesheetTrack.pIndexPoints = (const drflac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(drflac_cuesheet_track_index); + cuesheetTrack.pIndexPoints = (const ma_dr_flac_cuesheet_track_index*)pRunningData; pRunningData += cuesheetTrack.indexCount * sizeof(ma_dr_flac_cuesheet_track_index); pIter->pRunningData = pRunningData; pIter->countRemaining -= 1; if (pCuesheetTrack) { *pCuesheetTrack = cuesheetTrack; } - return DRFLAC_TRUE; + return MA_TRUE; } #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) #pragma GCC diagnostic pop #endif #endif /* dr_flac_c end */ -#endif /* DRFLAC_IMPLEMENTATION */ +#endif /* MA_DR_FLAC_IMPLEMENTATION */ #endif /* MA_NO_FLAC */ #if !defined(MA_NO_MP3) && !defined(MA_NO_DECODING) -#if !defined(DR_MP3_IMPLEMENTATION) && !defined(DRMP3_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ +#if !defined(MA_DR_MP3_IMPLEMENTATION) && !defined(MA_DR_MP3_IMPLEMENTATION) /* For backwards compatibility. Will be removed in version 0.11 for cleanliness. */ /* dr_mp3_c begin */ -#ifndef dr_mp3_c -#define dr_mp3_c +#ifndef ma_dr_mp3_c +#define ma_dr_mp3_c #include #include #include -DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision) +MA_API void ma_dr_mp3_version(ma_uint32* pMajor, ma_uint32* pMinor, ma_uint32* pRevision) { if (pMajor) { - *pMajor = DRMP3_VERSION_MAJOR; + *pMajor = MA_DR_MP3_VERSION_MAJOR; } if (pMinor) { - *pMinor = DRMP3_VERSION_MINOR; + *pMinor = MA_DR_MP3_VERSION_MINOR; } if (pRevision) { - *pRevision = DRMP3_VERSION_REVISION; + *pRevision = MA_DR_MP3_VERSION_REVISION; } } -DRMP3_API const char* drmp3_version_string(void) +MA_API const char* ma_dr_mp3_version_string(void) { - return DRMP3_VERSION_STRING; + return MA_DR_MP3_VERSION_STRING; } #if defined(__TINYC__) -#define DR_MP3_NO_SIMD +#define MA_DR_MP3_NO_SIMD #endif -#define DRMP3_OFFSET_PTR(p, offset) ((void*)((drmp3_uint8*)(p) + (offset))) -#define DRMP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 -#ifndef DRMP3_MAX_FRAME_SYNC_MATCHES -#define DRMP3_MAX_FRAME_SYNC_MATCHES 10 +#define MA_DR_MP3_OFFSET_PTR(p, offset) ((void*)((ma_uint8*)(p) + (offset))) +#define MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE 2304 +#ifndef MA_DR_MP3_MAX_FRAME_SYNC_MATCHES +#define MA_DR_MP3_MAX_FRAME_SYNC_MATCHES 10 #endif -#define DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES DRMP3_MAX_FREE_FORMAT_FRAME_SIZE -#define DRMP3_MAX_BITRESERVOIR_BYTES 511 -#define DRMP3_SHORT_BLOCK_TYPE 2 -#define DRMP3_STOP_BLOCK_TYPE 3 -#define DRMP3_MODE_MONO 3 -#define DRMP3_MODE_JOINT_STEREO 1 -#define DRMP3_HDR_SIZE 4 -#define DRMP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) -#define DRMP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) -#define DRMP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) -#define DRMP3_HDR_IS_CRC(h) (!((h[1]) & 1)) -#define DRMP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2) -#define DRMP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8) -#define DRMP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) -#define DRMP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) -#define DRMP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) -#define DRMP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) -#define DRMP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) -#define DRMP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) -#define DRMP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) -#define DRMP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) -#define DRMP3_HDR_GET_MY_SAMPLE_RATE(h) (DRMP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) -#define DRMP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) -#define DRMP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) -#define DRMP3_BITS_DEQUANTIZER_OUT -1 -#define DRMP3_MAX_SCF (255 + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210) -#define DRMP3_MAX_SCFI ((DRMP3_MAX_SCF + 3) & ~3) -#define DRMP3_MIN(a, b) ((a) > (b) ? (b) : (a)) -#define DRMP3_MAX(a, b) ((a) < (b) ? (b) : (a)) -#if !defined(DR_MP3_NO_SIMD) -#if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64)) -#define DR_MP3_ONLY_SIMD +#define MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE +#define MA_DR_MP3_MAX_BITRESERVOIR_BYTES 511 +#define MA_DR_MP3_SHORT_BLOCK_TYPE 2 +#define MA_DR_MP3_STOP_BLOCK_TYPE 3 +#define MA_DR_MP3_MODE_MONO 3 +#define MA_DR_MP3_MODE_JOINT_STEREO 1 +#define MA_DR_MP3_HDR_SIZE 4 +#define MA_DR_MP3_HDR_IS_MONO(h) (((h[3]) & 0xC0) == 0xC0) +#define MA_DR_MP3_HDR_IS_MS_STEREO(h) (((h[3]) & 0xE0) == 0x60) +#define MA_DR_MP3_HDR_IS_FREE_FORMAT(h) (((h[2]) & 0xF0) == 0) +#define MA_DR_MP3_HDR_IS_CRC(h) (!((h[1]) & 1)) +#define MA_DR_MP3_HDR_TEST_PADDING(h) ((h[2]) & 0x2) +#define MA_DR_MP3_HDR_TEST_MPEG1(h) ((h[1]) & 0x8) +#define MA_DR_MP3_HDR_TEST_NOT_MPEG25(h) ((h[1]) & 0x10) +#define MA_DR_MP3_HDR_TEST_I_STEREO(h) ((h[3]) & 0x10) +#define MA_DR_MP3_HDR_TEST_MS_STEREO(h) ((h[3]) & 0x20) +#define MA_DR_MP3_HDR_GET_STEREO_MODE(h) (((h[3]) >> 6) & 3) +#define MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(h) (((h[3]) >> 4) & 3) +#define MA_DR_MP3_HDR_GET_LAYER(h) (((h[1]) >> 1) & 3) +#define MA_DR_MP3_HDR_GET_BITRATE(h) ((h[2]) >> 4) +#define MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) (((h[2]) >> 2) & 3) +#define MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h) (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) + (((h[1] >> 3) & 1) + ((h[1] >> 4) & 1))*3) +#define MA_DR_MP3_HDR_IS_FRAME_576(h) ((h[1] & 14) == 2) +#define MA_DR_MP3_HDR_IS_LAYER_1(h) ((h[1] & 6) == 6) +#define MA_DR_MP3_BITS_DEQUANTIZER_OUT -1 +#define MA_DR_MP3_MAX_SCF (255 + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210) +#define MA_DR_MP3_MAX_SCFI ((MA_DR_MP3_MAX_SCF + 3) & ~3) +#define MA_DR_MP3_MIN(a, b) ((a) > (b) ? (b) : (a)) +#define MA_DR_MP3_MAX(a, b) ((a) < (b) ? (b) : (a)) +#if !defined(MA_DR_MP3_NO_SIMD) +#if !defined(MA_DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64)) +#define MA_DR_MP3_ONLY_SIMD #endif #if ((defined(_MSC_VER) && _MSC_VER >= 1400) && defined(_M_X64)) || ((defined(__i386) || defined(_M_IX86) || defined(__i386__) || defined(__x86_64__)) && ((defined(_M_IX86_FP) && _M_IX86_FP == 2) || defined(__SSE2__))) #if defined(_MSC_VER) #include #endif #include -#define DRMP3_HAVE_SSE 1 -#define DRMP3_HAVE_SIMD 1 -#define DRMP3_VSTORE _mm_storeu_ps -#define DRMP3_VLD _mm_loadu_ps -#define DRMP3_VSET _mm_set1_ps -#define DRMP3_VADD _mm_add_ps -#define DRMP3_VSUB _mm_sub_ps -#define DRMP3_VMUL _mm_mul_ps -#define DRMP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) -#define DRMP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) -#define DRMP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) -#define DRMP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) -typedef __m128 drmp3_f4; -#if defined(_MSC_VER) || defined(DR_MP3_ONLY_SIMD) -#define drmp3_cpuid __cpuid +#define MA_DR_MP3_HAVE_SSE 1 +#define MA_DR_MP3_HAVE_SIMD 1 +#define MA_DR_MP3_VSTORE _mm_storeu_ps +#define MA_DR_MP3_VLD _mm_loadu_ps +#define MA_DR_MP3_VSET _mm_set1_ps +#define MA_DR_MP3_VADD _mm_add_ps +#define MA_DR_MP3_VSUB _mm_sub_ps +#define MA_DR_MP3_VMUL _mm_mul_ps +#define MA_DR_MP3_VMAC(a, x, y) _mm_add_ps(a, _mm_mul_ps(x, y)) +#define MA_DR_MP3_VMSB(a, x, y) _mm_sub_ps(a, _mm_mul_ps(x, y)) +#define MA_DR_MP3_VMUL_S(x, s) _mm_mul_ps(x, _mm_set1_ps(s)) +#define MA_DR_MP3_VREV(x) _mm_shuffle_ps(x, x, _MM_SHUFFLE(0, 1, 2, 3)) +typedef __m128 ma_dr_mp3_f4; +#if defined(_MSC_VER) || defined(MA_DR_MP3_ONLY_SIMD) +#define ma_dr_mp3_cpuid __cpuid #else -static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], const int InfoType) +static __inline__ __attribute__((always_inline)) void ma_dr_mp3_cpuid(int CPUInfo[], const int InfoType) { #if defined(__PIC__) __asm__ __volatile__( @@ -90179,9 +89830,9 @@ static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[], #endif } #endif -static int drmp3_have_simd(void) +static int ma_dr_mp3_have_simd(void) { -#ifdef DR_MP3_ONLY_SIMD +#ifdef MA_DR_MP3_ONLY_SIMD return 1; #else static int g_have_simd; @@ -90193,10 +89844,10 @@ static int drmp3_have_simd(void) #endif if (g_have_simd) goto end; - drmp3_cpuid(CPUInfo, 0); + ma_dr_mp3_cpuid(CPUInfo, 0); if (CPUInfo[0] > 0) { - drmp3_cpuid(CPUInfo, 1); + ma_dr_mp3_cpuid(CPUInfo, 1); g_have_simd = (CPUInfo[3] & (1 << 26)) + 1; return g_have_simd - 1; } @@ -90206,108 +89857,108 @@ end: } #elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64) #include -#define DRMP3_HAVE_SSE 0 -#define DRMP3_HAVE_SIMD 1 -#define DRMP3_VSTORE vst1q_f32 -#define DRMP3_VLD vld1q_f32 -#define DRMP3_VSET vmovq_n_f32 -#define DRMP3_VADD vaddq_f32 -#define DRMP3_VSUB vsubq_f32 -#define DRMP3_VMUL vmulq_f32 -#define DRMP3_VMAC(a, x, y) vmlaq_f32(a, x, y) -#define DRMP3_VMSB(a, x, y) vmlsq_f32(a, x, y) -#define DRMP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) -#define DRMP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) -typedef float32x4_t drmp3_f4; -static int drmp3_have_simd(void) +#define MA_DR_MP3_HAVE_SSE 0 +#define MA_DR_MP3_HAVE_SIMD 1 +#define MA_DR_MP3_VSTORE vst1q_f32 +#define MA_DR_MP3_VLD vld1q_f32 +#define MA_DR_MP3_VSET vmovq_n_f32 +#define MA_DR_MP3_VADD vaddq_f32 +#define MA_DR_MP3_VSUB vsubq_f32 +#define MA_DR_MP3_VMUL vmulq_f32 +#define MA_DR_MP3_VMAC(a, x, y) vmlaq_f32(a, x, y) +#define MA_DR_MP3_VMSB(a, x, y) vmlsq_f32(a, x, y) +#define MA_DR_MP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s)) +#define MA_DR_MP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x))) +typedef float32x4_t ma_dr_mp3_f4; +static int ma_dr_mp3_have_simd(void) { return 1; } #else -#define DRMP3_HAVE_SSE 0 -#define DRMP3_HAVE_SIMD 0 -#ifdef DR_MP3_ONLY_SIMD -#error DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled +#define MA_DR_MP3_HAVE_SSE 0 +#define MA_DR_MP3_HAVE_SIMD 0 +#ifdef MA_DR_MP3_ONLY_SIMD +#error MA_DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled #endif #endif #else -#define DRMP3_HAVE_SIMD 0 +#define MA_DR_MP3_HAVE_SIMD 0 #endif #if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64) -#define DRMP3_HAVE_ARMV6 1 -static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_arm(drmp3_int32 a) +#define MA_DR_MP3_HAVE_ARMV6 1 +static __inline__ __attribute__((always_inline)) ma_int32 ma_dr_mp3_clip_int16_arm(ma_int32 a) { - drmp3_int32 x = 0; + ma_int32 x = 0; __asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a)); return x; } #else -#define DRMP3_HAVE_ARMV6 0 +#define MA_DR_MP3_HAVE_ARMV6 0 #endif -#ifndef DRMP3_ASSERT +#ifndef MA_DR_MP3_ASSERT #include -#define DRMP3_ASSERT(expression) assert(expression) +#define MA_DR_MP3_ASSERT(expression) assert(expression) #endif -#ifndef DRMP3_COPY_MEMORY -#define DRMP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#ifndef MA_DR_MP3_COPY_MEMORY +#define MA_DR_MP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) #endif -#ifndef DRMP3_MOVE_MEMORY -#define DRMP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) +#ifndef MA_DR_MP3_MOVE_MEMORY +#define MA_DR_MP3_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) #endif -#ifndef DRMP3_ZERO_MEMORY -#define DRMP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) +#ifndef MA_DR_MP3_ZERO_MEMORY +#define MA_DR_MP3_ZERO_MEMORY(p, sz) memset((p), 0, (sz)) #endif -#define DRMP3_ZERO_OBJECT(p) DRMP3_ZERO_MEMORY((p), sizeof(*(p))) -#ifndef DRMP3_MALLOC -#define DRMP3_MALLOC(sz) malloc((sz)) +#define MA_DR_MP3_ZERO_OBJECT(p) MA_DR_MP3_ZERO_MEMORY((p), sizeof(*(p))) +#ifndef MA_DR_MP3_MALLOC +#define MA_DR_MP3_MALLOC(sz) malloc((sz)) #endif -#ifndef DRMP3_REALLOC -#define DRMP3_REALLOC(p, sz) realloc((p), (sz)) +#ifndef MA_DR_MP3_REALLOC +#define MA_DR_MP3_REALLOC(p, sz) realloc((p), (sz)) #endif -#ifndef DRMP3_FREE -#define DRMP3_FREE(p) free((p)) +#ifndef MA_DR_MP3_FREE +#define MA_DR_MP3_FREE(p) free((p)) #endif typedef struct { - const drmp3_uint8 *buf; + const ma_uint8 *buf; int pos, limit; -} drmp3_bs; +} ma_dr_mp3_bs; typedef struct { float scf[3*64]; - drmp3_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; -} drmp3_L12_scale_info; + ma_uint8 total_bands, stereo_bands, bitalloc[64], scfcod[64]; +} ma_dr_mp3_L12_scale_info; typedef struct { - drmp3_uint8 tab_offset, code_tab_width, band_count; -} drmp3_L12_subband_alloc; + ma_uint8 tab_offset, code_tab_width, band_count; +} ma_dr_mp3_L12_subband_alloc; typedef struct { - const drmp3_uint8 *sfbtab; - drmp3_uint16 part_23_length, big_values, scalefac_compress; - drmp3_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; - drmp3_uint8 table_select[3], region_count[3], subblock_gain[3]; - drmp3_uint8 preflag, scalefac_scale, count1_table, scfsi; -} drmp3_L3_gr_info; + const ma_uint8 *sfbtab; + ma_uint16 part_23_length, big_values, scalefac_compress; + ma_uint8 global_gain, block_type, mixed_block_flag, n_long_sfb, n_short_sfb; + ma_uint8 table_select[3], region_count[3], subblock_gain[3]; + ma_uint8 preflag, scalefac_scale, count1_table, scfsi; +} ma_dr_mp3_L3_gr_info; typedef struct { - drmp3_bs bs; - drmp3_uint8 maindata[DRMP3_MAX_BITRESERVOIR_BYTES + DRMP3_MAX_L3_FRAME_PAYLOAD_BYTES]; - drmp3_L3_gr_info gr_info[4]; + ma_dr_mp3_bs bs; + ma_uint8 maindata[MA_DR_MP3_MAX_BITRESERVOIR_BYTES + MA_DR_MP3_MAX_L3_FRAME_PAYLOAD_BYTES]; + ma_dr_mp3_L3_gr_info gr_info[4]; float grbuf[2][576], scf[40], syn[18 + 15][2*32]; - drmp3_uint8 ist_pos[2][39]; -} drmp3dec_scratch; -static void drmp3_bs_init(drmp3_bs *bs, const drmp3_uint8 *data, int bytes) + ma_uint8 ist_pos[2][39]; +} ma_dr_mp3dec_scratch; +static void ma_dr_mp3_bs_init(ma_dr_mp3_bs *bs, const ma_uint8 *data, int bytes) { bs->buf = data; bs->pos = 0; bs->limit = bytes*8; } -static drmp3_uint32 drmp3_bs_get_bits(drmp3_bs *bs, int n) +static ma_uint32 ma_dr_mp3_bs_get_bits(ma_dr_mp3_bs *bs, int n) { - drmp3_uint32 next, cache = 0, s = bs->pos & 7; + ma_uint32 next, cache = 0, s = bs->pos & 7; int shl = n + s; - const drmp3_uint8 *p = bs->buf + (bs->pos >> 3); + const ma_uint8 *p = bs->buf + (bs->pos >> 3); if ((bs->pos += n) > bs->limit) return 0; next = *p++ & (255 >> s); @@ -90318,72 +89969,72 @@ static drmp3_uint32 drmp3_bs_get_bits(drmp3_bs *bs, int n) } return cache | (next >> -shl); } -static int drmp3_hdr_valid(const drmp3_uint8 *h) +static int ma_dr_mp3_hdr_valid(const ma_uint8 *h) { return h[0] == 0xff && ((h[1] & 0xF0) == 0xf0 || (h[1] & 0xFE) == 0xe2) && - (DRMP3_HDR_GET_LAYER(h) != 0) && - (DRMP3_HDR_GET_BITRATE(h) != 15) && - (DRMP3_HDR_GET_SAMPLE_RATE(h) != 3); + (MA_DR_MP3_HDR_GET_LAYER(h) != 0) && + (MA_DR_MP3_HDR_GET_BITRATE(h) != 15) && + (MA_DR_MP3_HDR_GET_SAMPLE_RATE(h) != 3); } -static int drmp3_hdr_compare(const drmp3_uint8 *h1, const drmp3_uint8 *h2) +static int ma_dr_mp3_hdr_compare(const ma_uint8 *h1, const ma_uint8 *h2) { - return drmp3_hdr_valid(h2) && + return ma_dr_mp3_hdr_valid(h2) && ((h1[1] ^ h2[1]) & 0xFE) == 0 && ((h1[2] ^ h2[2]) & 0x0C) == 0 && - !(DRMP3_HDR_IS_FREE_FORMAT(h1) ^ DRMP3_HDR_IS_FREE_FORMAT(h2)); + !(MA_DR_MP3_HDR_IS_FREE_FORMAT(h1) ^ MA_DR_MP3_HDR_IS_FREE_FORMAT(h2)); } -static unsigned drmp3_hdr_bitrate_kbps(const drmp3_uint8 *h) +static unsigned ma_dr_mp3_hdr_bitrate_kbps(const ma_uint8 *h) { - static const drmp3_uint8 halfrate[2][3][15] = { + static const ma_uint8 halfrate[2][3][15] = { { { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,4,8,12,16,20,24,28,32,40,48,56,64,72,80 }, { 0,16,24,28,32,40,48,56,64,72,80,88,96,112,128 } }, { { 0,16,20,24,28,32,40,48,56,64,80,96,112,128,160 }, { 0,16,24,28,32,40,48,56,64,80,96,112,128,160,192 }, { 0,16,32,48,64,80,96,112,128,144,160,176,192,208,224 } }, }; - return 2*halfrate[!!DRMP3_HDR_TEST_MPEG1(h)][DRMP3_HDR_GET_LAYER(h) - 1][DRMP3_HDR_GET_BITRATE(h)]; + return 2*halfrate[!!MA_DR_MP3_HDR_TEST_MPEG1(h)][MA_DR_MP3_HDR_GET_LAYER(h) - 1][MA_DR_MP3_HDR_GET_BITRATE(h)]; } -static unsigned drmp3_hdr_sample_rate_hz(const drmp3_uint8 *h) +static unsigned ma_dr_mp3_hdr_sample_rate_hz(const ma_uint8 *h) { static const unsigned g_hz[3] = { 44100, 48000, 32000 }; - return g_hz[DRMP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!DRMP3_HDR_TEST_MPEG1(h) >> (int)!DRMP3_HDR_TEST_NOT_MPEG25(h); + return g_hz[MA_DR_MP3_HDR_GET_SAMPLE_RATE(h)] >> (int)!MA_DR_MP3_HDR_TEST_MPEG1(h) >> (int)!MA_DR_MP3_HDR_TEST_NOT_MPEG25(h); } -static unsigned drmp3_hdr_frame_samples(const drmp3_uint8 *h) +static unsigned ma_dr_mp3_hdr_frame_samples(const ma_uint8 *h) { - return DRMP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)DRMP3_HDR_IS_FRAME_576(h)); + return MA_DR_MP3_HDR_IS_LAYER_1(h) ? 384 : (1152 >> (int)MA_DR_MP3_HDR_IS_FRAME_576(h)); } -static int drmp3_hdr_frame_bytes(const drmp3_uint8 *h, int free_format_size) +static int ma_dr_mp3_hdr_frame_bytes(const ma_uint8 *h, int free_format_size) { - int frame_bytes = drmp3_hdr_frame_samples(h)*drmp3_hdr_bitrate_kbps(h)*125/drmp3_hdr_sample_rate_hz(h); - if (DRMP3_HDR_IS_LAYER_1(h)) + int frame_bytes = ma_dr_mp3_hdr_frame_samples(h)*ma_dr_mp3_hdr_bitrate_kbps(h)*125/ma_dr_mp3_hdr_sample_rate_hz(h); + if (MA_DR_MP3_HDR_IS_LAYER_1(h)) { frame_bytes &= ~3; } return frame_bytes ? frame_bytes : free_format_size; } -static int drmp3_hdr_padding(const drmp3_uint8 *h) +static int ma_dr_mp3_hdr_padding(const ma_uint8 *h) { - return DRMP3_HDR_TEST_PADDING(h) ? (DRMP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0; + return MA_DR_MP3_HDR_TEST_PADDING(h) ? (MA_DR_MP3_HDR_IS_LAYER_1(h) ? 4 : 1) : 0; } -#ifndef DR_MP3_ONLY_MP3 -static const drmp3_L12_subband_alloc *drmp3_L12_subband_alloc_table(const drmp3_uint8 *hdr, drmp3_L12_scale_info *sci) +#ifndef MA_DR_MP3_ONLY_MP3 +static const ma_dr_mp3_L12_subband_alloc *ma_dr_mp3_L12_subband_alloc_table(const ma_uint8 *hdr, ma_dr_mp3_L12_scale_info *sci) { - const drmp3_L12_subband_alloc *alloc; - int mode = DRMP3_HDR_GET_STEREO_MODE(hdr); - int nbands, stereo_bands = (mode == DRMP3_MODE_MONO) ? 0 : (mode == DRMP3_MODE_JOINT_STEREO) ? (DRMP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; - if (DRMP3_HDR_IS_LAYER_1(hdr)) + const ma_dr_mp3_L12_subband_alloc *alloc; + int mode = MA_DR_MP3_HDR_GET_STEREO_MODE(hdr); + int nbands, stereo_bands = (mode == MA_DR_MP3_MODE_MONO) ? 0 : (mode == MA_DR_MP3_MODE_JOINT_STEREO) ? (MA_DR_MP3_HDR_GET_STEREO_MODE_EXT(hdr) << 2) + 4 : 32; + if (MA_DR_MP3_HDR_IS_LAYER_1(hdr)) { - static const drmp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } }; + static const ma_dr_mp3_L12_subband_alloc g_alloc_L1[] = { { 76, 4, 32 } }; alloc = g_alloc_L1; nbands = 32; - } else if (!DRMP3_HDR_TEST_MPEG1(hdr)) + } else if (!MA_DR_MP3_HDR_TEST_MPEG1(hdr)) { - static const drmp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; + static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M2[] = { { 60, 4, 4 }, { 44, 3, 7 }, { 44, 2, 19 } }; alloc = g_alloc_L2M2; nbands = 30; } else { - static const drmp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; - int sample_rate_idx = DRMP3_HDR_GET_SAMPLE_RATE(hdr); - unsigned kbps = drmp3_hdr_bitrate_kbps(hdr) >> (int)(mode != DRMP3_MODE_MONO); + static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1[] = { { 0, 4, 3 }, { 16, 4, 8 }, { 32, 3, 12 }, { 40, 2, 7 } }; + int sample_rate_idx = MA_DR_MP3_HDR_GET_SAMPLE_RATE(hdr); + unsigned kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr) >> (int)(mode != MA_DR_MP3_MODE_MONO); if (!kbps) { kbps = 192; @@ -90392,7 +90043,7 @@ static const drmp3_L12_subband_alloc *drmp3_L12_subband_alloc_table(const drmp3_ nbands = 27; if (kbps < 56) { - static const drmp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; + static const ma_dr_mp3_L12_subband_alloc g_alloc_L2M1_lowrate[] = { { 44, 4, 2 }, { 44, 3, 10 } }; alloc = g_alloc_L2M1_lowrate; nbands = sample_rate_idx == 2 ? 12 : 8; } else if (kbps >= 96 && sample_rate_idx != 1) @@ -90400,15 +90051,15 @@ static const drmp3_L12_subband_alloc *drmp3_L12_subband_alloc_table(const drmp3_ nbands = 30; } } - sci->total_bands = (drmp3_uint8)nbands; - sci->stereo_bands = (drmp3_uint8)DRMP3_MIN(stereo_bands, nbands); + sci->total_bands = (ma_uint8)nbands; + sci->stereo_bands = (ma_uint8)MA_DR_MP3_MIN(stereo_bands, nbands); return alloc; } -static void drmp3_L12_read_scalefactors(drmp3_bs *bs, drmp3_uint8 *pba, drmp3_uint8 *scfcod, int bands, float *scf) +static void ma_dr_mp3_L12_read_scalefactors(ma_dr_mp3_bs *bs, ma_uint8 *pba, ma_uint8 *scfcod, int bands, float *scf) { static const float g_deq_L12[18*3] = { -#define DRMP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x - DRMP3_DQ(3),DRMP3_DQ(7),DRMP3_DQ(15),DRMP3_DQ(31),DRMP3_DQ(63),DRMP3_DQ(127),DRMP3_DQ(255),DRMP3_DQ(511),DRMP3_DQ(1023),DRMP3_DQ(2047),DRMP3_DQ(4095),DRMP3_DQ(8191),DRMP3_DQ(16383),DRMP3_DQ(32767),DRMP3_DQ(65535),DRMP3_DQ(3),DRMP3_DQ(5),DRMP3_DQ(9) +#define MA_DR_MP3_DQ(x) 9.53674316e-07f/x, 7.56931807e-07f/x, 6.00777173e-07f/x + MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(7),MA_DR_MP3_DQ(15),MA_DR_MP3_DQ(31),MA_DR_MP3_DQ(63),MA_DR_MP3_DQ(127),MA_DR_MP3_DQ(255),MA_DR_MP3_DQ(511),MA_DR_MP3_DQ(1023),MA_DR_MP3_DQ(2047),MA_DR_MP3_DQ(4095),MA_DR_MP3_DQ(8191),MA_DR_MP3_DQ(16383),MA_DR_MP3_DQ(32767),MA_DR_MP3_DQ(65535),MA_DR_MP3_DQ(3),MA_DR_MP3_DQ(5),MA_DR_MP3_DQ(9) }; int i, m; for (i = 0; i < bands; i++) @@ -90420,16 +90071,16 @@ static void drmp3_L12_read_scalefactors(drmp3_bs *bs, drmp3_uint8 *pba, drmp3_ui { if (mask & m) { - int b = drmp3_bs_get_bits(bs, 6); + int b = ma_dr_mp3_bs_get_bits(bs, 6); s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3); } *scf++ = s; } } } -static void drmp3_L12_read_scale_info(const drmp3_uint8 *hdr, drmp3_bs *bs, drmp3_L12_scale_info *sci) +static void ma_dr_mp3_L12_read_scale_info(const ma_uint8 *hdr, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci) { - static const drmp3_uint8 g_bitalloc_code_tab[] = { + static const ma_uint8 g_bitalloc_code_tab[] = { 0,17, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16, 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,16, 0,17,18, 3,19,4,5,16, @@ -90438,12 +90089,12 @@ static void drmp3_L12_read_scale_info(const drmp3_uint8 *hdr, drmp3_bs *bs, drmp 0,17,18, 3,19,4,5, 6,7, 8, 9,10,11,12,13,14, 0, 2, 3, 4, 5,6,7, 8,9,10,11,12,13,14,15,16 }; - const drmp3_L12_subband_alloc *subband_alloc = drmp3_L12_subband_alloc_table(hdr, sci); + const ma_dr_mp3_L12_subband_alloc *subband_alloc = ma_dr_mp3_L12_subband_alloc_table(hdr, sci); int i, k = 0, ba_bits = 0; - const drmp3_uint8 *ba_code_tab = g_bitalloc_code_tab; + const ma_uint8 *ba_code_tab = g_bitalloc_code_tab; for (i = 0; i < sci->total_bands; i++) { - drmp3_uint8 ba; + ma_uint8 ba; if (i == k) { k += subband_alloc->band_count; @@ -90451,25 +90102,25 @@ static void drmp3_L12_read_scale_info(const drmp3_uint8 *hdr, drmp3_bs *bs, drmp ba_code_tab = g_bitalloc_code_tab + subband_alloc->tab_offset; subband_alloc++; } - ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; + ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)]; sci->bitalloc[2*i] = ba; if (i < sci->stereo_bands) { - ba = ba_code_tab[drmp3_bs_get_bits(bs, ba_bits)]; + ba = ba_code_tab[ma_dr_mp3_bs_get_bits(bs, ba_bits)]; } sci->bitalloc[2*i + 1] = sci->stereo_bands ? ba : 0; } for (i = 0; i < 2*sci->total_bands; i++) { - sci->scfcod[i] = (drmp3_uint8)(sci->bitalloc[i] ? DRMP3_HDR_IS_LAYER_1(hdr) ? 2 : drmp3_bs_get_bits(bs, 2) : 6); + sci->scfcod[i] = (ma_uint8)(sci->bitalloc[i] ? MA_DR_MP3_HDR_IS_LAYER_1(hdr) ? 2 : ma_dr_mp3_bs_get_bits(bs, 2) : 6); } - drmp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); + ma_dr_mp3_L12_read_scalefactors(bs, sci->bitalloc, sci->scfcod, sci->total_bands*2, sci->scf); for (i = sci->stereo_bands; i < sci->total_bands; i++) { sci->bitalloc[2*i + 1] = 0; } } -static int drmp3_L12_dequantize_granule(float *grbuf, drmp3_bs *bs, drmp3_L12_scale_info *sci, int group_size) +static int ma_dr_mp3_L12_dequantize_granule(float *grbuf, ma_dr_mp3_bs *bs, ma_dr_mp3_L12_scale_info *sci, int group_size) { int i, j, k, choff = 576; for (j = 0; j < 4; j++) @@ -90485,12 +90136,12 @@ static int drmp3_L12_dequantize_granule(float *grbuf, drmp3_bs *bs, drmp3_L12_sc int half = (1 << (ba - 1)) - 1; for (k = 0; k < group_size; k++) { - dst[k] = (float)((int)drmp3_bs_get_bits(bs, ba) - half); + dst[k] = (float)((int)ma_dr_mp3_bs_get_bits(bs, ba) - half); } } else { unsigned mod = (2 << (ba - 17)) + 1; - unsigned code = drmp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); + unsigned code = ma_dr_mp3_bs_get_bits(bs, mod + 2 - (mod >> 3)); for (k = 0; k < group_size; k++, code /= mod) { dst[k] = (float)((int)(code % mod - mod/2)); @@ -90503,10 +90154,10 @@ static int drmp3_L12_dequantize_granule(float *grbuf, drmp3_bs *bs, drmp3_L12_sc } return group_size*4; } -static void drmp3_L12_apply_scf_384(drmp3_L12_scale_info *sci, const float *scf, float *dst) +static void ma_dr_mp3_L12_apply_scf_384(ma_dr_mp3_L12_scale_info *sci, const float *scf, float *dst) { int i, k; - DRMP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); + MA_DR_MP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float)); for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6) { for (k = 0; k < 12; k++) @@ -90517,9 +90168,9 @@ static void drmp3_L12_apply_scf_384(drmp3_L12_scale_info *sci, const float *scf, } } #endif -static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr) +static int ma_dr_mp3_L3_read_side_info(ma_dr_mp3_bs *bs, ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr) { - static const drmp3_uint8 g_scf_long[8][23] = { + static const ma_uint8 g_scf_long[8][23] = { { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, { 12,12,12,12,12,12,16,20,24,28,32,40,48,56,64,76,90,2,2,2,2,2,0 }, { 6,6,6,6,6,6,8,10,12,14,16,20,24,28,32,38,46,52,60,68,58,54,0 }, @@ -90529,7 +90180,7 @@ static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drm { 4,4,4,4,4,4,6,6,6,8,10,12,16,18,22,28,34,40,46,54,54,192,0 }, { 4,4,4,4,4,4,6,6,8,10,12,16,20,24,30,38,46,56,68,84,102,26,0 } }; - static const drmp3_uint8 g_scf_short[8][40] = { + static const ma_uint8 g_scf_short[8][40] = { { 4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, { 8,8,8,8,8,8,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, { 4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, @@ -90539,7 +90190,7 @@ static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drm { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,6,6,6,10,10,10,12,12,12,14,14,14,16,16,16,20,20,20,26,26,26,66,66,66,0 }, { 4,4,4,4,4,4,4,4,4,4,4,4,6,6,6,8,8,8,12,12,12,16,16,16,20,20,20,26,26,26,34,34,34,42,42,42,12,12,12,0 } }; - static const drmp3_uint8 g_scf_mixed[8][40] = { + static const ma_uint8 g_scf_mixed[8][40] = { { 6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,12,12,12,14,14,14,18,18,18,24,24,24,30,30,30,40,40,40,18,18,18,0 }, { 12,12,12,4,4,4,8,8,8,12,12,12,16,16,16,20,20,20,24,24,24,28,28,28,36,36,36,2,2,2,2,2,2,2,2,2,26,26,26,0 }, { 6,6,6,6,6,6,6,6,6,6,6,6,8,8,8,10,10,10,14,14,14,18,18,18,26,26,26,32,32,32,42,42,42,18,18,18,0 }, @@ -90551,46 +90202,46 @@ static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drm }; unsigned tables, scfsi = 0; int main_data_begin, part_23_sum = 0; - int gr_count = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; - int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); - if (DRMP3_HDR_TEST_MPEG1(hdr)) + int gr_count = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; + int sr_idx = MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0); + if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) { gr_count *= 2; - main_data_begin = drmp3_bs_get_bits(bs, 9); - scfsi = drmp3_bs_get_bits(bs, 7 + gr_count); + main_data_begin = ma_dr_mp3_bs_get_bits(bs, 9); + scfsi = ma_dr_mp3_bs_get_bits(bs, 7 + gr_count); } else { - main_data_begin = drmp3_bs_get_bits(bs, 8 + gr_count) >> gr_count; + main_data_begin = ma_dr_mp3_bs_get_bits(bs, 8 + gr_count) >> gr_count; } do { - if (DRMP3_HDR_IS_MONO(hdr)) + if (MA_DR_MP3_HDR_IS_MONO(hdr)) { scfsi <<= 4; } - gr->part_23_length = (drmp3_uint16)drmp3_bs_get_bits(bs, 12); + gr->part_23_length = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 12); part_23_sum += gr->part_23_length; - gr->big_values = (drmp3_uint16)drmp3_bs_get_bits(bs, 9); + gr->big_values = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, 9); if (gr->big_values > 288) { return -1; } - gr->global_gain = (drmp3_uint8)drmp3_bs_get_bits(bs, 8); - gr->scalefac_compress = (drmp3_uint16)drmp3_bs_get_bits(bs, DRMP3_HDR_TEST_MPEG1(hdr) ? 4 : 9); + gr->global_gain = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 8); + gr->scalefac_compress = (ma_uint16)ma_dr_mp3_bs_get_bits(bs, MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 4 : 9); gr->sfbtab = g_scf_long[sr_idx]; gr->n_long_sfb = 22; gr->n_short_sfb = 0; - if (drmp3_bs_get_bits(bs, 1)) + if (ma_dr_mp3_bs_get_bits(bs, 1)) { - gr->block_type = (drmp3_uint8)drmp3_bs_get_bits(bs, 2); + gr->block_type = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 2); if (!gr->block_type) { return -1; } - gr->mixed_block_flag = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); + gr->mixed_block_flag = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); gr->region_count[0] = 7; gr->region_count[1] = 255; - if (gr->block_type == DRMP3_SHORT_BLOCK_TYPE) + if (gr->block_type == MA_DR_MP3_SHORT_BLOCK_TYPE) { scfsi &= 0x0F0F; if (!gr->mixed_block_flag) @@ -90602,31 +90253,31 @@ static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drm } else { gr->sfbtab = g_scf_mixed[sr_idx]; - gr->n_long_sfb = DRMP3_HDR_TEST_MPEG1(hdr) ? 8 : 6; + gr->n_long_sfb = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 8 : 6; gr->n_short_sfb = 30; } } - tables = drmp3_bs_get_bits(bs, 10); + tables = ma_dr_mp3_bs_get_bits(bs, 10); tables <<= 5; - gr->subblock_gain[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); - gr->subblock_gain[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); - gr->subblock_gain[2] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + gr->subblock_gain[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); + gr->subblock_gain[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); + gr->subblock_gain[2] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); } else { gr->block_type = 0; gr->mixed_block_flag = 0; - tables = drmp3_bs_get_bits(bs, 15); - gr->region_count[0] = (drmp3_uint8)drmp3_bs_get_bits(bs, 4); - gr->region_count[1] = (drmp3_uint8)drmp3_bs_get_bits(bs, 3); + tables = ma_dr_mp3_bs_get_bits(bs, 15); + gr->region_count[0] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 4); + gr->region_count[1] = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 3); gr->region_count[2] = 255; } - gr->table_select[0] = (drmp3_uint8)(tables >> 10); - gr->table_select[1] = (drmp3_uint8)((tables >> 5) & 31); - gr->table_select[2] = (drmp3_uint8)((tables) & 31); - gr->preflag = (drmp3_uint8)(DRMP3_HDR_TEST_MPEG1(hdr) ? drmp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500)); - gr->scalefac_scale = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); - gr->count1_table = (drmp3_uint8)drmp3_bs_get_bits(bs, 1); - gr->scfsi = (drmp3_uint8)((scfsi >> 12) & 15); + gr->table_select[0] = (ma_uint8)(tables >> 10); + gr->table_select[1] = (ma_uint8)((tables >> 5) & 31); + gr->table_select[2] = (ma_uint8)((tables) & 31); + gr->preflag = (ma_uint8)(MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? ma_dr_mp3_bs_get_bits(bs, 1) : (gr->scalefac_compress >= 500)); + gr->scalefac_scale = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); + gr->count1_table = (ma_uint8)ma_dr_mp3_bs_get_bits(bs, 1); + gr->scfsi = (ma_uint8)((scfsi >> 12) & 15); scfsi <<= 4; gr++; } while(--gr_count); @@ -90636,7 +90287,7 @@ static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drm } return main_data_begin; } -static void drmp3_L3_read_scalefactors(drmp3_uint8 *scf, drmp3_uint8 *ist_pos, const drmp3_uint8 *scf_size, const drmp3_uint8 *scf_count, drmp3_bs *bitbuf, int scfsi) +static void ma_dr_mp3_L3_read_scalefactors(ma_uint8 *scf, ma_uint8 *ist_pos, const ma_uint8 *scf_size, const ma_uint8 *scf_count, ma_dr_mp3_bs *bitbuf, int scfsi) { int i, k; for (i = 0; i < 4 && scf_count[i]; i++, scfsi *= 2) @@ -90644,22 +90295,22 @@ static void drmp3_L3_read_scalefactors(drmp3_uint8 *scf, drmp3_uint8 *ist_pos, c int cnt = scf_count[i]; if (scfsi & 8) { - DRMP3_COPY_MEMORY(scf, ist_pos, cnt); + MA_DR_MP3_COPY_MEMORY(scf, ist_pos, cnt); } else { int bits = scf_size[i]; if (!bits) { - DRMP3_ZERO_MEMORY(scf, cnt); - DRMP3_ZERO_MEMORY(ist_pos, cnt); + MA_DR_MP3_ZERO_MEMORY(scf, cnt); + MA_DR_MP3_ZERO_MEMORY(ist_pos, cnt); } else { int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1; for (k = 0; k < cnt; k++) { - int s = drmp3_bs_get_bits(bitbuf, bits); - ist_pos[k] = (drmp3_uint8)(s == max_scf ? -1 : s); - scf[k] = (drmp3_uint8)s; + int s = ma_dr_mp3_bs_get_bits(bitbuf, bits); + ist_pos[k] = (ma_uint8)(s == max_scf ? -1 : s); + scf[k] = (ma_uint8)s; } } } @@ -90668,86 +90319,86 @@ static void drmp3_L3_read_scalefactors(drmp3_uint8 *scf, drmp3_uint8 *ist_pos, c } scf[0] = scf[1] = scf[2] = 0; } -static float drmp3_L3_ldexp_q2(float y, int exp_q2) +static float ma_dr_mp3_L3_ldexp_q2(float y, int exp_q2) { static const float g_expfrac[4] = { 9.31322575e-10f,7.83145814e-10f,6.58544508e-10f,5.53767716e-10f }; int e; do { - e = DRMP3_MIN(30*4, exp_q2); + e = MA_DR_MP3_MIN(30*4, exp_q2); y *= g_expfrac[e & 3]*(1 << 30 >> (e >> 2)); } while ((exp_q2 -= e) > 0); return y; } -static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *ist_pos, drmp3_bs *bs, const drmp3_L3_gr_info *gr, float *scf, int ch) +static void ma_dr_mp3_L3_decode_scalefactors(const ma_uint8 *hdr, ma_uint8 *ist_pos, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr, float *scf, int ch) { - static const drmp3_uint8 g_scf_partitions[3][28] = { + static const ma_uint8 g_scf_partitions[3][28] = { { 6,5,5, 5,6,5,5,5,6,5, 7,3,11,10,0,0, 7, 7, 7,0, 6, 6,6,3, 8, 8,5,0 }, { 8,9,6,12,6,9,9,9,6,9,12,6,15,18,0,0, 6,15,12,0, 6,12,9,6, 6,18,9,0 }, { 9,9,6,12,9,9,9,9,9,9,12,6,18,18,0,0,12,12,12,0,12, 9,9,6,15,12,9,0 } }; - const drmp3_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; - drmp3_uint8 scf_size[4], iscf[40]; + const ma_uint8 *scf_partition = g_scf_partitions[!!gr->n_short_sfb + !gr->n_long_sfb]; + ma_uint8 scf_size[4], iscf[40]; int i, scf_shift = gr->scalefac_scale + 1, gain_exp, scfsi = gr->scfsi; float gain; - if (DRMP3_HDR_TEST_MPEG1(hdr)) + if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) { - static const drmp3_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; + static const ma_uint8 g_scfc_decode[16] = { 0,1,2,3, 12,5,6,7, 9,10,11,13, 14,15,18,19 }; int part = g_scfc_decode[gr->scalefac_compress]; - scf_size[1] = scf_size[0] = (drmp3_uint8)(part >> 2); - scf_size[3] = scf_size[2] = (drmp3_uint8)(part & 3); + scf_size[1] = scf_size[0] = (ma_uint8)(part >> 2); + scf_size[3] = scf_size[2] = (ma_uint8)(part & 3); } else { - static const drmp3_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; - int k, modprod, sfc, ist = DRMP3_HDR_TEST_I_STEREO(hdr) && ch; + static const ma_uint8 g_mod[6*4] = { 5,5,4,4,5,5,4,1,4,3,1,1,5,6,6,1,4,4,4,1,4,3,1,1 }; + int k, modprod, sfc, ist = MA_DR_MP3_HDR_TEST_I_STEREO(hdr) && ch; sfc = gr->scalefac_compress >> ist; for (k = ist*3*4; sfc >= 0; sfc -= modprod, k += 4) { for (modprod = 1, i = 3; i >= 0; i--) { - scf_size[i] = (drmp3_uint8)(sfc / modprod % g_mod[k + i]); + scf_size[i] = (ma_uint8)(sfc / modprod % g_mod[k + i]); modprod *= g_mod[k + i]; } } scf_partition += k; scfsi = -16; } - drmp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); + ma_dr_mp3_L3_read_scalefactors(iscf, ist_pos, scf_size, scf_partition, bs, scfsi); if (gr->n_short_sfb) { int sh = 3 - scf_shift; for (i = 0; i < gr->n_short_sfb; i += 3) { - iscf[gr->n_long_sfb + i + 0] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh)); - iscf[gr->n_long_sfb + i + 1] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh)); - iscf[gr->n_long_sfb + i + 2] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh)); + iscf[gr->n_long_sfb + i + 0] = (ma_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh)); + iscf[gr->n_long_sfb + i + 1] = (ma_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh)); + iscf[gr->n_long_sfb + i + 2] = (ma_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh)); } } else if (gr->preflag) { - static const drmp3_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; + static const ma_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 }; for (i = 0; i < 10; i++) { - iscf[11 + i] = (drmp3_uint8)(iscf[11 + i] + g_preamp[i]); + iscf[11 + i] = (ma_uint8)(iscf[11 + i] + g_preamp[i]); } } - gain_exp = gr->global_gain + DRMP3_BITS_DEQUANTIZER_OUT*4 - 210 - (DRMP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0); - gain = drmp3_L3_ldexp_q2(1 << (DRMP3_MAX_SCFI/4), DRMP3_MAX_SCFI - gain_exp); + gain_exp = gr->global_gain + MA_DR_MP3_BITS_DEQUANTIZER_OUT*4 - 210 - (MA_DR_MP3_HDR_IS_MS_STEREO(hdr) ? 2 : 0); + gain = ma_dr_mp3_L3_ldexp_q2(1 << (MA_DR_MP3_MAX_SCFI/4), MA_DR_MP3_MAX_SCFI - gain_exp); for (i = 0; i < (int)(gr->n_long_sfb + gr->n_short_sfb); i++) { - scf[i] = drmp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); + scf[i] = ma_dr_mp3_L3_ldexp_q2(gain, iscf[i] << scf_shift); } } -static const float g_drmp3_pow43[129 + 16] = { +static const float g_ma_dr_mp3_pow43[129 + 16] = { 0,-1,-2.519842f,-4.326749f,-6.349604f,-8.549880f,-10.902724f,-13.390518f,-16.000000f,-18.720754f,-21.544347f,-24.463781f,-27.473142f,-30.567351f,-33.741992f,-36.993181f, 0,1,2.519842f,4.326749f,6.349604f,8.549880f,10.902724f,13.390518f,16.000000f,18.720754f,21.544347f,24.463781f,27.473142f,30.567351f,33.741992f,36.993181f,40.317474f,43.711787f,47.173345f,50.699631f,54.288352f,57.937408f,61.644865f,65.408941f,69.227979f,73.100443f,77.024898f,81.000000f,85.024491f,89.097188f,93.216975f,97.382800f,101.593667f,105.848633f,110.146801f,114.487321f,118.869381f,123.292209f,127.755065f,132.257246f,136.798076f,141.376907f,145.993119f,150.646117f,155.335327f,160.060199f,164.820202f,169.614826f,174.443577f,179.305980f,184.201575f,189.129918f,194.090580f,199.083145f,204.107210f,209.162385f,214.248292f,219.364564f,224.510845f,229.686789f,234.892058f,240.126328f,245.389280f,250.680604f,256.000000f,261.347174f,266.721841f,272.123723f,277.552547f,283.008049f,288.489971f,293.998060f,299.532071f,305.091761f,310.676898f,316.287249f,321.922592f,327.582707f,333.267377f,338.976394f,344.709550f,350.466646f,356.247482f,362.051866f,367.879608f,373.730522f,379.604427f,385.501143f,391.420496f,397.362314f,403.326427f,409.312672f,415.320884f,421.350905f,427.402579f,433.475750f,439.570269f,445.685987f,451.822757f,457.980436f,464.158883f,470.357960f,476.577530f,482.817459f,489.077615f,495.357868f,501.658090f,507.978156f,514.317941f,520.677324f,527.056184f,533.454404f,539.871867f,546.308458f,552.764065f,559.238575f,565.731879f,572.243870f,578.774440f,585.323483f,591.890898f,598.476581f,605.080431f,611.702349f,618.342238f,625.000000f,631.675540f,638.368763f,645.079578f }; -static float drmp3_L3_pow_43(int x) +static float ma_dr_mp3_L3_pow_43(int x) { float frac; int sign, mult = 256; if (x < 129) { - return g_drmp3_pow43[16 + x]; + return g_ma_dr_mp3_pow43[16 + x]; } if (x < 1024) { @@ -90756,11 +90407,11 @@ static float drmp3_L3_pow_43(int x) } sign = 2*x & 64; frac = (float)((x & 63) - sign) / ((x & ~63) + sign); - return g_drmp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; + return g_ma_dr_mp3_pow43[16 + ((x + sign) >> 6)]*(1.f + frac*((4.f/3) + frac*(2.f/9)))*mult; } -static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) +static void ma_dr_mp3_L3_huffman(float *dst, ma_dr_mp3_bs *bs, const ma_dr_mp3_L3_gr_info *gr_info, const float *scf, int layer3gr_limit) { - static const drmp3_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + static const ma_int16 tabs[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 785,785,785,785,784,784,784,784,513,513,513,513,513,513,513,513,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256, -255,1313,1298,1282,785,785,785,785,784,784,784,784,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,290,288, -255,1313,1298,1282,769,769,769,769,529,529,529,529,529,529,529,529,528,528,528,528,528,528,528,528,512,512,512,512,512,512,512,512,290,288, @@ -90776,61 +90427,61 @@ static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *g -250,-1179,-1579,-1836,-1996,-2124,-2253,-2333,-2413,-2477,-2542,-2574,-2607,-2622,-2655,1314,1313,1298,1312,1282,785,785,785,785,1040,1040,1025,1025,768,768,768,768,-766,-798,-830,-862,-895,-911,-927,-943,-959,-975,-991,-1007,-1023,-1039,-1055,-1070,1724,1647,-1103,-1119,1631,1767,1662,1738,1708,1723,-1135,1780,1615,1779,1599,1677,1646,1778,1583,-1151,1777,1567,1737,1692,1765,1722,1707,1630,1751,1661,1764,1614,1736,1676,1763,1750,1645,1598,1721,1691,1762,1706,1582,1761,1566,-1167,1749,1629,767,766,751,765,494,494,735,764,719,749,734,763,447,447,748,718,477,506,431,491,446,476,461,505,415,430,475,445,504,399,460,489,414,503,383,474,429,459,502,502,746,752,488,398,501,473,413,472,486,271,480,270,-1439,-1455,1357,-1471,-1487,-1503,1341,1325,-1519,1489,1463,1403,1309,-1535,1372,1448,1418,1476,1356,1462,1387,-1551,1475,1340,1447,1402,1386,-1567,1068,1068,1474,1461,455,380,468,440,395,425,410,454,364,467,466,464,453,269,409,448,268,432,1371,1473,1432,1417,1308,1460,1355,1446,1459,1431,1083,1083,1401,1416,1458,1445,1067,1067,1370,1457,1051,1051,1291,1430,1385,1444,1354,1415,1400,1443,1082,1082,1173,1113,1186,1066,1185,1050,-1967,1158,1128,1172,1097,1171,1081,-1983,1157,1112,416,266,375,400,1170,1142,1127,1065,793,793,1169,1033,1156,1096,1141,1111,1155,1080,1126,1140,898,898,808,808,897,897,792,792,1095,1152,1032,1125,1110,1139,1079,1124,882,807,838,881,853,791,-2319,867,368,263,822,852,837,866,806,865,-2399,851,352,262,534,534,821,836,594,594,549,549,593,593,533,533,848,773,579,579,564,578,548,563,276,276,577,576,306,291,516,560,305,305,275,259, -251,-892,-2058,-2620,-2828,-2957,-3023,-3039,1041,1041,1040,1040,769,769,769,769,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,256,-511,-527,-543,-559,1530,-575,-591,1528,1527,1407,1526,1391,1023,1023,1023,1023,1525,1375,1268,1268,1103,1103,1087,1087,1039,1039,1523,-604,815,815,815,815,510,495,509,479,508,463,507,447,431,505,415,399,-734,-782,1262,-815,1259,1244,-831,1258,1228,-847,-863,1196,-879,1253,987,987,748,-767,493,493,462,477,414,414,686,669,478,446,461,445,474,429,487,458,412,471,1266,1264,1009,1009,799,799,-1019,-1276,-1452,-1581,-1677,-1757,-1821,-1886,-1933,-1997,1257,1257,1483,1468,1512,1422,1497,1406,1467,1496,1421,1510,1134,1134,1225,1225,1466,1451,1374,1405,1252,1252,1358,1480,1164,1164,1251,1251,1238,1238,1389,1465,-1407,1054,1101,-1423,1207,-1439,830,830,1248,1038,1237,1117,1223,1148,1236,1208,411,426,395,410,379,269,1193,1222,1132,1235,1221,1116,976,976,1192,1162,1177,1220,1131,1191,963,963,-1647,961,780,-1663,558,558,994,993,437,408,393,407,829,978,813,797,947,-1743,721,721,377,392,844,950,828,890,706,706,812,859,796,960,948,843,934,874,571,571,-1919,690,555,689,421,346,539,539,944,779,918,873,932,842,903,888,570,570,931,917,674,674,-2575,1562,-2591,1609,-2607,1654,1322,1322,1441,1441,1696,1546,1683,1593,1669,1624,1426,1426,1321,1321,1639,1680,1425,1425,1305,1305,1545,1668,1608,1623,1667,1592,1638,1666,1320,1320,1652,1607,1409,1409,1304,1304,1288,1288,1664,1637,1395,1395,1335,1335,1622,1636,1394,1394,1319,1319,1606,1621,1392,1392,1137,1137,1137,1137,345,390,360,375,404,373,1047,-2751,-2767,-2783,1062,1121,1046,-2799,1077,-2815,1106,1061,789,789,1105,1104,263,355,310,340,325,354,352,262,339,324,1091,1076,1029,1090,1060,1075,833,833,788,788,1088,1028,818,818,803,803,561,561,531,531,816,771,546,546,289,274,288,258, -253,-317,-381,-446,-478,-509,1279,1279,-811,-1179,-1451,-1756,-1900,-2028,-2189,-2253,-2333,-2414,-2445,-2511,-2526,1313,1298,-2559,1041,1041,1040,1040,1025,1025,1024,1024,1022,1007,1021,991,1020,975,1019,959,687,687,1018,1017,671,671,655,655,1016,1015,639,639,758,758,623,623,757,607,756,591,755,575,754,559,543,543,1009,783,-575,-621,-685,-749,496,-590,750,749,734,748,974,989,1003,958,988,973,1002,942,987,957,972,1001,926,986,941,971,956,1000,910,985,925,999,894,970,-1071,-1087,-1102,1390,-1135,1436,1509,1451,1374,-1151,1405,1358,1480,1420,-1167,1507,1494,1389,1342,1465,1435,1450,1326,1505,1310,1493,1373,1479,1404,1492,1464,1419,428,443,472,397,736,526,464,464,486,457,442,471,484,482,1357,1449,1434,1478,1388,1491,1341,1490,1325,1489,1463,1403,1309,1477,1372,1448,1418,1433,1476,1356,1462,1387,-1439,1475,1340,1447,1402,1474,1324,1461,1371,1473,269,448,1432,1417,1308,1460,-1711,1459,-1727,1441,1099,1099,1446,1386,1431,1401,-1743,1289,1083,1083,1160,1160,1458,1445,1067,1067,1370,1457,1307,1430,1129,1129,1098,1098,268,432,267,416,266,400,-1887,1144,1187,1082,1173,1113,1186,1066,1050,1158,1128,1143,1172,1097,1171,1081,420,391,1157,1112,1170,1142,1127,1065,1169,1049,1156,1096,1141,1111,1155,1080,1126,1154,1064,1153,1140,1095,1048,-2159,1125,1110,1137,-2175,823,823,1139,1138,807,807,384,264,368,263,868,838,853,791,867,822,852,837,866,806,865,790,-2319,851,821,836,352,262,850,805,849,-2399,533,533,835,820,336,261,578,548,563,577,532,532,832,772,562,562,547,547,305,275,560,515,290,290,288,258 }; - static const drmp3_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205}; - static const drmp3_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; - static const drmp3_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; - static const drmp3_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; -#define DRMP3_PEEK_BITS(n) (bs_cache >> (32 - (n))) -#define DRMP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } -#define DRMP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (drmp3_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } -#define DRMP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) + static const ma_uint8 tab32[] = { 130,162,193,209,44,28,76,140,9,9,9,9,9,9,9,9,190,254,222,238,126,94,157,157,109,61,173,205}; + static const ma_uint8 tab33[] = { 252,236,220,204,188,172,156,140,124,108,92,76,60,44,28,12 }; + static const ma_int16 tabindex[2*16] = { 0,32,64,98,0,132,180,218,292,364,426,538,648,746,0,1126,1460,1460,1460,1460,1460,1460,1460,1460,1842,1842,1842,1842,1842,1842,1842,1842 }; + static const ma_uint8 g_linbits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,8,10,13,4,5,6,7,8,9,11,13 }; +#define MA_DR_MP3_PEEK_BITS(n) (bs_cache >> (32 - (n))) +#define MA_DR_MP3_FLUSH_BITS(n) { bs_cache <<= (n); bs_sh += (n); } +#define MA_DR_MP3_CHECK_BITS while (bs_sh >= 0) { bs_cache |= (ma_uint32)*bs_next_ptr++ << bs_sh; bs_sh -= 8; } +#define MA_DR_MP3_BSPOS ((bs_next_ptr - bs->buf)*8 - 24 + bs_sh) float one = 0.0f; int ireg = 0, big_val_cnt = gr_info->big_values; - const drmp3_uint8 *sfb = gr_info->sfbtab; - const drmp3_uint8 *bs_next_ptr = bs->buf + bs->pos/8; - drmp3_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); + const ma_uint8 *sfb = gr_info->sfbtab; + const ma_uint8 *bs_next_ptr = bs->buf + bs->pos/8; + ma_uint32 bs_cache = (((bs_next_ptr[0]*256u + bs_next_ptr[1])*256u + bs_next_ptr[2])*256u + bs_next_ptr[3]) << (bs->pos & 7); int pairs_to_decode, np, bs_sh = (bs->pos & 7) - 8; bs_next_ptr += 4; while (big_val_cnt > 0) { int tab_num = gr_info->table_select[ireg]; int sfb_cnt = gr_info->region_count[ireg++]; - const drmp3_int16 *codebook = tabs + tabindex[tab_num]; + const ma_int16 *codebook = tabs + tabindex[tab_num]; int linbits = g_linbits[tab_num]; if (linbits) { do { np = *sfb++ / 2; - pairs_to_decode = DRMP3_MIN(big_val_cnt, np); + pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np); one = *scf++; do { int j, w = 5; - int leaf = codebook[DRMP3_PEEK_BITS(w)]; + int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)]; while (leaf < 0) { - DRMP3_FLUSH_BITS(w); + MA_DR_MP3_FLUSH_BITS(w); w = leaf & 7; - leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; + leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)]; } - DRMP3_FLUSH_BITS(leaf >> 8); + MA_DR_MP3_FLUSH_BITS(leaf >> 8); for (j = 0; j < 2; j++, dst++, leaf >>= 4) { int lsb = leaf & 0x0F; if (lsb == 15) { - lsb += DRMP3_PEEK_BITS(linbits); - DRMP3_FLUSH_BITS(linbits); - DRMP3_CHECK_BITS; - *dst = one*drmp3_L3_pow_43(lsb)*((drmp3_int32)bs_cache < 0 ? -1: 1); + lsb += MA_DR_MP3_PEEK_BITS(linbits); + MA_DR_MP3_FLUSH_BITS(linbits); + MA_DR_MP3_CHECK_BITS; + *dst = one*ma_dr_mp3_L3_pow_43(lsb)*((ma_int32)bs_cache < 0 ? -1: 1); } else { - *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; } - DRMP3_FLUSH_BITS(lsb ? 1 : 0); + MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); } - DRMP3_CHECK_BITS; + MA_DR_MP3_CHECK_BITS; } while (--pairs_to_decode); } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); } else @@ -90838,68 +90489,68 @@ static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *g do { np = *sfb++ / 2; - pairs_to_decode = DRMP3_MIN(big_val_cnt, np); + pairs_to_decode = MA_DR_MP3_MIN(big_val_cnt, np); one = *scf++; do { int j, w = 5; - int leaf = codebook[DRMP3_PEEK_BITS(w)]; + int leaf = codebook[MA_DR_MP3_PEEK_BITS(w)]; while (leaf < 0) { - DRMP3_FLUSH_BITS(w); + MA_DR_MP3_FLUSH_BITS(w); w = leaf & 7; - leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)]; + leaf = codebook[MA_DR_MP3_PEEK_BITS(w) - (leaf >> 3)]; } - DRMP3_FLUSH_BITS(leaf >> 8); + MA_DR_MP3_FLUSH_BITS(leaf >> 8); for (j = 0; j < 2; j++, dst++, leaf >>= 4) { int lsb = leaf & 0x0F; - *dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; - DRMP3_FLUSH_BITS(lsb ? 1 : 0); + *dst = g_ma_dr_mp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one; + MA_DR_MP3_FLUSH_BITS(lsb ? 1 : 0); } - DRMP3_CHECK_BITS; + MA_DR_MP3_CHECK_BITS; } while (--pairs_to_decode); } while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0); } } for (np = 1 - big_val_cnt;; dst += 4) { - const drmp3_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; - int leaf = codebook_count1[DRMP3_PEEK_BITS(4)]; + const ma_uint8 *codebook_count1 = (gr_info->count1_table) ? tab33 : tab32; + int leaf = codebook_count1[MA_DR_MP3_PEEK_BITS(4)]; if (!(leaf & 8)) { leaf = codebook_count1[(leaf >> 3) + (bs_cache << 4 >> (32 - (leaf & 3)))]; } - DRMP3_FLUSH_BITS(leaf & 7); - if (DRMP3_BSPOS > layer3gr_limit) + MA_DR_MP3_FLUSH_BITS(leaf & 7); + if (MA_DR_MP3_BSPOS > layer3gr_limit) { break; } -#define DRMP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } -#define DRMP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((drmp3_int32)bs_cache < 0) ? -one : one; DRMP3_FLUSH_BITS(1) } - DRMP3_RELOAD_SCALEFACTOR; - DRMP3_DEQ_COUNT1(0); - DRMP3_DEQ_COUNT1(1); - DRMP3_RELOAD_SCALEFACTOR; - DRMP3_DEQ_COUNT1(2); - DRMP3_DEQ_COUNT1(3); - DRMP3_CHECK_BITS; +#define MA_DR_MP3_RELOAD_SCALEFACTOR if (!--np) { np = *sfb++/2; if (!np) break; one = *scf++; } +#define MA_DR_MP3_DEQ_COUNT1(s) if (leaf & (128 >> s)) { dst[s] = ((ma_int32)bs_cache < 0) ? -one : one; MA_DR_MP3_FLUSH_BITS(1) } + MA_DR_MP3_RELOAD_SCALEFACTOR; + MA_DR_MP3_DEQ_COUNT1(0); + MA_DR_MP3_DEQ_COUNT1(1); + MA_DR_MP3_RELOAD_SCALEFACTOR; + MA_DR_MP3_DEQ_COUNT1(2); + MA_DR_MP3_DEQ_COUNT1(3); + MA_DR_MP3_CHECK_BITS; } bs->pos = layer3gr_limit; } -static void drmp3_L3_midside_stereo(float *left, int n) +static void ma_dr_mp3_L3_midside_stereo(float *left, int n) { int i = 0; float *right = left + 576; -#if DRMP3_HAVE_SIMD - if (drmp3_have_simd()) +#if MA_DR_MP3_HAVE_SIMD + if (ma_dr_mp3_have_simd()) { for (; i < n - 3; i += 4) { - drmp3_f4 vl = DRMP3_VLD(left + i); - drmp3_f4 vr = DRMP3_VLD(right + i); - DRMP3_VSTORE(left + i, DRMP3_VADD(vl, vr)); - DRMP3_VSTORE(right + i, DRMP3_VSUB(vl, vr)); + ma_dr_mp3_f4 vl = MA_DR_MP3_VLD(left + i); + ma_dr_mp3_f4 vr = MA_DR_MP3_VLD(right + i); + MA_DR_MP3_VSTORE(left + i, MA_DR_MP3_VADD(vl, vr)); + MA_DR_MP3_VSTORE(right + i, MA_DR_MP3_VSUB(vl, vr)); } #ifdef __GNUC__ if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0) @@ -90915,7 +90566,7 @@ static void drmp3_L3_midside_stereo(float *left, int n) right[i] = a - b; } } -static void drmp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr) +static void ma_dr_mp3_L3_intensity_stereo_band(float *left, int n, float kl, float kr) { int i; for (i = 0; i < n; i++) @@ -90924,7 +90575,7 @@ static void drmp3_L3_intensity_stereo_band(float *left, int n, float kl, float k left[i] = left[i]*kl; } } -static void drmp3_L3_stereo_top_band(const float *right, const drmp3_uint8 *sfb, int nbands, int max_band[3]) +static void ma_dr_mp3_L3_stereo_top_band(const float *right, const ma_uint8 *sfb, int nbands, int max_band[3]) { int i, k; max_band[0] = max_band[1] = max_band[2] = -1; @@ -90941,57 +90592,57 @@ static void drmp3_L3_stereo_top_band(const float *right, const drmp3_uint8 *sfb, right += sfb[i]; } } -static void drmp3_L3_stereo_process(float *left, const drmp3_uint8 *ist_pos, const drmp3_uint8 *sfb, const drmp3_uint8 *hdr, int max_band[3], int mpeg2_sh) +static void ma_dr_mp3_L3_stereo_process(float *left, const ma_uint8 *ist_pos, const ma_uint8 *sfb, const ma_uint8 *hdr, int max_band[3], int mpeg2_sh) { static const float g_pan[7*2] = { 0,1,0.21132487f,0.78867513f,0.36602540f,0.63397460f,0.5f,0.5f,0.63397460f,0.36602540f,0.78867513f,0.21132487f,1,0 }; - unsigned i, max_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 7 : 64; + unsigned i, max_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 7 : 64; for (i = 0; sfb[i]; i++) { unsigned ipos = ist_pos[i]; if ((int)i > max_band[i % 3] && ipos < max_pos) { - float kl, kr, s = DRMP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; - if (DRMP3_HDR_TEST_MPEG1(hdr)) + float kl, kr, s = MA_DR_MP3_HDR_TEST_MS_STEREO(hdr) ? 1.41421356f : 1; + if (MA_DR_MP3_HDR_TEST_MPEG1(hdr)) { kl = g_pan[2*ipos]; kr = g_pan[2*ipos + 1]; } else { kl = 1; - kr = drmp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); + kr = ma_dr_mp3_L3_ldexp_q2(1, (ipos + 1) >> 1 << mpeg2_sh); if (ipos & 1) { kl = kr; kr = 1; } } - drmp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); - } else if (DRMP3_HDR_TEST_MS_STEREO(hdr)) + ma_dr_mp3_L3_intensity_stereo_band(left, sfb[i], kl*s, kr*s); + } else if (MA_DR_MP3_HDR_TEST_MS_STEREO(hdr)) { - drmp3_L3_midside_stereo(left, sfb[i]); + ma_dr_mp3_L3_midside_stereo(left, sfb[i]); } left += sfb[i]; } } -static void drmp3_L3_intensity_stereo(float *left, drmp3_uint8 *ist_pos, const drmp3_L3_gr_info *gr, const drmp3_uint8 *hdr) +static void ma_dr_mp3_L3_intensity_stereo(float *left, ma_uint8 *ist_pos, const ma_dr_mp3_L3_gr_info *gr, const ma_uint8 *hdr) { int max_band[3], n_sfb = gr->n_long_sfb + gr->n_short_sfb; int i, max_blocks = gr->n_short_sfb ? 3 : 1; - drmp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); + ma_dr_mp3_L3_stereo_top_band(left + 576, gr->sfbtab, n_sfb, max_band); if (gr->n_long_sfb) { - max_band[0] = max_band[1] = max_band[2] = DRMP3_MAX(DRMP3_MAX(max_band[0], max_band[1]), max_band[2]); + max_band[0] = max_band[1] = max_band[2] = MA_DR_MP3_MAX(MA_DR_MP3_MAX(max_band[0], max_band[1]), max_band[2]); } for (i = 0; i < max_blocks; i++) { - int default_pos = DRMP3_HDR_TEST_MPEG1(hdr) ? 3 : 0; + int default_pos = MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 3 : 0; int itop = n_sfb - max_blocks + i; int prev = itop - max_blocks; - ist_pos[itop] = (drmp3_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); + ist_pos[itop] = (ma_uint8)(max_band[i] >= prev ? default_pos : ist_pos[prev]); } - drmp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); + ma_dr_mp3_L3_stereo_process(left, ist_pos, gr->sfbtab, hdr, max_band, gr[1].scalefac_compress & 1); } -static void drmp3_L3_reorder(float *grbuf, float *scratch, const drmp3_uint8 *sfb) +static void ma_dr_mp3_L3_reorder(float *grbuf, float *scratch, const ma_uint8 *sfb) { int i, len; float *src = grbuf, *dst = scratch; @@ -91004,9 +90655,9 @@ static void drmp3_L3_reorder(float *grbuf, float *scratch, const drmp3_uint8 *sf *dst++ = src[2*len]; } } - DRMP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float)); + MA_DR_MP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float)); } -static void drmp3_L3_antialias(float *grbuf, int nbands) +static void ma_dr_mp3_L3_antialias(float *grbuf, int nbands) { static const float g_aa[2][8] = { {0.85749293f,0.88174200f,0.94962865f,0.98331459f,0.99551782f,0.99916056f,0.99989920f,0.99999316f}, @@ -91015,20 +90666,20 @@ static void drmp3_L3_antialias(float *grbuf, int nbands) for (; nbands > 0; nbands--, grbuf += 18) { int i = 0; -#if DRMP3_HAVE_SIMD - if (drmp3_have_simd()) for (; i < 8; i += 4) +#if MA_DR_MP3_HAVE_SIMD + if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4) { - drmp3_f4 vu = DRMP3_VLD(grbuf + 18 + i); - drmp3_f4 vd = DRMP3_VLD(grbuf + 14 - i); - drmp3_f4 vc0 = DRMP3_VLD(g_aa[0] + i); - drmp3_f4 vc1 = DRMP3_VLD(g_aa[1] + i); - vd = DRMP3_VREV(vd); - DRMP3_VSTORE(grbuf + 18 + i, DRMP3_VSUB(DRMP3_VMUL(vu, vc0), DRMP3_VMUL(vd, vc1))); - vd = DRMP3_VADD(DRMP3_VMUL(vu, vc1), DRMP3_VMUL(vd, vc0)); - DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vd)); + ma_dr_mp3_f4 vu = MA_DR_MP3_VLD(grbuf + 18 + i); + ma_dr_mp3_f4 vd = MA_DR_MP3_VLD(grbuf + 14 - i); + ma_dr_mp3_f4 vc0 = MA_DR_MP3_VLD(g_aa[0] + i); + ma_dr_mp3_f4 vc1 = MA_DR_MP3_VLD(g_aa[1] + i); + vd = MA_DR_MP3_VREV(vd); + MA_DR_MP3_VSTORE(grbuf + 18 + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vu, vc0), MA_DR_MP3_VMUL(vd, vc1))); + vd = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vu, vc1), MA_DR_MP3_VMUL(vd, vc0)); + MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vd)); } #endif -#ifndef DR_MP3_ONLY_SIMD +#ifndef MA_DR_MP3_ONLY_SIMD for(; i < 8; i++) { float u = grbuf[18 + i]; @@ -91039,7 +90690,7 @@ static void drmp3_L3_antialias(float *grbuf, int nbands) #endif } } -static void drmp3_L3_dct3_9(float *y) +static void ma_dr_mp3_L3_dct3_9(float *y) { float s0, s1, s2, s3, s4, s5, s6, s7, s8, t0, t2, t4; s0 = y[0]; s2 = y[2]; s4 = y[4]; s6 = y[6]; s8 = y[8]; @@ -91072,7 +90723,7 @@ static void drmp3_L3_dct3_9(float *y) y[7] = s2 - s1; y[8] = s4 + s7; } -static void drmp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) +static void ma_dr_mp3_L3_imdct36(float *grbuf, float *overlap, const float *window, int nbands) { int i, j; static const float g_twid9[18] = { @@ -91090,28 +90741,28 @@ static void drmp3_L3_imdct36(float *grbuf, float *overlap, const float *window, si[7 - 2*i] = grbuf[4*i + 4] - grbuf[4*i + 3]; co[2 + 2*i] = -(grbuf[4*i + 3] + grbuf[4*i + 4]); } - drmp3_L3_dct3_9(co); - drmp3_L3_dct3_9(si); + ma_dr_mp3_L3_dct3_9(co); + ma_dr_mp3_L3_dct3_9(si); si[1] = -si[1]; si[3] = -si[3]; si[5] = -si[5]; si[7] = -si[7]; i = 0; -#if DRMP3_HAVE_SIMD - if (drmp3_have_simd()) for (; i < 8; i += 4) +#if MA_DR_MP3_HAVE_SIMD + if (ma_dr_mp3_have_simd()) for (; i < 8; i += 4) { - drmp3_f4 vovl = DRMP3_VLD(overlap + i); - drmp3_f4 vc = DRMP3_VLD(co + i); - drmp3_f4 vs = DRMP3_VLD(si + i); - drmp3_f4 vr0 = DRMP3_VLD(g_twid9 + i); - drmp3_f4 vr1 = DRMP3_VLD(g_twid9 + 9 + i); - drmp3_f4 vw0 = DRMP3_VLD(window + i); - drmp3_f4 vw1 = DRMP3_VLD(window + 9 + i); - drmp3_f4 vsum = DRMP3_VADD(DRMP3_VMUL(vc, vr1), DRMP3_VMUL(vs, vr0)); - DRMP3_VSTORE(overlap + i, DRMP3_VSUB(DRMP3_VMUL(vc, vr0), DRMP3_VMUL(vs, vr1))); - DRMP3_VSTORE(grbuf + i, DRMP3_VSUB(DRMP3_VMUL(vovl, vw0), DRMP3_VMUL(vsum, vw1))); - vsum = DRMP3_VADD(DRMP3_VMUL(vovl, vw1), DRMP3_VMUL(vsum, vw0)); - DRMP3_VSTORE(grbuf + 14 - i, DRMP3_VREV(vsum)); + ma_dr_mp3_f4 vovl = MA_DR_MP3_VLD(overlap + i); + ma_dr_mp3_f4 vc = MA_DR_MP3_VLD(co + i); + ma_dr_mp3_f4 vs = MA_DR_MP3_VLD(si + i); + ma_dr_mp3_f4 vr0 = MA_DR_MP3_VLD(g_twid9 + i); + ma_dr_mp3_f4 vr1 = MA_DR_MP3_VLD(g_twid9 + 9 + i); + ma_dr_mp3_f4 vw0 = MA_DR_MP3_VLD(window + i); + ma_dr_mp3_f4 vw1 = MA_DR_MP3_VLD(window + 9 + i); + ma_dr_mp3_f4 vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vc, vr1), MA_DR_MP3_VMUL(vs, vr0)); + MA_DR_MP3_VSTORE(overlap + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vc, vr0), MA_DR_MP3_VMUL(vs, vr1))); + MA_DR_MP3_VSTORE(grbuf + i, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vovl, vw0), MA_DR_MP3_VMUL(vsum, vw1))); + vsum = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vovl, vw1), MA_DR_MP3_VMUL(vsum, vw0)); + MA_DR_MP3_VSTORE(grbuf + 14 - i, MA_DR_MP3_VREV(vsum)); } #endif for (; i < 9; i++) @@ -91124,7 +90775,7 @@ static void drmp3_L3_imdct36(float *grbuf, float *overlap, const float *window, } } } -static void drmp3_L3_idct3(float x0, float x1, float x2, float *dst) +static void ma_dr_mp3_L3_idct3(float x0, float x1, float x2, float *dst) { float m1 = x1*0.86602540f; float a1 = x0 - x2*0.5f; @@ -91132,13 +90783,13 @@ static void drmp3_L3_idct3(float x0, float x1, float x2, float *dst) dst[0] = a1 + m1; dst[2] = a1 - m1; } -static void drmp3_L3_imdct12(float *x, float *dst, float *overlap) +static void ma_dr_mp3_L3_imdct12(float *x, float *dst, float *overlap) { static const float g_twid3[6] = { 0.79335334f,0.92387953f,0.99144486f, 0.60876143f,0.38268343f,0.13052619f }; float co[3], si[3]; int i; - drmp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); - drmp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); + ma_dr_mp3_L3_idct3(-x[0], x[6] + x[3], x[12] + x[9], co); + ma_dr_mp3_L3_idct3(x[15], x[12] - x[9], x[6] - x[3], si); si[1] = -si[1]; for (i = 0; i < 3; i++) { @@ -91149,26 +90800,26 @@ static void drmp3_L3_imdct12(float *x, float *dst, float *overlap) dst[5 - i] = ovl*g_twid3[5 - i] + sum*g_twid3[2 - i]; } } -static void drmp3_L3_imdct_short(float *grbuf, float *overlap, int nbands) +static void ma_dr_mp3_L3_imdct_short(float *grbuf, float *overlap, int nbands) { for (;nbands > 0; nbands--, overlap += 9, grbuf += 18) { float tmp[18]; - DRMP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp)); - DRMP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float)); - drmp3_L3_imdct12(tmp, grbuf + 6, overlap + 6); - drmp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); - drmp3_L3_imdct12(tmp + 2, overlap, overlap + 6); + MA_DR_MP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp)); + MA_DR_MP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float)); + ma_dr_mp3_L3_imdct12(tmp, grbuf + 6, overlap + 6); + ma_dr_mp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6); + ma_dr_mp3_L3_imdct12(tmp + 2, overlap, overlap + 6); } } -static void drmp3_L3_change_sign(float *grbuf) +static void ma_dr_mp3_L3_change_sign(float *grbuf) { int b, i; for (b = 0, grbuf += 18; b < 32; b += 2, grbuf += 36) for (i = 1; i < 18; i += 2) grbuf[i] = -grbuf[i]; } -static void drmp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) +static void ma_dr_mp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, unsigned n_long_bands) { static const float g_mdct_window[2][18] = { { 0.99904822f,0.99144486f,0.97629601f,0.95371695f,0.92387953f,0.88701083f,0.84339145f,0.79335334f,0.73727734f,0.04361938f,0.13052619f,0.21643961f,0.30070580f,0.38268343f,0.46174861f,0.53729961f,0.60876143f,0.67559021f }, @@ -91176,159 +90827,159 @@ static void drmp3_L3_imdct_gr(float *grbuf, float *overlap, unsigned block_type, }; if (n_long_bands) { - drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); + ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[0], n_long_bands); grbuf += 18*n_long_bands; overlap += 9*n_long_bands; } - if (block_type == DRMP3_SHORT_BLOCK_TYPE) - drmp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands); + if (block_type == MA_DR_MP3_SHORT_BLOCK_TYPE) + ma_dr_mp3_L3_imdct_short(grbuf, overlap, 32 - n_long_bands); else - drmp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == DRMP3_STOP_BLOCK_TYPE], 32 - n_long_bands); + ma_dr_mp3_L3_imdct36(grbuf, overlap, g_mdct_window[block_type == MA_DR_MP3_STOP_BLOCK_TYPE], 32 - n_long_bands); } -static void drmp3_L3_save_reservoir(drmp3dec *h, drmp3dec_scratch *s) +static void ma_dr_mp3_L3_save_reservoir(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s) { int pos = (s->bs.pos + 7)/8u; int remains = s->bs.limit/8u - pos; - if (remains > DRMP3_MAX_BITRESERVOIR_BYTES) + if (remains > MA_DR_MP3_MAX_BITRESERVOIR_BYTES) { - pos += remains - DRMP3_MAX_BITRESERVOIR_BYTES; - remains = DRMP3_MAX_BITRESERVOIR_BYTES; + pos += remains - MA_DR_MP3_MAX_BITRESERVOIR_BYTES; + remains = MA_DR_MP3_MAX_BITRESERVOIR_BYTES; } if (remains > 0) { - DRMP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains); + MA_DR_MP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains); } h->reserv = remains; } -static int drmp3_L3_restore_reservoir(drmp3dec *h, drmp3_bs *bs, drmp3dec_scratch *s, int main_data_begin) +static int ma_dr_mp3_L3_restore_reservoir(ma_dr_mp3dec *h, ma_dr_mp3_bs *bs, ma_dr_mp3dec_scratch *s, int main_data_begin) { int frame_bytes = (bs->limit - bs->pos)/8; - int bytes_have = DRMP3_MIN(h->reserv, main_data_begin); - DRMP3_COPY_MEMORY(s->maindata, h->reserv_buf + DRMP3_MAX(0, h->reserv - main_data_begin), DRMP3_MIN(h->reserv, main_data_begin)); - DRMP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); - drmp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); + int bytes_have = MA_DR_MP3_MIN(h->reserv, main_data_begin); + MA_DR_MP3_COPY_MEMORY(s->maindata, h->reserv_buf + MA_DR_MP3_MAX(0, h->reserv - main_data_begin), MA_DR_MP3_MIN(h->reserv, main_data_begin)); + MA_DR_MP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes); + ma_dr_mp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes); return h->reserv >= main_data_begin; } -static void drmp3_L3_decode(drmp3dec *h, drmp3dec_scratch *s, drmp3_L3_gr_info *gr_info, int nch) +static void ma_dr_mp3_L3_decode(ma_dr_mp3dec *h, ma_dr_mp3dec_scratch *s, ma_dr_mp3_L3_gr_info *gr_info, int nch) { int ch; for (ch = 0; ch < nch; ch++) { int layer3gr_limit = s->bs.pos + gr_info[ch].part_23_length; - drmp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); - drmp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); + ma_dr_mp3_L3_decode_scalefactors(h->header, s->ist_pos[ch], &s->bs, gr_info + ch, s->scf, ch); + ma_dr_mp3_L3_huffman(s->grbuf[ch], &s->bs, gr_info + ch, s->scf, layer3gr_limit); } - if (DRMP3_HDR_TEST_I_STEREO(h->header)) + if (MA_DR_MP3_HDR_TEST_I_STEREO(h->header)) { - drmp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); - } else if (DRMP3_HDR_IS_MS_STEREO(h->header)) + ma_dr_mp3_L3_intensity_stereo(s->grbuf[0], s->ist_pos[1], gr_info, h->header); + } else if (MA_DR_MP3_HDR_IS_MS_STEREO(h->header)) { - drmp3_L3_midside_stereo(s->grbuf[0], 576); + ma_dr_mp3_L3_midside_stereo(s->grbuf[0], 576); } for (ch = 0; ch < nch; ch++, gr_info++) { int aa_bands = 31; - int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(DRMP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2); + int n_long_bands = (gr_info->mixed_block_flag ? 2 : 0) << (int)(MA_DR_MP3_HDR_GET_MY_SAMPLE_RATE(h->header) == 2); if (gr_info->n_short_sfb) { aa_bands = n_long_bands - 1; - drmp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); + ma_dr_mp3_L3_reorder(s->grbuf[ch] + n_long_bands*18, s->syn[0], gr_info->sfbtab + gr_info->n_long_sfb); } - drmp3_L3_antialias(s->grbuf[ch], aa_bands); - drmp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); - drmp3_L3_change_sign(s->grbuf[ch]); + ma_dr_mp3_L3_antialias(s->grbuf[ch], aa_bands); + ma_dr_mp3_L3_imdct_gr(s->grbuf[ch], h->mdct_overlap[ch], gr_info->block_type, n_long_bands); + ma_dr_mp3_L3_change_sign(s->grbuf[ch]); } } -static void drmp3d_DCT_II(float *grbuf, int n) +static void ma_dr_mp3d_DCT_II(float *grbuf, int n) { static const float g_sec[24] = { 10.19000816f,0.50060302f,0.50241929f,3.40760851f,0.50547093f,0.52249861f,2.05778098f,0.51544732f,0.56694406f,1.48416460f,0.53104258f,0.64682180f,1.16943991f,0.55310392f,0.78815460f,0.97256821f,0.58293498f,1.06067765f,0.83934963f,0.62250412f,1.72244716f,0.74453628f,0.67480832f,5.10114861f }; int i, k = 0; -#if DRMP3_HAVE_SIMD - if (drmp3_have_simd()) for (; k < n; k += 4) +#if MA_DR_MP3_HAVE_SIMD + if (ma_dr_mp3_have_simd()) for (; k < n; k += 4) { - drmp3_f4 t[4][8], *x; + ma_dr_mp3_f4 t[4][8], *x; float *y = grbuf + k; for (x = t[0], i = 0; i < 8; i++, x++) { - drmp3_f4 x0 = DRMP3_VLD(&y[i*18]); - drmp3_f4 x1 = DRMP3_VLD(&y[(15 - i)*18]); - drmp3_f4 x2 = DRMP3_VLD(&y[(16 + i)*18]); - drmp3_f4 x3 = DRMP3_VLD(&y[(31 - i)*18]); - drmp3_f4 t0 = DRMP3_VADD(x0, x3); - drmp3_f4 t1 = DRMP3_VADD(x1, x2); - drmp3_f4 t2 = DRMP3_VMUL_S(DRMP3_VSUB(x1, x2), g_sec[3*i + 0]); - drmp3_f4 t3 = DRMP3_VMUL_S(DRMP3_VSUB(x0, x3), g_sec[3*i + 1]); - x[0] = DRMP3_VADD(t0, t1); - x[8] = DRMP3_VMUL_S(DRMP3_VSUB(t0, t1), g_sec[3*i + 2]); - x[16] = DRMP3_VADD(t3, t2); - x[24] = DRMP3_VMUL_S(DRMP3_VSUB(t3, t2), g_sec[3*i + 2]); + ma_dr_mp3_f4 x0 = MA_DR_MP3_VLD(&y[i*18]); + ma_dr_mp3_f4 x1 = MA_DR_MP3_VLD(&y[(15 - i)*18]); + ma_dr_mp3_f4 x2 = MA_DR_MP3_VLD(&y[(16 + i)*18]); + ma_dr_mp3_f4 x3 = MA_DR_MP3_VLD(&y[(31 - i)*18]); + ma_dr_mp3_f4 t0 = MA_DR_MP3_VADD(x0, x3); + ma_dr_mp3_f4 t1 = MA_DR_MP3_VADD(x1, x2); + ma_dr_mp3_f4 t2 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x1, x2), g_sec[3*i + 0]); + ma_dr_mp3_f4 t3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x3), g_sec[3*i + 1]); + x[0] = MA_DR_MP3_VADD(t0, t1); + x[8] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t0, t1), g_sec[3*i + 2]); + x[16] = MA_DR_MP3_VADD(t3, t2); + x[24] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(t3, t2), g_sec[3*i + 2]); } for (x = t[0], i = 0; i < 4; i++, x += 8) { - drmp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; - xt = DRMP3_VSUB(x0, x7); x0 = DRMP3_VADD(x0, x7); - x7 = DRMP3_VSUB(x1, x6); x1 = DRMP3_VADD(x1, x6); - x6 = DRMP3_VSUB(x2, x5); x2 = DRMP3_VADD(x2, x5); - x5 = DRMP3_VSUB(x3, x4); x3 = DRMP3_VADD(x3, x4); - x4 = DRMP3_VSUB(x0, x3); x0 = DRMP3_VADD(x0, x3); - x3 = DRMP3_VSUB(x1, x2); x1 = DRMP3_VADD(x1, x2); - x[0] = DRMP3_VADD(x0, x1); - x[4] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x1), 0.70710677f); - x5 = DRMP3_VADD(x5, x6); - x6 = DRMP3_VMUL_S(DRMP3_VADD(x6, x7), 0.70710677f); - x7 = DRMP3_VADD(x7, xt); - x3 = DRMP3_VMUL_S(DRMP3_VADD(x3, x4), 0.70710677f); - x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); - x7 = DRMP3_VADD(x7, DRMP3_VMUL_S(x5, 0.382683432f)); - x5 = DRMP3_VSUB(x5, DRMP3_VMUL_S(x7, 0.198912367f)); - x0 = DRMP3_VSUB(xt, x6); xt = DRMP3_VADD(xt, x6); - x[1] = DRMP3_VMUL_S(DRMP3_VADD(xt, x7), 0.50979561f); - x[2] = DRMP3_VMUL_S(DRMP3_VADD(x4, x3), 0.54119611f); - x[3] = DRMP3_VMUL_S(DRMP3_VSUB(x0, x5), 0.60134488f); - x[5] = DRMP3_VMUL_S(DRMP3_VADD(x0, x5), 0.89997619f); - x[6] = DRMP3_VMUL_S(DRMP3_VSUB(x4, x3), 1.30656302f); - x[7] = DRMP3_VMUL_S(DRMP3_VSUB(xt, x7), 2.56291556f); + ma_dr_mp3_f4 x0 = x[0], x1 = x[1], x2 = x[2], x3 = x[3], x4 = x[4], x5 = x[5], x6 = x[6], x7 = x[7], xt; + xt = MA_DR_MP3_VSUB(x0, x7); x0 = MA_DR_MP3_VADD(x0, x7); + x7 = MA_DR_MP3_VSUB(x1, x6); x1 = MA_DR_MP3_VADD(x1, x6); + x6 = MA_DR_MP3_VSUB(x2, x5); x2 = MA_DR_MP3_VADD(x2, x5); + x5 = MA_DR_MP3_VSUB(x3, x4); x3 = MA_DR_MP3_VADD(x3, x4); + x4 = MA_DR_MP3_VSUB(x0, x3); x0 = MA_DR_MP3_VADD(x0, x3); + x3 = MA_DR_MP3_VSUB(x1, x2); x1 = MA_DR_MP3_VADD(x1, x2); + x[0] = MA_DR_MP3_VADD(x0, x1); + x[4] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x1), 0.70710677f); + x5 = MA_DR_MP3_VADD(x5, x6); + x6 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x6, x7), 0.70710677f); + x7 = MA_DR_MP3_VADD(x7, xt); + x3 = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x3, x4), 0.70710677f); + x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f)); + x7 = MA_DR_MP3_VADD(x7, MA_DR_MP3_VMUL_S(x5, 0.382683432f)); + x5 = MA_DR_MP3_VSUB(x5, MA_DR_MP3_VMUL_S(x7, 0.198912367f)); + x0 = MA_DR_MP3_VSUB(xt, x6); xt = MA_DR_MP3_VADD(xt, x6); + x[1] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(xt, x7), 0.50979561f); + x[2] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x4, x3), 0.54119611f); + x[3] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x0, x5), 0.60134488f); + x[5] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VADD(x0, x5), 0.89997619f); + x[6] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(x4, x3), 1.30656302f); + x[7] = MA_DR_MP3_VMUL_S(MA_DR_MP3_VSUB(xt, x7), 2.56291556f); } if (k > n - 3) { -#if DRMP3_HAVE_SSE -#define DRMP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) +#if MA_DR_MP3_HAVE_SSE +#define MA_DR_MP3_VSAVE2(i, v) _mm_storel_pi((__m64 *)(void*)&y[i*18], v) #else -#define DRMP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v)) +#define MA_DR_MP3_VSAVE2(i, v) vst1_f32((float32_t *)&y[(i)*18], vget_low_f32(v)) #endif for (i = 0; i < 7; i++, y += 4*18) { - drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); - DRMP3_VSAVE2(0, t[0][i]); - DRMP3_VSAVE2(1, DRMP3_VADD(t[2][i], s)); - DRMP3_VSAVE2(2, DRMP3_VADD(t[1][i], t[1][i + 1])); - DRMP3_VSAVE2(3, DRMP3_VADD(t[2][1 + i], s)); + ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]); + MA_DR_MP3_VSAVE2(0, t[0][i]); + MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][i], s)); + MA_DR_MP3_VSAVE2(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1])); + MA_DR_MP3_VSAVE2(3, MA_DR_MP3_VADD(t[2][1 + i], s)); } - DRMP3_VSAVE2(0, t[0][7]); - DRMP3_VSAVE2(1, DRMP3_VADD(t[2][7], t[3][7])); - DRMP3_VSAVE2(2, t[1][7]); - DRMP3_VSAVE2(3, t[3][7]); + MA_DR_MP3_VSAVE2(0, t[0][7]); + MA_DR_MP3_VSAVE2(1, MA_DR_MP3_VADD(t[2][7], t[3][7])); + MA_DR_MP3_VSAVE2(2, t[1][7]); + MA_DR_MP3_VSAVE2(3, t[3][7]); } else { -#define DRMP3_VSAVE4(i, v) DRMP3_VSTORE(&y[(i)*18], v) +#define MA_DR_MP3_VSAVE4(i, v) MA_DR_MP3_VSTORE(&y[(i)*18], v) for (i = 0; i < 7; i++, y += 4*18) { - drmp3_f4 s = DRMP3_VADD(t[3][i], t[3][i + 1]); - DRMP3_VSAVE4(0, t[0][i]); - DRMP3_VSAVE4(1, DRMP3_VADD(t[2][i], s)); - DRMP3_VSAVE4(2, DRMP3_VADD(t[1][i], t[1][i + 1])); - DRMP3_VSAVE4(3, DRMP3_VADD(t[2][1 + i], s)); + ma_dr_mp3_f4 s = MA_DR_MP3_VADD(t[3][i], t[3][i + 1]); + MA_DR_MP3_VSAVE4(0, t[0][i]); + MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][i], s)); + MA_DR_MP3_VSAVE4(2, MA_DR_MP3_VADD(t[1][i], t[1][i + 1])); + MA_DR_MP3_VSAVE4(3, MA_DR_MP3_VADD(t[2][1 + i], s)); } - DRMP3_VSAVE4(0, t[0][7]); - DRMP3_VSAVE4(1, DRMP3_VADD(t[2][7], t[3][7])); - DRMP3_VSAVE4(2, t[1][7]); - DRMP3_VSAVE4(3, t[3][7]); + MA_DR_MP3_VSAVE4(0, t[0][7]); + MA_DR_MP3_VSAVE4(1, MA_DR_MP3_VADD(t[2][7], t[3][7])); + MA_DR_MP3_VSAVE4(2, t[1][7]); + MA_DR_MP3_VSAVE4(3, t[3][7]); } } else #endif -#ifdef DR_MP3_ONLY_SIMD +#ifdef MA_DR_MP3_ONLY_SIMD {} #else for (; k < n; k++) @@ -91389,31 +91040,31 @@ static void drmp3d_DCT_II(float *grbuf, int n) } #endif } -#ifndef DR_MP3_FLOAT_OUTPUT -typedef drmp3_int16 drmp3d_sample_t; -static drmp3_int16 drmp3d_scale_pcm(float sample) +#ifndef MA_DR_MP3_FLOAT_OUTPUT +typedef ma_int16 ma_dr_mp3d_sample_t; +static ma_int16 ma_dr_mp3d_scale_pcm(float sample) { - drmp3_int16 s; -#if DRMP3_HAVE_ARMV6 - drmp3_int32 s32 = (drmp3_int32)(sample + .5f); + ma_int16 s; +#if MA_DR_MP3_HAVE_ARMV6 + ma_int32 s32 = (ma_int32)(sample + .5f); s32 -= (s32 < 0); - s = (drmp3_int16)drmp3_clip_int16_arm(s32); + s = (ma_int16)ma_dr_mp3_clip_int16_arm(s32); #else - if (sample >= 32766.5) return (drmp3_int16) 32767; - if (sample <= -32767.5) return (drmp3_int16)-32768; - s = (drmp3_int16)(sample + .5f); + if (sample >= 32766.5) return (ma_int16) 32767; + if (sample <= -32767.5) return (ma_int16)-32768; + s = (ma_int16)(sample + .5f); s -= (s < 0); #endif return s; } #else -typedef float drmp3d_sample_t; -static float drmp3d_scale_pcm(float sample) +typedef float ma_dr_mp3d_sample_t; +static float ma_dr_mp3d_scale_pcm(float sample) { return sample*(1.f/32768.f); } #endif -static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z) +static void ma_dr_mp3d_synth_pair(ma_dr_mp3d_sample_t *pcm, int nch, const float *z) { float a; a = (z[14*64] - z[ 0]) * 29; @@ -91424,7 +91075,7 @@ static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z) a += (z[ 5*64] + z[ 9*64]) * 6574; a += (z[ 8*64] - z[ 6*64]) * 37489; a += z[ 7*64] * 75038; - pcm[0] = drmp3d_scale_pcm(a); + pcm[0] = ma_dr_mp3d_scale_pcm(a); z += 2; a = z[14*64] * 104; a += z[12*64] * 1567; @@ -91434,13 +91085,13 @@ static void drmp3d_synth_pair(drmp3d_sample_t *pcm, int nch, const float *z) a += z[ 4*64] * -45; a += z[ 2*64] * 146; a += z[ 0*64] * -5; - pcm[16*nch] = drmp3d_scale_pcm(a); + pcm[16*nch] = ma_dr_mp3d_scale_pcm(a); } -static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) +static void ma_dr_mp3d_synth(float *xl, ma_dr_mp3d_sample_t *dstl, int nch, float *lins) { int i; float *xr = xl + 576*(nch - 1); - drmp3d_sample_t *dstr = dstl + (nch - 1); + ma_dr_mp3d_sample_t *dstr = dstl + (nch - 1); static const float g_win[] = { -1,26,-31,208,218,401,-519,2063,2000,4788,-5517,7134,5959,35640,-39336,74992, -1,24,-35,202,222,347,-581,2080,1952,4425,-5879,7640,5288,33791,-41176,74856, @@ -91468,18 +91119,18 @@ static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) zlin[4*31 + 1] = xr[1 + 18*16]; zlin[4*31 + 2] = xl[1]; zlin[4*31 + 3] = xr[1]; - drmp3d_synth_pair(dstr, nch, lins + 4*15 + 1); - drmp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); - drmp3d_synth_pair(dstl, nch, lins + 4*15); - drmp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); -#if DRMP3_HAVE_SIMD - if (drmp3_have_simd()) for (i = 14; i >= 0; i--) + ma_dr_mp3d_synth_pair(dstr, nch, lins + 4*15 + 1); + ma_dr_mp3d_synth_pair(dstr + 32*nch, nch, lins + 4*15 + 64 + 1); + ma_dr_mp3d_synth_pair(dstl, nch, lins + 4*15); + ma_dr_mp3d_synth_pair(dstl + 32*nch, nch, lins + 4*15 + 64); +#if MA_DR_MP3_HAVE_SIMD + if (ma_dr_mp3_have_simd()) for (i = 14; i >= 0; i--) { -#define DRMP3_VLOAD(k) drmp3_f4 w0 = DRMP3_VSET(*w++); drmp3_f4 w1 = DRMP3_VSET(*w++); drmp3_f4 vz = DRMP3_VLD(&zlin[4*i - 64*k]); drmp3_f4 vy = DRMP3_VLD(&zlin[4*i - 64*(15 - k)]); -#define DRMP3_V0(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0)) ; a = DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1)); } -#define DRMP3_V1(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vz, w0), DRMP3_VMUL(vy, w1))); } -#define DRMP3_V2(k) { DRMP3_VLOAD(k) b = DRMP3_VADD(b, DRMP3_VADD(DRMP3_VMUL(vz, w1), DRMP3_VMUL(vy, w0))); a = DRMP3_VADD(a, DRMP3_VSUB(DRMP3_VMUL(vy, w1), DRMP3_VMUL(vz, w0))); } - drmp3_f4 a, b; +#define MA_DR_MP3_VLOAD(k) ma_dr_mp3_f4 w0 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 w1 = MA_DR_MP3_VSET(*w++); ma_dr_mp3_f4 vz = MA_DR_MP3_VLD(&zlin[4*i - 64*k]); ma_dr_mp3_f4 vy = MA_DR_MP3_VLD(&zlin[4*i - 64*(15 - k)]); +#define MA_DR_MP3_V0(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0)) ; a = MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1)); } +#define MA_DR_MP3_V1(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vz, w0), MA_DR_MP3_VMUL(vy, w1))); } +#define MA_DR_MP3_V2(k) { MA_DR_MP3_VLOAD(k) b = MA_DR_MP3_VADD(b, MA_DR_MP3_VADD(MA_DR_MP3_VMUL(vz, w1), MA_DR_MP3_VMUL(vy, w0))); a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSUB(MA_DR_MP3_VMUL(vy, w1), MA_DR_MP3_VMUL(vz, w0))); } + ma_dr_mp3_f4 a, b; zlin[4*i] = xl[18*(31 - i)]; zlin[4*i + 1] = xr[18*(31 - i)]; zlin[4*i + 2] = xl[1 + 18*(31 - i)]; @@ -91488,28 +91139,28 @@ static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) zlin[4*i + 64 + 1] = xr[1 + 18*(1 + i)]; zlin[4*i - 64 + 2] = xl[18*(1 + i)]; zlin[4*i - 64 + 3] = xr[18*(1 + i)]; - DRMP3_V0(0) DRMP3_V2(1) DRMP3_V1(2) DRMP3_V2(3) DRMP3_V1(4) DRMP3_V2(5) DRMP3_V1(6) DRMP3_V2(7) + MA_DR_MP3_V0(0) MA_DR_MP3_V2(1) MA_DR_MP3_V1(2) MA_DR_MP3_V2(3) MA_DR_MP3_V1(4) MA_DR_MP3_V2(5) MA_DR_MP3_V1(6) MA_DR_MP3_V2(7) { -#ifndef DR_MP3_FLOAT_OUTPUT -#if DRMP3_HAVE_SSE - static const drmp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; - static const drmp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; +#ifndef MA_DR_MP3_FLOAT_OUTPUT +#if MA_DR_MP3_HAVE_SSE + static const ma_dr_mp3_f4 g_max = { 32767.0f, 32767.0f, 32767.0f, 32767.0f }; + static const ma_dr_mp3_f4 g_min = { -32768.0f, -32768.0f, -32768.0f, -32768.0f }; __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, g_max), g_min)), _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, g_max), g_min))); - dstr[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); - dstr[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); - dstl[(15 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); - dstl[(17 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); - dstr[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); - dstr[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); - dstl[(47 - i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); - dstl[(49 + i)*nch] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); + dstr[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 1); + dstr[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 5); + dstl[(15 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 0); + dstl[(17 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 4); + dstr[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 3); + dstr[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 7); + dstl[(47 - i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 2); + dstl[(49 + i)*nch] = (ma_int16)_mm_extract_epi16(pcm8, 6); #else int16x4_t pcma, pcmb; - a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); - b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); - pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); - pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); + a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f)); + b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0))))); vst1_lane_s16(dstr + (15 - i)*nch, pcma, 1); vst1_lane_s16(dstr + (17 + i)*nch, pcmb, 1); vst1_lane_s16(dstl + (15 - i)*nch, pcma, 0); @@ -91520,14 +91171,14 @@ static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) vst1_lane_s16(dstl + (49 + i)*nch, pcmb, 2); #endif #else - #if DRMP3_HAVE_SSE - static const drmp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; + #if MA_DR_MP3_HAVE_SSE + static const ma_dr_mp3_f4 g_scale = { 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f, 1.0f/32768.0f }; #else - const drmp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f); + const ma_dr_mp3_f4 g_scale = vdupq_n_f32(1.0f/32768.0f); #endif - a = DRMP3_VMUL(a, g_scale); - b = DRMP3_VMUL(b, g_scale); -#if DRMP3_HAVE_SSE + a = MA_DR_MP3_VMUL(a, g_scale); + b = MA_DR_MP3_VMUL(b, g_scale); +#if MA_DR_MP3_HAVE_SSE _mm_store_ss(dstr + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(1, 1, 1, 1))); _mm_store_ss(dstr + (17 + i)*nch, _mm_shuffle_ps(b, b, _MM_SHUFFLE(1, 1, 1, 1))); _mm_store_ss(dstl + (15 - i)*nch, _mm_shuffle_ps(a, a, _MM_SHUFFLE(0, 0, 0, 0))); @@ -91550,15 +91201,15 @@ static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) } } else #endif -#ifdef DR_MP3_ONLY_SIMD +#ifdef MA_DR_MP3_ONLY_SIMD {} #else for (i = 14; i >= 0; i--) { -#define DRMP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; -#define DRMP3_S0(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } -#define DRMP3_S1(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } -#define DRMP3_S2(k) { int j; DRMP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } +#define MA_DR_MP3_LOAD(k) float w0 = *w++; float w1 = *w++; float *vz = &zlin[4*i - k*64]; float *vy = &zlin[4*i - (15 - k)*64]; +#define MA_DR_MP3_S0(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] = vz[j]*w1 + vy[j]*w0, a[j] = vz[j]*w0 - vy[j]*w1; } +#define MA_DR_MP3_S1(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vz[j]*w0 - vy[j]*w1; } +#define MA_DR_MP3_S2(k) { int j; MA_DR_MP3_LOAD(k); for (j = 0; j < 4; j++) b[j] += vz[j]*w1 + vy[j]*w0, a[j] += vy[j]*w1 - vz[j]*w0; } float a[4], b[4]; zlin[4*i] = xl[18*(31 - i)]; zlin[4*i + 1] = xr[18*(31 - i)]; @@ -91568,31 +91219,31 @@ static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins) zlin[4*(i + 16) + 1] = xr[1 + 18*(1 + i)]; zlin[4*(i - 16) + 2] = xl[18*(1 + i)]; zlin[4*(i - 16) + 3] = xr[18*(1 + i)]; - DRMP3_S0(0) DRMP3_S2(1) DRMP3_S1(2) DRMP3_S2(3) DRMP3_S1(4) DRMP3_S2(5) DRMP3_S1(6) DRMP3_S2(7) - dstr[(15 - i)*nch] = drmp3d_scale_pcm(a[1]); - dstr[(17 + i)*nch] = drmp3d_scale_pcm(b[1]); - dstl[(15 - i)*nch] = drmp3d_scale_pcm(a[0]); - dstl[(17 + i)*nch] = drmp3d_scale_pcm(b[0]); - dstr[(47 - i)*nch] = drmp3d_scale_pcm(a[3]); - dstr[(49 + i)*nch] = drmp3d_scale_pcm(b[3]); - dstl[(47 - i)*nch] = drmp3d_scale_pcm(a[2]); - dstl[(49 + i)*nch] = drmp3d_scale_pcm(b[2]); + MA_DR_MP3_S0(0) MA_DR_MP3_S2(1) MA_DR_MP3_S1(2) MA_DR_MP3_S2(3) MA_DR_MP3_S1(4) MA_DR_MP3_S2(5) MA_DR_MP3_S1(6) MA_DR_MP3_S2(7) + dstr[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[1]); + dstr[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[1]); + dstl[(15 - i)*nch] = ma_dr_mp3d_scale_pcm(a[0]); + dstl[(17 + i)*nch] = ma_dr_mp3d_scale_pcm(b[0]); + dstr[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[3]); + dstr[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[3]); + dstl[(47 - i)*nch] = ma_dr_mp3d_scale_pcm(a[2]); + dstl[(49 + i)*nch] = ma_dr_mp3d_scale_pcm(b[2]); } #endif } -static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, drmp3d_sample_t *pcm, float *lins) +static void ma_dr_mp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int nch, ma_dr_mp3d_sample_t *pcm, float *lins) { int i; for (i = 0; i < nch; i++) { - drmp3d_DCT_II(grbuf + 576*i, nbands); + ma_dr_mp3d_DCT_II(grbuf + 576*i, nbands); } - DRMP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64); + MA_DR_MP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64); for (i = 0; i < nbands; i += 2) { - drmp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); + ma_dr_mp3d_synth(grbuf + i, pcm + 32*nch*i, nch, lins + i*64); } -#ifndef DR_MP3_NONSTANDARD_BUT_LOGICAL +#ifndef MA_DR_MP3_NONSTANDARD_BUT_LOGICAL if (nch == 1) { for (i = 0; i < 15*64; i += 2) @@ -91602,38 +91253,38 @@ static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int } else #endif { - DRMP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64); + MA_DR_MP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64); } } -static int drmp3d_match_frame(const drmp3_uint8 *hdr, int mp3_bytes, int frame_bytes) +static int ma_dr_mp3d_match_frame(const ma_uint8 *hdr, int mp3_bytes, int frame_bytes) { int i, nmatch; - for (i = 0, nmatch = 0; nmatch < DRMP3_MAX_FRAME_SYNC_MATCHES; nmatch++) + for (i = 0, nmatch = 0; nmatch < MA_DR_MP3_MAX_FRAME_SYNC_MATCHES; nmatch++) { - i += drmp3_hdr_frame_bytes(hdr + i, frame_bytes) + drmp3_hdr_padding(hdr + i); - if (i + DRMP3_HDR_SIZE > mp3_bytes) + i += ma_dr_mp3_hdr_frame_bytes(hdr + i, frame_bytes) + ma_dr_mp3_hdr_padding(hdr + i); + if (i + MA_DR_MP3_HDR_SIZE > mp3_bytes) return nmatch > 0; - if (!drmp3_hdr_compare(hdr, hdr + i)) + if (!ma_dr_mp3_hdr_compare(hdr, hdr + i)) return 0; } return 1; } -static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) +static int ma_dr_mp3d_find_frame(const ma_uint8 *mp3, int mp3_bytes, int *free_format_bytes, int *ptr_frame_bytes) { int i, k; - for (i = 0; i < mp3_bytes - DRMP3_HDR_SIZE; i++, mp3++) + for (i = 0; i < mp3_bytes - MA_DR_MP3_HDR_SIZE; i++, mp3++) { - if (drmp3_hdr_valid(mp3)) + if (ma_dr_mp3_hdr_valid(mp3)) { - int frame_bytes = drmp3_hdr_frame_bytes(mp3, *free_format_bytes); - int frame_and_padding = frame_bytes + drmp3_hdr_padding(mp3); - for (k = DRMP3_HDR_SIZE; !frame_bytes && k < DRMP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - DRMP3_HDR_SIZE; k++) + int frame_bytes = ma_dr_mp3_hdr_frame_bytes(mp3, *free_format_bytes); + int frame_and_padding = frame_bytes + ma_dr_mp3_hdr_padding(mp3); + for (k = MA_DR_MP3_HDR_SIZE; !frame_bytes && k < MA_DR_MP3_MAX_FREE_FORMAT_FRAME_SIZE && i + 2*k < mp3_bytes - MA_DR_MP3_HDR_SIZE; k++) { - if (drmp3_hdr_compare(mp3, mp3 + k)) + if (ma_dr_mp3_hdr_compare(mp3, mp3 + k)) { - int fb = k - drmp3_hdr_padding(mp3); - int nextfb = fb + drmp3_hdr_padding(mp3 + k); - if (i + k + nextfb + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + k + nextfb)) + int fb = k - ma_dr_mp3_hdr_padding(mp3); + int nextfb = fb + ma_dr_mp3_hdr_padding(mp3 + k); + if (i + k + nextfb + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + k + nextfb)) continue; frame_and_padding = k; frame_bytes = fb; @@ -91641,7 +91292,7 @@ static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_fo } } if ((frame_bytes && i + frame_and_padding <= mp3_bytes && - drmp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || + ma_dr_mp3d_match_frame(mp3, mp3_bytes - i, frame_bytes)) || (!i && frame_and_padding == mp3_bytes)) { *ptr_frame_bytes = frame_and_padding; @@ -91653,28 +91304,28 @@ static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_fo *ptr_frame_bytes = 0; return mp3_bytes; } -DRMP3_API void drmp3dec_init(drmp3dec *dec) +MA_API void ma_dr_mp3dec_init(ma_dr_mp3dec *dec) { dec->header[0] = 0; } -DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info) +MA_API int ma_dr_mp3dec_decode_frame(ma_dr_mp3dec *dec, const ma_uint8 *mp3, int mp3_bytes, void *pcm, ma_dr_mp3dec_frame_info *info) { int i = 0, igr, frame_size = 0, success = 1; - const drmp3_uint8 *hdr; - drmp3_bs bs_frame[1]; - drmp3dec_scratch scratch; - if (mp3_bytes > 4 && dec->header[0] == 0xff && drmp3_hdr_compare(dec->header, mp3)) + const ma_uint8 *hdr; + ma_dr_mp3_bs bs_frame[1]; + ma_dr_mp3dec_scratch scratch; + if (mp3_bytes > 4 && dec->header[0] == 0xff && ma_dr_mp3_hdr_compare(dec->header, mp3)) { - frame_size = drmp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + drmp3_hdr_padding(mp3); - if (frame_size != mp3_bytes && (frame_size + DRMP3_HDR_SIZE > mp3_bytes || !drmp3_hdr_compare(mp3, mp3 + frame_size))) + frame_size = ma_dr_mp3_hdr_frame_bytes(mp3, dec->free_format_bytes) + ma_dr_mp3_hdr_padding(mp3); + if (frame_size != mp3_bytes && (frame_size + MA_DR_MP3_HDR_SIZE > mp3_bytes || !ma_dr_mp3_hdr_compare(mp3, mp3 + frame_size))) { frame_size = 0; } } if (!frame_size) { - DRMP3_ZERO_MEMORY(dec, sizeof(drmp3dec)); - i = drmp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); + MA_DR_MP3_ZERO_MEMORY(dec, sizeof(ma_dr_mp3dec)); + i = ma_dr_mp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size); if (!frame_size || i + frame_size > mp3_bytes) { info->frame_bytes = i; @@ -91682,96 +91333,96 @@ DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int m } } hdr = mp3 + i; - DRMP3_COPY_MEMORY(dec->header, hdr, DRMP3_HDR_SIZE); + MA_DR_MP3_COPY_MEMORY(dec->header, hdr, MA_DR_MP3_HDR_SIZE); info->frame_bytes = i + frame_size; - info->channels = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2; - info->hz = drmp3_hdr_sample_rate_hz(hdr); - info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr); - info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr); - drmp3_bs_init(bs_frame, hdr + DRMP3_HDR_SIZE, frame_size - DRMP3_HDR_SIZE); - if (DRMP3_HDR_IS_CRC(hdr)) + info->channels = MA_DR_MP3_HDR_IS_MONO(hdr) ? 1 : 2; + info->hz = ma_dr_mp3_hdr_sample_rate_hz(hdr); + info->layer = 4 - MA_DR_MP3_HDR_GET_LAYER(hdr); + info->bitrate_kbps = ma_dr_mp3_hdr_bitrate_kbps(hdr); + ma_dr_mp3_bs_init(bs_frame, hdr + MA_DR_MP3_HDR_SIZE, frame_size - MA_DR_MP3_HDR_SIZE); + if (MA_DR_MP3_HDR_IS_CRC(hdr)) { - drmp3_bs_get_bits(bs_frame, 16); + ma_dr_mp3_bs_get_bits(bs_frame, 16); } if (info->layer == 3) { - int main_data_begin = drmp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); + int main_data_begin = ma_dr_mp3_L3_read_side_info(bs_frame, scratch.gr_info, hdr); if (main_data_begin < 0 || bs_frame->pos > bs_frame->limit) { - drmp3dec_init(dec); + ma_dr_mp3dec_init(dec); return 0; } - success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); + success = ma_dr_mp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin); if (success && pcm != NULL) { - for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels)) + for (igr = 0; igr < (MA_DR_MP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*576*info->channels)) { - DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); - drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); + MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + ma_dr_mp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels); + ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]); } } - drmp3_L3_save_reservoir(dec, &scratch); + ma_dr_mp3_L3_save_reservoir(dec, &scratch); } else { -#ifdef DR_MP3_ONLY_MP3 +#ifdef MA_DR_MP3_ONLY_MP3 return 0; #else - drmp3_L12_scale_info sci[1]; + ma_dr_mp3_L12_scale_info sci[1]; if (pcm == NULL) { - return drmp3_hdr_frame_samples(hdr); + return ma_dr_mp3_hdr_frame_samples(hdr); } - drmp3_L12_read_scale_info(hdr, bs_frame, sci); - DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + ma_dr_mp3_L12_read_scale_info(hdr, bs_frame, sci); + MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); for (i = 0, igr = 0; igr < 3; igr++) { - if (12 == (i += drmp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) + if (12 == (i += ma_dr_mp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1))) { i = 0; - drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); - drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]); - DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); - pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels); + ma_dr_mp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]); + ma_dr_mp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (ma_dr_mp3d_sample_t*)pcm, scratch.syn[0]); + MA_DR_MP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float)); + pcm = MA_DR_MP3_OFFSET_PTR(pcm, sizeof(ma_dr_mp3d_sample_t)*384*info->channels); } if (bs_frame->pos > bs_frame->limit) { - drmp3dec_init(dec); + ma_dr_mp3dec_init(dec); return 0; } } #endif } - return success*drmp3_hdr_frame_samples(dec->header); + return success*ma_dr_mp3_hdr_frame_samples(dec->header); } -DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples) +MA_API void ma_dr_mp3dec_f32_to_s16(const float *in, ma_int16 *out, size_t num_samples) { size_t i = 0; -#if DRMP3_HAVE_SIMD +#if MA_DR_MP3_HAVE_SIMD size_t aligned_count = num_samples & ~7; for(; i < aligned_count; i+=8) { - drmp3_f4 scale = DRMP3_VSET(32768.0f); - drmp3_f4 a = DRMP3_VMUL(DRMP3_VLD(&in[i ]), scale); - drmp3_f4 b = DRMP3_VMUL(DRMP3_VLD(&in[i+4]), scale); -#if DRMP3_HAVE_SSE - drmp3_f4 s16max = DRMP3_VSET( 32767.0f); - drmp3_f4 s16min = DRMP3_VSET(-32768.0f); + ma_dr_mp3_f4 scale = MA_DR_MP3_VSET(32768.0f); + ma_dr_mp3_f4 a = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i ]), scale); + ma_dr_mp3_f4 b = MA_DR_MP3_VMUL(MA_DR_MP3_VLD(&in[i+4]), scale); +#if MA_DR_MP3_HAVE_SSE + ma_dr_mp3_f4 s16max = MA_DR_MP3_VSET( 32767.0f); + ma_dr_mp3_f4 s16min = MA_DR_MP3_VSET(-32768.0f); __m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)), _mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min))); - out[i ] = (drmp3_int16)_mm_extract_epi16(pcm8, 0); - out[i+1] = (drmp3_int16)_mm_extract_epi16(pcm8, 1); - out[i+2] = (drmp3_int16)_mm_extract_epi16(pcm8, 2); - out[i+3] = (drmp3_int16)_mm_extract_epi16(pcm8, 3); - out[i+4] = (drmp3_int16)_mm_extract_epi16(pcm8, 4); - out[i+5] = (drmp3_int16)_mm_extract_epi16(pcm8, 5); - out[i+6] = (drmp3_int16)_mm_extract_epi16(pcm8, 6); - out[i+7] = (drmp3_int16)_mm_extract_epi16(pcm8, 7); + out[i ] = (ma_int16)_mm_extract_epi16(pcm8, 0); + out[i+1] = (ma_int16)_mm_extract_epi16(pcm8, 1); + out[i+2] = (ma_int16)_mm_extract_epi16(pcm8, 2); + out[i+3] = (ma_int16)_mm_extract_epi16(pcm8, 3); + out[i+4] = (ma_int16)_mm_extract_epi16(pcm8, 4); + out[i+5] = (ma_int16)_mm_extract_epi16(pcm8, 5); + out[i+6] = (ma_int16)_mm_extract_epi16(pcm8, 6); + out[i+7] = (ma_int16)_mm_extract_epi16(pcm8, 7); #else int16x4_t pcma, pcmb; - a = DRMP3_VADD(a, DRMP3_VSET(0.5f)); - b = DRMP3_VADD(b, DRMP3_VSET(0.5f)); - pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0))))); - pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0))))); + a = MA_DR_MP3_VADD(a, MA_DR_MP3_VSET(0.5f)); + b = MA_DR_MP3_VADD(b, MA_DR_MP3_VSET(0.5f)); + pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, MA_DR_MP3_VSET(0))))); + pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, MA_DR_MP3_VSET(0))))); vst1_lane_s16(out+i , pcma, 0); vst1_lane_s16(out+i+1, pcma, 1); vst1_lane_s16(out+i+2, pcma, 2); @@ -91787,78 +91438,69 @@ DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num { float sample = in[i] * 32768.0f; if (sample >= 32766.5) - out[i] = (drmp3_int16) 32767; + out[i] = (ma_int16) 32767; else if (sample <= -32767.5) - out[i] = (drmp3_int16)-32768; + out[i] = (ma_int16)-32768; else { - short s = (drmp3_int16)(sample + .5f); + short s = (ma_int16)(sample + .5f); s -= (s < 0); out[i] = s; } } } -#if defined(SIZE_MAX) - #define DRMP3_SIZE_MAX SIZE_MAX -#else - #if defined(_WIN64) || defined(_LP64) || defined(__LP64__) - #define DRMP3_SIZE_MAX ((drmp3_uint64)0xFFFFFFFFFFFFFFFF) - #else - #define DRMP3_SIZE_MAX 0xFFFFFFFF - #endif +#ifndef MA_DR_MP3_SEEK_LEADING_MP3_FRAMES +#define MA_DR_MP3_SEEK_LEADING_MP3_FRAMES 2 #endif -#ifndef DRMP3_SEEK_LEADING_MP3_FRAMES -#define DRMP3_SEEK_LEADING_MP3_FRAMES 2 +#define MA_DR_MP3_MIN_DATA_CHUNK_SIZE 16384 +#ifndef MA_DR_MP3_DATA_CHUNK_SIZE +#define MA_DR_MP3_DATA_CHUNK_SIZE (MA_DR_MP3_MIN_DATA_CHUNK_SIZE*4) #endif -#define DRMP3_MIN_DATA_CHUNK_SIZE 16384 -#ifndef DRMP3_DATA_CHUNK_SIZE -#define DRMP3_DATA_CHUNK_SIZE (DRMP3_MIN_DATA_CHUNK_SIZE*4) +#define MA_DR_MP3_COUNTOF(x) (sizeof(x) / sizeof(x[0])) +#define MA_DR_MP3_CLAMP(x, lo, hi) (MA_DR_MP3_MAX(lo, MA_DR_MP3_MIN(x, hi))) +#ifndef MA_DR_MP3_PI_D +#define MA_DR_MP3_PI_D 3.14159265358979323846264 #endif -#define DRMP3_COUNTOF(x) (sizeof(x) / sizeof(x[0])) -#define DRMP3_CLAMP(x, lo, hi) (DRMP3_MAX(lo, DRMP3_MIN(x, hi))) -#ifndef DRMP3_PI_D -#define DRMP3_PI_D 3.14159265358979323846264 -#endif -#define DRMP3_DEFAULT_RESAMPLER_LPF_ORDER 2 -static DRMP3_INLINE float drmp3_mix_f32(float x, float y, float a) +#define MA_DR_MP3_DEFAULT_RESAMPLER_LPF_ORDER 2 +static MA_INLINE float ma_dr_mp3_mix_f32(float x, float y, float a) { return x*(1-a) + y*a; } -static DRMP3_INLINE float drmp3_mix_f32_fast(float x, float y, float a) +static MA_INLINE float ma_dr_mp3_mix_f32_fast(float x, float y, float a) { float r0 = (y - x); float r1 = r0*a; return x + r1; } -static DRMP3_INLINE drmp3_uint32 drmp3_gcf_u32(drmp3_uint32 a, drmp3_uint32 b) +static MA_INLINE ma_uint32 ma_dr_mp3_gcf_u32(ma_uint32 a, ma_uint32 b) { for (;;) { if (b == 0) { break; } else { - drmp3_uint32 t = a; + ma_uint32 t = a; a = b; b = t % a; } } return a; } -static void* drmp3__malloc_default(size_t sz, void* pUserData) +static void* ma_dr_mp3__malloc_default(size_t sz, void* pUserData) { (void)pUserData; - return DRMP3_MALLOC(sz); + return MA_DR_MP3_MALLOC(sz); } -static void* drmp3__realloc_default(void* p, size_t sz, void* pUserData) +static void* ma_dr_mp3__realloc_default(void* p, size_t sz, void* pUserData) { (void)pUserData; - return DRMP3_REALLOC(p, sz); + return MA_DR_MP3_REALLOC(p, sz); } -static void drmp3__free_default(void* p, void* pUserData) +static void ma_dr_mp3__free_default(void* p, void* pUserData) { (void)pUserData; - DRMP3_FREE(p); + MA_DR_MP3_FREE(p); } -static void* drmp3__malloc_from_callbacks(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks) +static void* ma_dr_mp3__malloc_from_callbacks(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; @@ -91871,7 +91513,7 @@ static void* drmp3__malloc_from_callbacks(size_t sz, const drmp3_allocation_call } return NULL; } -static void* drmp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drmp3_allocation_callbacks* pAllocationCallbacks) +static void* ma_dr_mp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks == NULL) { return NULL; @@ -91886,14 +91528,14 @@ static void* drmp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, return NULL; } if (p != NULL) { - DRMP3_COPY_MEMORY(p2, p, szOld); + MA_DR_MP3_COPY_MEMORY(p2, p, szOld); pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } return p2; } return NULL; } -static void drmp3__free_from_callbacks(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks) +static void ma_dr_mp3__free_from_callbacks(void* p, const ma_allocation_callbacks* pAllocationCallbacks) { if (p == NULL || pAllocationCallbacks == NULL) { return; @@ -91902,111 +91544,114 @@ static void drmp3__free_from_callbacks(void* p, const drmp3_allocation_callbacks pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData); } } -static drmp3_allocation_callbacks drmp3_copy_allocation_callbacks_or_defaults(const drmp3_allocation_callbacks* pAllocationCallbacks) +static ma_allocation_callbacks ma_dr_mp3_copy_allocation_callbacks_or_defaults(const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { return *pAllocationCallbacks; } else { - drmp3_allocation_callbacks allocationCallbacks; + ma_allocation_callbacks allocationCallbacks; allocationCallbacks.pUserData = NULL; - allocationCallbacks.onMalloc = drmp3__malloc_default; - allocationCallbacks.onRealloc = drmp3__realloc_default; - allocationCallbacks.onFree = drmp3__free_default; + allocationCallbacks.onMalloc = ma_dr_mp3__malloc_default; + allocationCallbacks.onRealloc = ma_dr_mp3__realloc_default; + allocationCallbacks.onFree = ma_dr_mp3__free_default; return allocationCallbacks; } } -static size_t drmp3__on_read(drmp3* pMP3, void* pBufferOut, size_t bytesToRead) +static size_t ma_dr_mp3__on_read(ma_dr_mp3* pMP3, void* pBufferOut, size_t bytesToRead) { size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead); pMP3->streamCursor += bytesRead; return bytesRead; } -static drmp3_bool32 drmp3__on_seek(drmp3* pMP3, int offset, drmp3_seek_origin origin) +static ma_bool32 ma_dr_mp3__on_seek(ma_dr_mp3* pMP3, int offset, ma_dr_mp3_seek_origin origin) { - DRMP3_ASSERT(offset >= 0); + MA_DR_MP3_ASSERT(offset >= 0); if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) { - return DRMP3_FALSE; + return MA_FALSE; } - if (origin == drmp3_seek_origin_start) { - pMP3->streamCursor = (drmp3_uint64)offset; + if (origin == ma_dr_mp3_seek_origin_start) { + pMP3->streamCursor = (ma_uint64)offset; } else { pMP3->streamCursor += offset; } - return DRMP3_TRUE; + return MA_TRUE; } -static drmp3_bool32 drmp3__on_seek_64(drmp3* pMP3, drmp3_uint64 offset, drmp3_seek_origin origin) +static ma_bool32 ma_dr_mp3__on_seek_64(ma_dr_mp3* pMP3, ma_uint64 offset, ma_dr_mp3_seek_origin origin) { if (offset <= 0x7FFFFFFF) { - return drmp3__on_seek(pMP3, (int)offset, origin); + return ma_dr_mp3__on_seek(pMP3, (int)offset, origin); } - if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_start)) { - return DRMP3_FALSE; + if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_start)) { + return MA_FALSE; } offset -= 0x7FFFFFFF; while (offset > 0) { if (offset <= 0x7FFFFFFF) { - if (!drmp3__on_seek(pMP3, (int)offset, drmp3_seek_origin_current)) { - return DRMP3_FALSE; + if (!ma_dr_mp3__on_seek(pMP3, (int)offset, ma_dr_mp3_seek_origin_current)) { + return MA_FALSE; } offset = 0; } else { - if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_current)) { - return DRMP3_FALSE; + if (!ma_dr_mp3__on_seek(pMP3, 0x7FFFFFFF, ma_dr_mp3_seek_origin_current)) { + return MA_FALSE; } offset -= 0x7FFFFFFF; } } - return DRMP3_TRUE; + return MA_TRUE; } -static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) +static ma_uint32 ma_dr_mp3_decode_next_frame_ex__callbacks(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) { - drmp3_uint32 pcmFramesRead = 0; - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(pMP3->onRead != NULL); + ma_uint32 pcmFramesRead = 0; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->onRead != NULL); if (pMP3->atEnd) { return 0; } for (;;) { - drmp3dec_frame_info info; - if (pMP3->dataSize < DRMP3_MIN_DATA_CHUNK_SIZE) { + ma_dr_mp3dec_frame_info info; + if (pMP3->dataSize < MA_DR_MP3_MIN_DATA_CHUNK_SIZE) { size_t bytesRead; if (pMP3->pData != NULL) { - DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); + MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); } pMP3->dataConsumed = 0; - if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) { - drmp3_uint8* pNewData; + if (pMP3->dataCapacity < MA_DR_MP3_DATA_CHUNK_SIZE) { + ma_uint8* pNewData; size_t newDataCap; - newDataCap = DRMP3_DATA_CHUNK_SIZE; - pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); + newDataCap = MA_DR_MP3_DATA_CHUNK_SIZE; + pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); if (pNewData == NULL) { return 0; } pMP3->pData = pNewData; pMP3->dataCapacity = newDataCap; } - bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); if (bytesRead == 0) { if (pMP3->dataSize == 0) { - pMP3->atEnd = DRMP3_TRUE; + pMP3->atEnd = MA_TRUE; return 0; } } pMP3->dataSize += bytesRead; } if (pMP3->dataSize > INT_MAX) { - pMP3->atEnd = DRMP3_TRUE; + pMP3->atEnd = MA_TRUE; return 0; } - DRMP3_ASSERT(pMP3->pData != NULL); - DRMP3_ASSERT(pMP3->dataCapacity > 0); - pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); + MA_DR_MP3_ASSERT(pMP3->pData != NULL); + MA_DR_MP3_ASSERT(pMP3->dataCapacity > 0); + if (pMP3->pData == NULL) { + return 0; + } + pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); if (info.frame_bytes > 0) { pMP3->dataConsumed += (size_t)info.frame_bytes; pMP3->dataSize -= (size_t)info.frame_bytes; } if (pcmFramesRead > 0) { - pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header); + pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); pMP3->pcmFramesConsumedInMP3Frame = 0; pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; pMP3->mp3FrameChannels = info.channels; @@ -92014,22 +91659,22 @@ static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sa break; } else if (info.frame_bytes == 0) { size_t bytesRead; - DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); + MA_DR_MP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize); pMP3->dataConsumed = 0; if (pMP3->dataCapacity == pMP3->dataSize) { - drmp3_uint8* pNewData; + ma_uint8* pNewData; size_t newDataCap; - newDataCap = pMP3->dataCapacity + DRMP3_DATA_CHUNK_SIZE; - pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); + newDataCap = pMP3->dataCapacity + MA_DR_MP3_DATA_CHUNK_SIZE; + pNewData = (ma_uint8*)ma_dr_mp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks); if (pNewData == NULL) { return 0; } pMP3->pData = pNewData; pMP3->dataCapacity = newDataCap; } - bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); + bytesRead = ma_dr_mp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize)); if (bytesRead == 0) { - pMP3->atEnd = DRMP3_TRUE; + pMP3->atEnd = MA_TRUE; return 0; } pMP3->dataSize += bytesRead; @@ -92037,19 +91682,19 @@ static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sa }; return pcmFramesRead; } -static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) +static ma_uint32 ma_dr_mp3_decode_next_frame_ex__memory(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) { - drmp3_uint32 pcmFramesRead = 0; - drmp3dec_frame_info info; - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(pMP3->memory.pData != NULL); + ma_uint32 pcmFramesRead = 0; + ma_dr_mp3dec_frame_info info; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->memory.pData != NULL); if (pMP3->atEnd) { return 0; } for (;;) { - pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info); + pcmFramesRead = ma_dr_mp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info); if (pcmFramesRead > 0) { - pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header); + pcmFramesRead = ma_dr_mp3_hdr_frame_samples(pMP3->decoder.header); pMP3->pcmFramesConsumedInMP3Frame = 0; pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead; pMP3->mp3FrameChannels = info.channels; @@ -92064,25 +91709,25 @@ static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sampl pMP3->memory.currentReadPos += (size_t)info.frame_bytes; return pcmFramesRead; } -static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames) +static ma_uint32 ma_dr_mp3_decode_next_frame_ex(ma_dr_mp3* pMP3, ma_dr_mp3d_sample_t* pPCMFrames) { if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) { - return drmp3_decode_next_frame_ex__memory(pMP3, pPCMFrames); + return ma_dr_mp3_decode_next_frame_ex__memory(pMP3, pPCMFrames); } else { - return drmp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames); + return ma_dr_mp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames); } } -static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3) +static ma_uint32 ma_dr_mp3_decode_next_frame(ma_dr_mp3* pMP3) { - DRMP3_ASSERT(pMP3 != NULL); - return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames); + MA_DR_MP3_ASSERT(pMP3 != NULL); + return ma_dr_mp3_decode_next_frame_ex(pMP3, (ma_dr_mp3d_sample_t*)pMP3->pcmFrames); } #if 0 -static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3) +static ma_uint32 ma_dr_mp3_seek_next_frame(ma_dr_mp3* pMP3) { - drmp3_uint32 pcmFrameCount; - DRMP3_ASSERT(pMP3 != NULL); - pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL); + ma_uint32 pcmFrameCount; + MA_DR_MP3_ASSERT(pMP3 != NULL); + pcmFrameCount = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); if (pcmFrameCount == 0) { return 0; } @@ -92092,55 +91737,55 @@ static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3) return pcmFrameCount; } #endif -static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks) +static ma_bool32 ma_dr_mp3_init_internal(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(onRead != NULL); - drmp3dec_init(&pMP3->decoder); + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(onRead != NULL); + ma_dr_mp3dec_init(&pMP3->decoder); pMP3->onRead = onRead; pMP3->onSeek = onSeek; pMP3->pUserData = pUserData; - pMP3->allocationCallbacks = drmp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); + pMP3->allocationCallbacks = ma_dr_mp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks); if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) { - return DRMP3_FALSE; + return MA_FALSE; } - if (drmp3_decode_next_frame(pMP3) == 0) { - drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); - return DRMP3_FALSE; + if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { + ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); + return MA_FALSE; } pMP3->channels = pMP3->mp3FrameChannels; pMP3->sampleRate = pMP3->mp3FrameSampleRate; - return DRMP3_TRUE; + return MA_TRUE; } -DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_mp3_init(ma_dr_mp3* pMP3, ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, const ma_allocation_callbacks* pAllocationCallbacks) { if (pMP3 == NULL || onRead == NULL) { - return DRMP3_FALSE; + return MA_FALSE; } - DRMP3_ZERO_OBJECT(pMP3); - return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); + MA_DR_MP3_ZERO_OBJECT(pMP3); + return ma_dr_mp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks); } -static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) +static size_t ma_dr_mp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) { - drmp3* pMP3 = (drmp3*)pUserData; + ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; size_t bytesRemaining; - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos); + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos); bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos; if (bytesToRead > bytesRemaining) { bytesToRead = bytesRemaining; } if (bytesToRead > 0) { - DRMP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead); + MA_DR_MP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead); pMP3->memory.currentReadPos += bytesToRead; } return bytesToRead; } -static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3_seek_origin origin) +static ma_bool32 ma_dr_mp3__on_seek_memory(void* pUserData, int byteOffset, ma_dr_mp3_seek_origin origin) { - drmp3* pMP3 = (drmp3*)pUserData; - DRMP3_ASSERT(pMP3 != NULL); - if (origin == drmp3_seek_origin_current) { + ma_dr_mp3* pMP3 = (ma_dr_mp3*)pUserData; + MA_DR_MP3_ASSERT(pMP3 != NULL); + if (origin == ma_dr_mp3_seek_origin_current) { if (byteOffset > 0) { if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) { byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); @@ -92152,585 +91797,75 @@ static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3 } pMP3->memory.currentReadPos += byteOffset; } else { - if ((drmp3_uint32)byteOffset <= pMP3->memory.dataSize) { + if ((ma_uint32)byteOffset <= pMP3->memory.dataSize) { pMP3->memory.currentReadPos = byteOffset; } else { pMP3->memory.currentReadPos = pMP3->memory.dataSize; } } - return DRMP3_TRUE; + return MA_TRUE; } -DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_mp3_init_memory(ma_dr_mp3* pMP3, const void* pData, size_t dataSize, const ma_allocation_callbacks* pAllocationCallbacks) { if (pMP3 == NULL) { - return DRMP3_FALSE; + return MA_FALSE; } - DRMP3_ZERO_OBJECT(pMP3); + MA_DR_MP3_ZERO_OBJECT(pMP3); if (pData == NULL || dataSize == 0) { - return DRMP3_FALSE; + return MA_FALSE; } - pMP3->memory.pData = (const drmp3_uint8*)pData; + pMP3->memory.pData = (const ma_uint8*)pData; pMP3->memory.dataSize = dataSize; pMP3->memory.currentReadPos = 0; - return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3, pAllocationCallbacks); + return ma_dr_mp3_init_internal(pMP3, ma_dr_mp3__on_read_memory, ma_dr_mp3__on_seek_memory, pMP3, pAllocationCallbacks); } -#ifndef DR_MP3_NO_STDIO +#ifndef MA_DR_MP3_NO_STDIO #include #include -#include -static drmp3_result drmp3_result_from_errno(int e) -{ - switch (e) - { - case 0: return DRMP3_SUCCESS; - #ifdef EPERM - case EPERM: return DRMP3_INVALID_OPERATION; - #endif - #ifdef ENOENT - case ENOENT: return DRMP3_DOES_NOT_EXIST; - #endif - #ifdef ESRCH - case ESRCH: return DRMP3_DOES_NOT_EXIST; - #endif - #ifdef EINTR - case EINTR: return DRMP3_INTERRUPT; - #endif - #ifdef EIO - case EIO: return DRMP3_IO_ERROR; - #endif - #ifdef ENXIO - case ENXIO: return DRMP3_DOES_NOT_EXIST; - #endif - #ifdef E2BIG - case E2BIG: return DRMP3_INVALID_ARGS; - #endif - #ifdef ENOEXEC - case ENOEXEC: return DRMP3_INVALID_FILE; - #endif - #ifdef EBADF - case EBADF: return DRMP3_INVALID_FILE; - #endif - #ifdef ECHILD - case ECHILD: return DRMP3_ERROR; - #endif - #ifdef EAGAIN - case EAGAIN: return DRMP3_UNAVAILABLE; - #endif - #ifdef ENOMEM - case ENOMEM: return DRMP3_OUT_OF_MEMORY; - #endif - #ifdef EACCES - case EACCES: return DRMP3_ACCESS_DENIED; - #endif - #ifdef EFAULT - case EFAULT: return DRMP3_BAD_ADDRESS; - #endif - #ifdef ENOTBLK - case ENOTBLK: return DRMP3_ERROR; - #endif - #ifdef EBUSY - case EBUSY: return DRMP3_BUSY; - #endif - #ifdef EEXIST - case EEXIST: return DRMP3_ALREADY_EXISTS; - #endif - #ifdef EXDEV - case EXDEV: return DRMP3_ERROR; - #endif - #ifdef ENODEV - case ENODEV: return DRMP3_DOES_NOT_EXIST; - #endif - #ifdef ENOTDIR - case ENOTDIR: return DRMP3_NOT_DIRECTORY; - #endif - #ifdef EISDIR - case EISDIR: return DRMP3_IS_DIRECTORY; - #endif - #ifdef EINVAL - case EINVAL: return DRMP3_INVALID_ARGS; - #endif - #ifdef ENFILE - case ENFILE: return DRMP3_TOO_MANY_OPEN_FILES; - #endif - #ifdef EMFILE - case EMFILE: return DRMP3_TOO_MANY_OPEN_FILES; - #endif - #ifdef ENOTTY - case ENOTTY: return DRMP3_INVALID_OPERATION; - #endif - #ifdef ETXTBSY - case ETXTBSY: return DRMP3_BUSY; - #endif - #ifdef EFBIG - case EFBIG: return DRMP3_TOO_BIG; - #endif - #ifdef ENOSPC - case ENOSPC: return DRMP3_NO_SPACE; - #endif - #ifdef ESPIPE - case ESPIPE: return DRMP3_BAD_SEEK; - #endif - #ifdef EROFS - case EROFS: return DRMP3_ACCESS_DENIED; - #endif - #ifdef EMLINK - case EMLINK: return DRMP3_TOO_MANY_LINKS; - #endif - #ifdef EPIPE - case EPIPE: return DRMP3_BAD_PIPE; - #endif - #ifdef EDOM - case EDOM: return DRMP3_OUT_OF_RANGE; - #endif - #ifdef ERANGE - case ERANGE: return DRMP3_OUT_OF_RANGE; - #endif - #ifdef EDEADLK - case EDEADLK: return DRMP3_DEADLOCK; - #endif - #ifdef ENAMETOOLONG - case ENAMETOOLONG: return DRMP3_PATH_TOO_LONG; - #endif - #ifdef ENOLCK - case ENOLCK: return DRMP3_ERROR; - #endif - #ifdef ENOSYS - case ENOSYS: return DRMP3_NOT_IMPLEMENTED; - #endif - #ifdef ENOTEMPTY - case ENOTEMPTY: return DRMP3_DIRECTORY_NOT_EMPTY; - #endif - #ifdef ELOOP - case ELOOP: return DRMP3_TOO_MANY_LINKS; - #endif - #ifdef ENOMSG - case ENOMSG: return DRMP3_NO_MESSAGE; - #endif - #ifdef EIDRM - case EIDRM: return DRMP3_ERROR; - #endif - #ifdef ECHRNG - case ECHRNG: return DRMP3_ERROR; - #endif - #ifdef EL2NSYNC - case EL2NSYNC: return DRMP3_ERROR; - #endif - #ifdef EL3HLT - case EL3HLT: return DRMP3_ERROR; - #endif - #ifdef EL3RST - case EL3RST: return DRMP3_ERROR; - #endif - #ifdef ELNRNG - case ELNRNG: return DRMP3_OUT_OF_RANGE; - #endif - #ifdef EUNATCH - case EUNATCH: return DRMP3_ERROR; - #endif - #ifdef ENOCSI - case ENOCSI: return DRMP3_ERROR; - #endif - #ifdef EL2HLT - case EL2HLT: return DRMP3_ERROR; - #endif - #ifdef EBADE - case EBADE: return DRMP3_ERROR; - #endif - #ifdef EBADR - case EBADR: return DRMP3_ERROR; - #endif - #ifdef EXFULL - case EXFULL: return DRMP3_ERROR; - #endif - #ifdef ENOANO - case ENOANO: return DRMP3_ERROR; - #endif - #ifdef EBADRQC - case EBADRQC: return DRMP3_ERROR; - #endif - #ifdef EBADSLT - case EBADSLT: return DRMP3_ERROR; - #endif - #ifdef EBFONT - case EBFONT: return DRMP3_INVALID_FILE; - #endif - #ifdef ENOSTR - case ENOSTR: return DRMP3_ERROR; - #endif - #ifdef ENODATA - case ENODATA: return DRMP3_NO_DATA_AVAILABLE; - #endif - #ifdef ETIME - case ETIME: return DRMP3_TIMEOUT; - #endif - #ifdef ENOSR - case ENOSR: return DRMP3_NO_DATA_AVAILABLE; - #endif - #ifdef ENONET - case ENONET: return DRMP3_NO_NETWORK; - #endif - #ifdef ENOPKG - case ENOPKG: return DRMP3_ERROR; - #endif - #ifdef EREMOTE - case EREMOTE: return DRMP3_ERROR; - #endif - #ifdef ENOLINK - case ENOLINK: return DRMP3_ERROR; - #endif - #ifdef EADV - case EADV: return DRMP3_ERROR; - #endif - #ifdef ESRMNT - case ESRMNT: return DRMP3_ERROR; - #endif - #ifdef ECOMM - case ECOMM: return DRMP3_ERROR; - #endif - #ifdef EPROTO - case EPROTO: return DRMP3_ERROR; - #endif - #ifdef EMULTIHOP - case EMULTIHOP: return DRMP3_ERROR; - #endif - #ifdef EDOTDOT - case EDOTDOT: return DRMP3_ERROR; - #endif - #ifdef EBADMSG - case EBADMSG: return DRMP3_BAD_MESSAGE; - #endif - #ifdef EOVERFLOW - case EOVERFLOW: return DRMP3_TOO_BIG; - #endif - #ifdef ENOTUNIQ - case ENOTUNIQ: return DRMP3_NOT_UNIQUE; - #endif - #ifdef EBADFD - case EBADFD: return DRMP3_ERROR; - #endif - #ifdef EREMCHG - case EREMCHG: return DRMP3_ERROR; - #endif - #ifdef ELIBACC - case ELIBACC: return DRMP3_ACCESS_DENIED; - #endif - #ifdef ELIBBAD - case ELIBBAD: return DRMP3_INVALID_FILE; - #endif - #ifdef ELIBSCN - case ELIBSCN: return DRMP3_INVALID_FILE; - #endif - #ifdef ELIBMAX - case ELIBMAX: return DRMP3_ERROR; - #endif - #ifdef ELIBEXEC - case ELIBEXEC: return DRMP3_ERROR; - #endif - #ifdef EILSEQ - case EILSEQ: return DRMP3_INVALID_DATA; - #endif - #ifdef ERESTART - case ERESTART: return DRMP3_ERROR; - #endif - #ifdef ESTRPIPE - case ESTRPIPE: return DRMP3_ERROR; - #endif - #ifdef EUSERS - case EUSERS: return DRMP3_ERROR; - #endif - #ifdef ENOTSOCK - case ENOTSOCK: return DRMP3_NOT_SOCKET; - #endif - #ifdef EDESTADDRREQ - case EDESTADDRREQ: return DRMP3_NO_ADDRESS; - #endif - #ifdef EMSGSIZE - case EMSGSIZE: return DRMP3_TOO_BIG; - #endif - #ifdef EPROTOTYPE - case EPROTOTYPE: return DRMP3_BAD_PROTOCOL; - #endif - #ifdef ENOPROTOOPT - case ENOPROTOOPT: return DRMP3_PROTOCOL_UNAVAILABLE; - #endif - #ifdef EPROTONOSUPPORT - case EPROTONOSUPPORT: return DRMP3_PROTOCOL_NOT_SUPPORTED; - #endif - #ifdef ESOCKTNOSUPPORT - case ESOCKTNOSUPPORT: return DRMP3_SOCKET_NOT_SUPPORTED; - #endif - #ifdef EOPNOTSUPP - case EOPNOTSUPP: return DRMP3_INVALID_OPERATION; - #endif - #ifdef EPFNOSUPPORT - case EPFNOSUPPORT: return DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EAFNOSUPPORT - case EAFNOSUPPORT: return DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EADDRINUSE - case EADDRINUSE: return DRMP3_ALREADY_IN_USE; - #endif - #ifdef EADDRNOTAVAIL - case EADDRNOTAVAIL: return DRMP3_ERROR; - #endif - #ifdef ENETDOWN - case ENETDOWN: return DRMP3_NO_NETWORK; - #endif - #ifdef ENETUNREACH - case ENETUNREACH: return DRMP3_NO_NETWORK; - #endif - #ifdef ENETRESET - case ENETRESET: return DRMP3_NO_NETWORK; - #endif - #ifdef ECONNABORTED - case ECONNABORTED: return DRMP3_NO_NETWORK; - #endif - #ifdef ECONNRESET - case ECONNRESET: return DRMP3_CONNECTION_RESET; - #endif - #ifdef ENOBUFS - case ENOBUFS: return DRMP3_NO_SPACE; - #endif - #ifdef EISCONN - case EISCONN: return DRMP3_ALREADY_CONNECTED; - #endif - #ifdef ENOTCONN - case ENOTCONN: return DRMP3_NOT_CONNECTED; - #endif - #ifdef ESHUTDOWN - case ESHUTDOWN: return DRMP3_ERROR; - #endif - #ifdef ETOOMANYREFS - case ETOOMANYREFS: return DRMP3_ERROR; - #endif - #ifdef ETIMEDOUT - case ETIMEDOUT: return DRMP3_TIMEOUT; - #endif - #ifdef ECONNREFUSED - case ECONNREFUSED: return DRMP3_CONNECTION_REFUSED; - #endif - #ifdef EHOSTDOWN - case EHOSTDOWN: return DRMP3_NO_HOST; - #endif - #ifdef EHOSTUNREACH - case EHOSTUNREACH: return DRMP3_NO_HOST; - #endif - #ifdef EALREADY - case EALREADY: return DRMP3_IN_PROGRESS; - #endif - #ifdef EINPROGRESS - case EINPROGRESS: return DRMP3_IN_PROGRESS; - #endif - #ifdef ESTALE - case ESTALE: return DRMP3_INVALID_FILE; - #endif - #ifdef EUCLEAN - case EUCLEAN: return DRMP3_ERROR; - #endif - #ifdef ENOTNAM - case ENOTNAM: return DRMP3_ERROR; - #endif - #ifdef ENAVAIL - case ENAVAIL: return DRMP3_ERROR; - #endif - #ifdef EISNAM - case EISNAM: return DRMP3_ERROR; - #endif - #ifdef EREMOTEIO - case EREMOTEIO: return DRMP3_IO_ERROR; - #endif - #ifdef EDQUOT - case EDQUOT: return DRMP3_NO_SPACE; - #endif - #ifdef ENOMEDIUM - case ENOMEDIUM: return DRMP3_DOES_NOT_EXIST; - #endif - #ifdef EMEDIUMTYPE - case EMEDIUMTYPE: return DRMP3_ERROR; - #endif - #ifdef ECANCELED - case ECANCELED: return DRMP3_CANCELLED; - #endif - #ifdef ENOKEY - case ENOKEY: return DRMP3_ERROR; - #endif - #ifdef EKEYEXPIRED - case EKEYEXPIRED: return DRMP3_ERROR; - #endif - #ifdef EKEYREVOKED - case EKEYREVOKED: return DRMP3_ERROR; - #endif - #ifdef EKEYREJECTED - case EKEYREJECTED: return DRMP3_ERROR; - #endif - #ifdef EOWNERDEAD - case EOWNERDEAD: return DRMP3_ERROR; - #endif - #ifdef ENOTRECOVERABLE - case ENOTRECOVERABLE: return DRMP3_ERROR; - #endif - #ifdef ERFKILL - case ERFKILL: return DRMP3_ERROR; - #endif - #ifdef EHWPOISON - case EHWPOISON: return DRMP3_ERROR; - #endif - default: return DRMP3_ERROR; - } -} -static drmp3_result drmp3_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode) -{ -#if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err; -#endif - if (ppFile != NULL) { - *ppFile = NULL; - } - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return DRMP3_INVALID_ARGS; - } -#if defined(_MSC_VER) && _MSC_VER >= 1400 - err = fopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return drmp3_result_from_errno(err); - } -#else -#if defined(_WIN32) || defined(__APPLE__) - *ppFile = fopen(pFilePath, pOpenMode); -#else - #if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE) - *ppFile = fopen64(pFilePath, pOpenMode); - #else - *ppFile = fopen(pFilePath, pOpenMode); - #endif -#endif - if (*ppFile == NULL) { - drmp3_result result = drmp3_result_from_errno(errno); - if (result == DRMP3_SUCCESS) { - result = DRMP3_ERROR; - } - return result; - } -#endif - return DRMP3_SUCCESS; -} -#if defined(_WIN32) - #if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS)) - #define DRMP3_HAS_WFOPEN - #endif -#endif -static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drmp3_allocation_callbacks* pAllocationCallbacks) -{ - if (ppFile != NULL) { - *ppFile = NULL; - } - if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) { - return DRMP3_INVALID_ARGS; - } -#if defined(DRMP3_HAS_WFOPEN) - { - #if defined(_MSC_VER) && _MSC_VER >= 1400 - errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode); - if (err != 0) { - return drmp3_result_from_errno(err); - } - #else - *ppFile = _wfopen(pFilePath, pOpenMode); - if (*ppFile == NULL) { - return drmp3_result_from_errno(errno); - } - #endif - (void)pAllocationCallbacks; - } -#else - #if defined(__DJGPP__) - { - } - #else - { - mbstate_t mbs; - size_t lenMB; - const wchar_t* pFilePathTemp = pFilePath; - char* pFilePathMB = NULL; - char pOpenModeMB[32] = {0}; - DRMP3_ZERO_OBJECT(&mbs); - lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs); - if (lenMB == (size_t)-1) { - return drmp3_result_from_errno(errno); - } - pFilePathMB = (char*)drmp3__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks); - if (pFilePathMB == NULL) { - return DRMP3_OUT_OF_MEMORY; - } - pFilePathTemp = pFilePath; - DRMP3_ZERO_OBJECT(&mbs); - wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs); - { - size_t i = 0; - for (;;) { - if (pOpenMode[i] == 0) { - pOpenModeMB[i] = '\0'; - break; - } - pOpenModeMB[i] = (char)pOpenMode[i]; - i += 1; - } - } - *ppFile = fopen(pFilePathMB, pOpenModeMB); - drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks); - } - #endif - if (*ppFile == NULL) { - return DRMP3_ERROR; - } -#endif - return DRMP3_SUCCESS; -} -static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) +static size_t ma_dr_mp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) { return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); } -static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin) +static ma_bool32 ma_dr_mp3__on_seek_stdio(void* pUserData, int offset, ma_dr_mp3_seek_origin origin) { - return fseek((FILE*)pUserData, offset, (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; + return fseek((FILE*)pUserData, offset, (origin == ma_dr_mp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; } -DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_mp3_init_file(ma_dr_mp3* pMP3, const char* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) { - drmp3_bool32 result; + ma_bool32 result; FILE* pFile; - if (drmp3_fopen(&pFile, pFilePath, "rb") != DRMP3_SUCCESS) { - return DRMP3_FALSE; + if (ma_fopen(&pFile, pFilePath, "rb") != MA_SUCCESS) { + return MA_FALSE; } - result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != DRMP3_TRUE) { + result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != MA_TRUE) { fclose(pFile); return result; } - return DRMP3_TRUE; + return MA_TRUE; } -DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks) +MA_API ma_bool32 ma_dr_mp3_init_file_w(ma_dr_mp3* pMP3, const wchar_t* pFilePath, const ma_allocation_callbacks* pAllocationCallbacks) { - drmp3_bool32 result; + ma_bool32 result; FILE* pFile; - if (drmp3_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != DRMP3_SUCCESS) { - return DRMP3_FALSE; + if (ma_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != MA_SUCCESS) { + return MA_FALSE; } - result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); - if (result != DRMP3_TRUE) { + result = ma_dr_mp3_init(pMP3, ma_dr_mp3__on_read_stdio, ma_dr_mp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks); + if (result != MA_TRUE) { fclose(pFile); return result; } - return DRMP3_TRUE; + return MA_TRUE; } #endif -DRMP3_API void drmp3_uninit(drmp3* pMP3) +MA_API void ma_dr_mp3_uninit(ma_dr_mp3* pMP3) { if (pMP3 == NULL) { return; } -#ifndef DR_MP3_NO_STDIO - if (pMP3->onRead == drmp3__on_read_stdio) { +#ifndef MA_DR_MP3_NO_STDIO + if (pMP3->onRead == ma_dr_mp3__on_read_stdio) { FILE* pFile = (FILE*)pMP3->pUserData; if (pFile != NULL) { fclose(pFile); @@ -92738,14 +91873,14 @@ DRMP3_API void drmp3_uninit(drmp3* pMP3) } } #endif - drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); + ma_dr_mp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); } -#if defined(DR_MP3_FLOAT_OUTPUT) -static void drmp3_f32_to_s16(drmp3_int16* dst, const float* src, drmp3_uint64 sampleCount) +#if defined(MA_DR_MP3_FLOAT_OUTPUT) +static void ma_dr_mp3_f32_to_s16(ma_int16* dst, const float* src, ma_uint64 sampleCount) { - drmp3_uint64 i; - drmp3_uint64 i4; - drmp3_uint64 sampleCount4; + ma_uint64 i; + ma_uint64 i4; + ma_uint64 sampleCount4; i = 0; sampleCount4 = sampleCount >> 2; for (i4 = 0; i4 < sampleCount4; i4 += 1) { @@ -92761,24 +91896,24 @@ static void drmp3_f32_to_s16(drmp3_int16* dst, const float* src, drmp3_uint64 sa x1 = x1 * 32767.0f; x2 = x2 * 32767.0f; x3 = x3 * 32767.0f; - dst[i+0] = (drmp3_int16)x0; - dst[i+1] = (drmp3_int16)x1; - dst[i+2] = (drmp3_int16)x2; - dst[i+3] = (drmp3_int16)x3; + dst[i+0] = (ma_int16)x0; + dst[i+1] = (ma_int16)x1; + dst[i+2] = (ma_int16)x2; + dst[i+3] = (ma_int16)x3; i += 4; } for (; i < sampleCount; i += 1) { float x = src[i]; x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); x = x * 32767.0f; - dst[i] = (drmp3_int16)x; + dst[i] = (ma_int16)x; } } #endif -#if !defined(DR_MP3_FLOAT_OUTPUT) -static void drmp3_s16_to_f32(float* dst, const drmp3_int16* src, drmp3_uint64 sampleCount) +#if !defined(MA_DR_MP3_FLOAT_OUTPUT) +static void ma_dr_mp3_s16_to_f32(float* dst, const ma_int16* src, ma_uint64 sampleCount) { - drmp3_uint64 i; + ma_uint64 i; for (i = 0; i < sampleCount; i += 1) { float x = (float)src[i]; x = x * 0.000030517578125f; @@ -92786,22 +91921,22 @@ static void drmp3_s16_to_f32(float* dst, const drmp3_int16* src, drmp3_uint64 sa } } #endif -static drmp3_uint64 drmp3_read_pcm_frames_raw(drmp3* pMP3, drmp3_uint64 framesToRead, void* pBufferOut) +static ma_uint64 ma_dr_mp3_read_pcm_frames_raw(ma_dr_mp3* pMP3, ma_uint64 framesToRead, void* pBufferOut) { - drmp3_uint64 totalFramesRead = 0; - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(pMP3->onRead != NULL); + ma_uint64 totalFramesRead = 0; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->onRead != NULL); while (framesToRead > 0) { - drmp3_uint32 framesToConsume = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); + ma_uint32 framesToConsume = (ma_uint32)MA_DR_MP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead); if (pBufferOut != NULL) { - #if defined(DR_MP3_FLOAT_OUTPUT) - float* pFramesOutF32 = (float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); - float* pFramesInF32 = (float*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); - DRMP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); + #if defined(MA_DR_MP3_FLOAT_OUTPUT) + float* pFramesOutF32 = (float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels); + float* pFramesInF32 = (float*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + MA_DR_MP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels); #else - drmp3_int16* pFramesOutS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalFramesRead * pMP3->channels); - drmp3_int16* pFramesInS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(drmp3_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); - DRMP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(drmp3_int16) * framesToConsume * pMP3->channels); + ma_int16* pFramesOutS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalFramesRead * pMP3->channels); + ma_int16* pFramesInS16 = (ma_int16*)MA_DR_MP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(ma_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels); + MA_DR_MP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(ma_int16) * framesToConsume * pMP3->channels); #endif } pMP3->currentPCMFrame += framesToConsume; @@ -92812,125 +91947,125 @@ static drmp3_uint64 drmp3_read_pcm_frames_raw(drmp3* pMP3, drmp3_uint64 framesTo if (framesToRead == 0) { break; } - DRMP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); - if (drmp3_decode_next_frame(pMP3) == 0) { + MA_DR_MP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0); + if (ma_dr_mp3_decode_next_frame(pMP3) == 0) { break; } } return totalFramesRead; } -DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut) +MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_f32(ma_dr_mp3* pMP3, ma_uint64 framesToRead, float* pBufferOut) { if (pMP3 == NULL || pMP3->onRead == NULL) { return 0; } -#if defined(DR_MP3_FLOAT_OUTPUT) - return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); +#if defined(MA_DR_MP3_FLOAT_OUTPUT) + return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); #else { - drmp3_int16 pTempS16[8192]; - drmp3_uint64 totalPCMFramesRead = 0; + ma_int16 pTempS16[8192]; + ma_uint64 totalPCMFramesRead = 0; while (totalPCMFramesRead < framesToRead) { - drmp3_uint64 framesJustRead; - drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead; - drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempS16) / pMP3->channels; + ma_uint64 framesJustRead; + ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead; + ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempS16) / pMP3->channels; if (framesToReadNow > framesRemaining) { framesToReadNow = framesRemaining; } - framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16); + framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16); if (framesJustRead == 0) { break; } - drmp3_s16_to_f32((float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels); + ma_dr_mp3_s16_to_f32((float*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels); totalPCMFramesRead += framesJustRead; } return totalPCMFramesRead; } #endif } -DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut) +MA_API ma_uint64 ma_dr_mp3_read_pcm_frames_s16(ma_dr_mp3* pMP3, ma_uint64 framesToRead, ma_int16* pBufferOut) { if (pMP3 == NULL || pMP3->onRead == NULL) { return 0; } -#if !defined(DR_MP3_FLOAT_OUTPUT) - return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); +#if !defined(MA_DR_MP3_FLOAT_OUTPUT) + return ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut); #else { float pTempF32[4096]; - drmp3_uint64 totalPCMFramesRead = 0; + ma_uint64 totalPCMFramesRead = 0; while (totalPCMFramesRead < framesToRead) { - drmp3_uint64 framesJustRead; - drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead; - drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempF32) / pMP3->channels; + ma_uint64 framesJustRead; + ma_uint64 framesRemaining = framesToRead - totalPCMFramesRead; + ma_uint64 framesToReadNow = MA_DR_MP3_COUNTOF(pTempF32) / pMP3->channels; if (framesToReadNow > framesRemaining) { framesToReadNow = framesRemaining; } - framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32); + framesJustRead = ma_dr_mp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32); if (framesJustRead == 0) { break; } - drmp3_f32_to_s16((drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels); + ma_dr_mp3_f32_to_s16((ma_int16*)MA_DR_MP3_OFFSET_PTR(pBufferOut, sizeof(ma_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels); totalPCMFramesRead += framesJustRead; } return totalPCMFramesRead; } #endif } -static void drmp3_reset(drmp3* pMP3) +static void ma_dr_mp3_reset(ma_dr_mp3* pMP3) { - DRMP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3 != NULL); pMP3->pcmFramesConsumedInMP3Frame = 0; pMP3->pcmFramesRemainingInMP3Frame = 0; pMP3->currentPCMFrame = 0; pMP3->dataSize = 0; - pMP3->atEnd = DRMP3_FALSE; - drmp3dec_init(&pMP3->decoder); + pMP3->atEnd = MA_FALSE; + ma_dr_mp3dec_init(&pMP3->decoder); } -static drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3) +static ma_bool32 ma_dr_mp3_seek_to_start_of_stream(ma_dr_mp3* pMP3) { - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(pMP3->onSeek != NULL); - if (!drmp3__on_seek(pMP3, 0, drmp3_seek_origin_start)) { - return DRMP3_FALSE; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->onSeek != NULL); + if (!ma_dr_mp3__on_seek(pMP3, 0, ma_dr_mp3_seek_origin_start)) { + return MA_FALSE; } - drmp3_reset(pMP3); - return DRMP3_TRUE; + ma_dr_mp3_reset(pMP3); + return MA_TRUE; } -static drmp3_bool32 drmp3_seek_forward_by_pcm_frames__brute_force(drmp3* pMP3, drmp3_uint64 frameOffset) +static ma_bool32 ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameOffset) { - drmp3_uint64 framesRead; -#if defined(DR_MP3_FLOAT_OUTPUT) - framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); + ma_uint64 framesRead; +#if defined(MA_DR_MP3_FLOAT_OUTPUT) + framesRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, frameOffset, NULL); #else - framesRead = drmp3_read_pcm_frames_s16(pMP3, frameOffset, NULL); + framesRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, frameOffset, NULL); #endif if (framesRead != frameOffset) { - return DRMP3_FALSE; + return MA_FALSE; } - return DRMP3_TRUE; + return MA_TRUE; } -static drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 frameIndex) +static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__brute_force(ma_dr_mp3* pMP3, ma_uint64 frameIndex) { - DRMP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3 != NULL); if (frameIndex == pMP3->currentPCMFrame) { - return DRMP3_TRUE; + return MA_TRUE; } if (frameIndex < pMP3->currentPCMFrame) { - if (!drmp3_seek_to_start_of_stream(pMP3)) { - return DRMP3_FALSE; + if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { + return MA_FALSE; } } - DRMP3_ASSERT(frameIndex >= pMP3->currentPCMFrame); - return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame)); + MA_DR_MP3_ASSERT(frameIndex >= pMP3->currentPCMFrame); + return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame)); } -static drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex, drmp3_uint32* pSeekPointIndex) +static ma_bool32 ma_dr_mp3_find_closest_seek_point(ma_dr_mp3* pMP3, ma_uint64 frameIndex, ma_uint32* pSeekPointIndex) { - drmp3_uint32 iSeekPoint; - DRMP3_ASSERT(pSeekPointIndex != NULL); + ma_uint32 iSeekPoint; + MA_DR_MP3_ASSERT(pSeekPointIndex != NULL); *pSeekPointIndex = 0; if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) { - return DRMP3_FALSE; + return MA_FALSE; } for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) { if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) { @@ -92938,18 +92073,18 @@ static drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 fram } *pSeekPointIndex = iSeekPoint; } - return DRMP3_TRUE; + return MA_TRUE; } -static drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frameIndex) +static ma_bool32 ma_dr_mp3_seek_to_pcm_frame__seek_table(ma_dr_mp3* pMP3, ma_uint64 frameIndex) { - drmp3_seek_point seekPoint; - drmp3_uint32 priorSeekPointIndex; - drmp3_uint16 iMP3Frame; - drmp3_uint64 leftoverFrames; - DRMP3_ASSERT(pMP3 != NULL); - DRMP3_ASSERT(pMP3->pSeekPoints != NULL); - DRMP3_ASSERT(pMP3->seekPointCount > 0); - if (drmp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) { + ma_dr_mp3_seek_point seekPoint; + ma_uint32 priorSeekPointIndex; + ma_uint16 iMP3Frame; + ma_uint64 leftoverFrames; + MA_DR_MP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3->pSeekPoints != NULL); + MA_DR_MP3_ASSERT(pMP3->seekPointCount > 0); + if (ma_dr_mp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) { seekPoint = pMP3->pSeekPoints[priorSeekPointIndex]; } else { seekPoint.seekPosInBytes = 0; @@ -92957,71 +92092,71 @@ static drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint6 seekPoint.mp3FramesToDiscard = 0; seekPoint.pcmFramesToDiscard = 0; } - if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, drmp3_seek_origin_start)) { - return DRMP3_FALSE; + if (!ma_dr_mp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, ma_dr_mp3_seek_origin_start)) { + return MA_FALSE; } - drmp3_reset(pMP3); + ma_dr_mp3_reset(pMP3); for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) { - drmp3_uint32 pcmFramesRead; - drmp3d_sample_t* pPCMFrames; + ma_uint32 pcmFramesRead; + ma_dr_mp3d_sample_t* pPCMFrames; pPCMFrames = NULL; if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) { - pPCMFrames = (drmp3d_sample_t*)pMP3->pcmFrames; + pPCMFrames = (ma_dr_mp3d_sample_t*)pMP3->pcmFrames; } - pcmFramesRead = drmp3_decode_next_frame_ex(pMP3, pPCMFrames); + pcmFramesRead = ma_dr_mp3_decode_next_frame_ex(pMP3, pPCMFrames); if (pcmFramesRead == 0) { - return DRMP3_FALSE; + return MA_FALSE; } } pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard; leftoverFrames = frameIndex - pMP3->currentPCMFrame; - return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames); + return ma_dr_mp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames); } -DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex) +MA_API ma_bool32 ma_dr_mp3_seek_to_pcm_frame(ma_dr_mp3* pMP3, ma_uint64 frameIndex) { if (pMP3 == NULL || pMP3->onSeek == NULL) { - return DRMP3_FALSE; + return MA_FALSE; } if (frameIndex == 0) { - return drmp3_seek_to_start_of_stream(pMP3); + return ma_dr_mp3_seek_to_start_of_stream(pMP3); } if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) { - return drmp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex); + return ma_dr_mp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex); } else { - return drmp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex); + return ma_dr_mp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex); } } -DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount) +MA_API ma_bool32 ma_dr_mp3_get_mp3_and_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint64* pMP3FrameCount, ma_uint64* pPCMFrameCount) { - drmp3_uint64 currentPCMFrame; - drmp3_uint64 totalPCMFrameCount; - drmp3_uint64 totalMP3FrameCount; + ma_uint64 currentPCMFrame; + ma_uint64 totalPCMFrameCount; + ma_uint64 totalMP3FrameCount; if (pMP3 == NULL) { - return DRMP3_FALSE; + return MA_FALSE; } if (pMP3->onSeek == NULL) { - return DRMP3_FALSE; + return MA_FALSE; } currentPCMFrame = pMP3->currentPCMFrame; - if (!drmp3_seek_to_start_of_stream(pMP3)) { - return DRMP3_FALSE; + if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { + return MA_FALSE; } totalPCMFrameCount = 0; totalMP3FrameCount = 0; for (;;) { - drmp3_uint32 pcmFramesInCurrentMP3Frame; - pcmFramesInCurrentMP3Frame = drmp3_decode_next_frame_ex(pMP3, NULL); + ma_uint32 pcmFramesInCurrentMP3Frame; + pcmFramesInCurrentMP3Frame = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); if (pcmFramesInCurrentMP3Frame == 0) { break; } totalPCMFrameCount += pcmFramesInCurrentMP3Frame; totalMP3FrameCount += 1; } - if (!drmp3_seek_to_start_of_stream(pMP3)) { - return DRMP3_FALSE; + if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { + return MA_FALSE; } - if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { - return DRMP3_FALSE; + if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { + return MA_FALSE; } if (pMP3FrameCount != NULL) { *pMP3FrameCount = totalMP3FrameCount; @@ -93029,89 +92164,89 @@ DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint if (pPCMFrameCount != NULL) { *pPCMFrameCount = totalPCMFrameCount; } - return DRMP3_TRUE; + return MA_TRUE; } -DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3) +MA_API ma_uint64 ma_dr_mp3_get_pcm_frame_count(ma_dr_mp3* pMP3) { - drmp3_uint64 totalPCMFrameCount; - if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { + ma_uint64 totalPCMFrameCount; + if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) { return 0; } return totalPCMFrameCount; } -DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3) +MA_API ma_uint64 ma_dr_mp3_get_mp3_frame_count(ma_dr_mp3* pMP3) { - drmp3_uint64 totalMP3FrameCount; - if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) { + ma_uint64 totalMP3FrameCount; + if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) { return 0; } return totalMP3FrameCount; } -static void drmp3__accumulate_running_pcm_frame_count(drmp3* pMP3, drmp3_uint32 pcmFrameCountIn, drmp3_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart) +static void ma_dr_mp3__accumulate_running_pcm_frame_count(ma_dr_mp3* pMP3, ma_uint32 pcmFrameCountIn, ma_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart) { float srcRatio; float pcmFrameCountOutF; - drmp3_uint32 pcmFrameCountOut; + ma_uint32 pcmFrameCountOut; srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate; - DRMP3_ASSERT(srcRatio > 0); + MA_DR_MP3_ASSERT(srcRatio > 0); pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio); - pcmFrameCountOut = (drmp3_uint32)pcmFrameCountOutF; + pcmFrameCountOut = (ma_uint32)pcmFrameCountOutF; *pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut; *pRunningPCMFrameCount += pcmFrameCountOut; } typedef struct { - drmp3_uint64 bytePos; - drmp3_uint64 pcmFrameIndex; -} drmp3__seeking_mp3_frame_info; -DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints) + ma_uint64 bytePos; + ma_uint64 pcmFrameIndex; +} ma_dr_mp3__seeking_mp3_frame_info; +MA_API ma_bool32 ma_dr_mp3_calculate_seek_points(ma_dr_mp3* pMP3, ma_uint32* pSeekPointCount, ma_dr_mp3_seek_point* pSeekPoints) { - drmp3_uint32 seekPointCount; - drmp3_uint64 currentPCMFrame; - drmp3_uint64 totalMP3FrameCount; - drmp3_uint64 totalPCMFrameCount; + ma_uint32 seekPointCount; + ma_uint64 currentPCMFrame; + ma_uint64 totalMP3FrameCount; + ma_uint64 totalPCMFrameCount; if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) { - return DRMP3_FALSE; + return MA_FALSE; } seekPointCount = *pSeekPointCount; if (seekPointCount == 0) { - return DRMP3_FALSE; + return MA_FALSE; } currentPCMFrame = pMP3->currentPCMFrame; - if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) { - return DRMP3_FALSE; + if (!ma_dr_mp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) { + return MA_FALSE; } - if (totalMP3FrameCount < DRMP3_SEEK_LEADING_MP3_FRAMES+1) { + if (totalMP3FrameCount < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1) { seekPointCount = 1; pSeekPoints[0].seekPosInBytes = 0; pSeekPoints[0].pcmFrameIndex = 0; pSeekPoints[0].mp3FramesToDiscard = 0; pSeekPoints[0].pcmFramesToDiscard = 0; } else { - drmp3_uint64 pcmFramesBetweenSeekPoints; - drmp3__seeking_mp3_frame_info mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES+1]; - drmp3_uint64 runningPCMFrameCount = 0; + ma_uint64 pcmFramesBetweenSeekPoints; + ma_dr_mp3__seeking_mp3_frame_info mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1]; + ma_uint64 runningPCMFrameCount = 0; float runningPCMFrameCountFractionalPart = 0; - drmp3_uint64 nextTargetPCMFrame; - drmp3_uint32 iMP3Frame; - drmp3_uint32 iSeekPoint; + ma_uint64 nextTargetPCMFrame; + ma_uint32 iMP3Frame; + ma_uint32 iSeekPoint; if (seekPointCount > totalMP3FrameCount-1) { - seekPointCount = (drmp3_uint32)totalMP3FrameCount-1; + seekPointCount = (ma_uint32)totalMP3FrameCount-1; } pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1); - if (!drmp3_seek_to_start_of_stream(pMP3)) { - return DRMP3_FALSE; + if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { + return MA_FALSE; } - for (iMP3Frame = 0; iMP3Frame < DRMP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) { - drmp3_uint32 pcmFramesInCurrentMP3FrameIn; - DRMP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize); + for (iMP3Frame = 0; iMP3Frame < MA_DR_MP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) { + ma_uint32 pcmFramesInCurrentMP3FrameIn; + MA_DR_MP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize); mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize; mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount; - pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); + pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); if (pcmFramesInCurrentMP3FrameIn == 0) { - return DRMP3_FALSE; + return MA_FALSE; } - drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); + ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); } nextTargetPCMFrame = 0; for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) { @@ -93120,43 +92255,43 @@ DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pS if (nextTargetPCMFrame < runningPCMFrameCount) { pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; - pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES; - pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); + pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES; + pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); break; } else { size_t i; - drmp3_uint32 pcmFramesInCurrentMP3FrameIn; - for (i = 0; i < DRMP3_COUNTOF(mp3FrameInfo)-1; ++i) { + ma_uint32 pcmFramesInCurrentMP3FrameIn; + for (i = 0; i < MA_DR_MP3_COUNTOF(mp3FrameInfo)-1; ++i) { mp3FrameInfo[i] = mp3FrameInfo[i+1]; } - mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize; - mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount; - pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL); + mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize; + mp3FrameInfo[MA_DR_MP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount; + pcmFramesInCurrentMP3FrameIn = ma_dr_mp3_decode_next_frame_ex(pMP3, NULL); if (pcmFramesInCurrentMP3FrameIn == 0) { pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos; pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame; - pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES; - pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); + pSeekPoints[iSeekPoint].mp3FramesToDiscard = MA_DR_MP3_SEEK_LEADING_MP3_FRAMES; + pSeekPoints[iSeekPoint].pcmFramesToDiscard = (ma_uint16)(nextTargetPCMFrame - mp3FrameInfo[MA_DR_MP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex); break; } - drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); + ma_dr_mp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart); } } } - if (!drmp3_seek_to_start_of_stream(pMP3)) { - return DRMP3_FALSE; + if (!ma_dr_mp3_seek_to_start_of_stream(pMP3)) { + return MA_FALSE; } - if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { - return DRMP3_FALSE; + if (!ma_dr_mp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) { + return MA_FALSE; } } *pSeekPointCount = seekPointCount; - return DRMP3_TRUE; + return MA_TRUE; } -DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints) +MA_API ma_bool32 ma_dr_mp3_bind_seek_table(ma_dr_mp3* pMP3, ma_uint32 seekPointCount, ma_dr_mp3_seek_point* pSeekPoints) { if (pMP3 == NULL) { - return DRMP3_FALSE; + return MA_FALSE; } if (seekPointCount == 0 || pSeekPoints == NULL) { pMP3->seekPointCount = 0; @@ -93165,25 +92300,25 @@ DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPoint pMP3->seekPointCount = seekPointCount; pMP3->pSeekPoints = pSeekPoints; } - return DRMP3_TRUE; + return MA_TRUE; } -static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) +static float* ma_dr_mp3__full_read_and_close_f32(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount) { - drmp3_uint64 totalFramesRead = 0; - drmp3_uint64 framesCapacity = 0; + ma_uint64 totalFramesRead = 0; + ma_uint64 framesCapacity = 0; float* pFrames = NULL; float temp[4096]; - DRMP3_ASSERT(pMP3 != NULL); + MA_DR_MP3_ASSERT(pMP3 != NULL); for (;;) { - drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels; - drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp); + ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels; + ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp); if (framesJustRead == 0) { break; } if (framesCapacity < totalFramesRead + framesJustRead) { - drmp3_uint64 oldFramesBufferSize; - drmp3_uint64 newFramesBufferSize; - drmp3_uint64 newFramesCap; + ma_uint64 oldFramesBufferSize; + ma_uint64 newFramesBufferSize; + ma_uint64 newFramesCap; float* pNewFrames; newFramesCap = framesCapacity * 2; if (newFramesCap < totalFramesRead + framesJustRead) { @@ -93191,18 +92326,18 @@ static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, } oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float); newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float); - if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) { + if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) { break; } - pNewFrames = (float*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); + pNewFrames = (float*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); if (pNewFrames == NULL) { - drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); + ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); break; } pFrames = pNewFrames; framesCapacity = newFramesCap; } - DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float))); + MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float))); totalFramesRead += framesJustRead; if (framesJustRead != framesToReadRightNow) { break; @@ -93212,48 +92347,48 @@ static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, pConfig->channels = pMP3->channels; pConfig->sampleRate = pMP3->sampleRate; } - drmp3_uninit(pMP3); + ma_dr_mp3_uninit(pMP3); if (pTotalFrameCount) { *pTotalFrameCount = totalFramesRead; } return pFrames; } -static drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount) +static ma_int16* ma_dr_mp3__full_read_and_close_s16(ma_dr_mp3* pMP3, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount) { - drmp3_uint64 totalFramesRead = 0; - drmp3_uint64 framesCapacity = 0; - drmp3_int16* pFrames = NULL; - drmp3_int16 temp[4096]; - DRMP3_ASSERT(pMP3 != NULL); + ma_uint64 totalFramesRead = 0; + ma_uint64 framesCapacity = 0; + ma_int16* pFrames = NULL; + ma_int16 temp[4096]; + MA_DR_MP3_ASSERT(pMP3 != NULL); for (;;) { - drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels; - drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp); + ma_uint64 framesToReadRightNow = MA_DR_MP3_COUNTOF(temp) / pMP3->channels; + ma_uint64 framesJustRead = ma_dr_mp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp); if (framesJustRead == 0) { break; } if (framesCapacity < totalFramesRead + framesJustRead) { - drmp3_uint64 newFramesBufferSize; - drmp3_uint64 oldFramesBufferSize; - drmp3_uint64 newFramesCap; - drmp3_int16* pNewFrames; + ma_uint64 newFramesBufferSize; + ma_uint64 oldFramesBufferSize; + ma_uint64 newFramesCap; + ma_int16* pNewFrames; newFramesCap = framesCapacity * 2; if (newFramesCap < totalFramesRead + framesJustRead) { newFramesCap = totalFramesRead + framesJustRead; } - oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(drmp3_int16); - newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(drmp3_int16); - if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) { + oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(ma_int16); + newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(ma_int16); + if (newFramesBufferSize > (ma_uint64)MA_SIZE_MAX) { break; } - pNewFrames = (drmp3_int16*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); + pNewFrames = (ma_int16*)ma_dr_mp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks); if (pNewFrames == NULL) { - drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); + ma_dr_mp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks); break; } pFrames = pNewFrames; framesCapacity = newFramesCap; } - DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(drmp3_int16))); + MA_DR_MP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(ma_int16))); totalFramesRead += framesJustRead; if (framesJustRead != framesToReadRightNow) { break; @@ -93263,81 +92398,81 @@ static drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pC pConfig->channels = pMP3->channels; pConfig->sampleRate = pMP3->sampleRate; } - drmp3_uninit(pMP3); + ma_dr_mp3_uninit(pMP3); if (pTotalFrameCount) { *pTotalFrameCount = totalFramesRead; } return pFrames; } -DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +MA_API float* ma_dr_mp3_open_and_read_pcm_frames_f32(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drmp3 mp3; - if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } - return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); + return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); } -DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int16* ma_dr_mp3_open_and_read_pcm_frames_s16(ma_dr_mp3_read_proc onRead, ma_dr_mp3_seek_proc onSeek, void* pUserData, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drmp3 mp3; - if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) { return NULL; } - return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); + return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); } -DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +MA_API float* ma_dr_mp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drmp3 mp3; - if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { return NULL; } - return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); + return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); } -DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int16* ma_dr_mp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drmp3 mp3; - if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) { return NULL; } - return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); + return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); } -#ifndef DR_MP3_NO_STDIO -DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +#ifndef MA_DR_MP3_NO_STDIO +MA_API float* ma_dr_mp3_open_file_and_read_pcm_frames_f32(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drmp3 mp3; - if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) { + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) { return NULL; } - return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); + return ma_dr_mp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount); } -DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks) +MA_API ma_int16* ma_dr_mp3_open_file_and_read_pcm_frames_s16(const char* filePath, ma_dr_mp3_config* pConfig, ma_uint64* pTotalFrameCount, const ma_allocation_callbacks* pAllocationCallbacks) { - drmp3 mp3; - if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) { + ma_dr_mp3 mp3; + if (!ma_dr_mp3_init_file(&mp3, filePath, pAllocationCallbacks)) { return NULL; } - return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); + return ma_dr_mp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount); } #endif -DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks) +MA_API void* ma_dr_mp3_malloc(size_t sz, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { - return drmp3__malloc_from_callbacks(sz, pAllocationCallbacks); + return ma_dr_mp3__malloc_from_callbacks(sz, pAllocationCallbacks); } else { - return drmp3__malloc_default(sz, NULL); + return ma_dr_mp3__malloc_default(sz, NULL); } } -DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks) +MA_API void ma_dr_mp3_free(void* p, const ma_allocation_callbacks* pAllocationCallbacks) { if (pAllocationCallbacks != NULL) { - drmp3__free_from_callbacks(p, pAllocationCallbacks); + ma_dr_mp3__free_from_callbacks(p, pAllocationCallbacks); } else { - drmp3__free_default(p, NULL); + ma_dr_mp3__free_default(p, NULL); } } #endif /* dr_mp3_c end */ -#endif /* DRMP3_IMPLEMENTATION */ +#endif /* MA_DR_MP3_IMPLEMENTATION */ #endif /* MA_NO_MP3 */ From 7eb49d1c7b92efdfd5ebe8680776967197888121 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 22 Sep 2023 11:57:49 +0200 Subject: [PATCH 0293/1350] EXTERNAL: msf_gif.h, reviewed some warnings --- src/external/msf_gif.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/external/msf_gif.h b/src/external/msf_gif.h index 9374c8b84..bc2c6edef 100644 --- a/src/external/msf_gif.h +++ b/src/external/msf_gif.h @@ -256,16 +256,16 @@ static void msf_cook_frame(MsfCookedFrame * frame, uint8_t * raw, uint8_t * used int width, int height, int pitch, int depth) { MsfTimeFunc //bit depth for each channel - const static int rdepthsArray[17] = { 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5 }; - const static int gdepthsArray[17] = { 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6 }; - const static int bdepthsArray[17] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5 }; + static const int rdepthsArray[17] = { 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5 }; + static const int gdepthsArray[17] = { 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6 }; + static const int bdepthsArray[17] = { 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5 }; //this extra level of indirection looks unnecessary but we need to explicitly decay the arrays to pointers //in order to be able to swap them because of C's annoying not-quite-pointers, not-quite-value-types stack arrays. const int * rdepths = msf_gif_bgra_flag? bdepthsArray : rdepthsArray; const int * gdepths = gdepthsArray; const int * bdepths = msf_gif_bgra_flag? rdepthsArray : bdepthsArray; - const static int ditherKernel[16] = { + static const int ditherKernel[16] = { 0 << 12, 8 << 12, 2 << 12, 10 << 12, 12 << 12, 4 << 12, 14 << 12, 6 << 12, 3 << 12, 11 << 12, 1 << 12, 9 << 12, @@ -404,7 +404,7 @@ static MsfGifBuffer * msf_compress_frame(void * allocContext, int width, int hei MsfGifBuffer * buffer = (MsfGifBuffer *) MSF_GIF_MALLOC(allocContext, maxBufSize); if (!buffer) { return NULL; } uint8_t * writeHead = buffer->data; - MsfStridedList lzw = { lzwMem }; + MsfStridedList lzw = { lzwMem, 0, 0 }; //allocate tlb int totalBits = frame.rbits + frame.gbits + frame.bbits; From f27ea1f0c874b5bbae5f83de423256cc141aebc6 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 22 Sep 2023 11:58:14 +0200 Subject: [PATCH 0294/1350] REVIEWED: `IsGestureDetected()` parameter type --- src/raylib.h | 2 +- src/rgestures.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 98e5c48a0..635b3dfb7 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1163,7 +1163,7 @@ RLAPI int GetTouchPointCount(void); // Get number of t // Gestures and Touch Handling Functions (Module: rgestures) //------------------------------------------------------------------------------------ RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags -RLAPI bool IsGestureDetected(int gesture); // Check if a gesture have been detected +RLAPI bool IsGestureDetected(unsigned int gesture); // Check if a gesture have been detected RLAPI int GetGestureDetected(void); // Get latest detected gesture RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector diff --git a/src/rgestures.h b/src/rgestures.h index 779dbed56..9161b7484 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -255,7 +255,7 @@ void SetGesturesEnabled(unsigned int flags) } // Check if a gesture have been detected -bool IsGestureDetected(int gesture) +bool IsGestureDetected(unsigned int gesture) { if ((GESTURES.enabledFlags & GESTURES.current) == gesture) return true; else return false; From 83d82b6697204efa4601455ebb4beaef609bfc86 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 22 Sep 2023 11:58:24 +0200 Subject: [PATCH 0295/1350] Update rmodels.c --- src/rmodels.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 1f8568625..f9ceec137 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5246,7 +5246,7 @@ static bool GetPoseAtTimeGLTF(cgltf_accessor *input, cgltf_accessor *output, flo float tend = 0.0f; int keyframe = 0; // Defaults to first pose - for (int i = 0; i < input->count - 1; i++) + for (int i = 0; i < (int)input->count - 1; i++) { cgltf_bool r1 = cgltf_accessor_read_float(input, i, &tstart, 1); if (!r1) return false; @@ -5722,7 +5722,7 @@ static Model LoadM3D(const char *fileName) int skinid = m3d->vertex[m3d->face[i].vertex[n]].skinid; // Check if there is a skin for this mesh, should be, just failsafe - if (skinid != M3D_UNDEF && skinid < (int)m3d->numskin) + if ((skinid != M3D_UNDEF) && (skinid < (int)m3d->numskin)) { for (j = 0; j < 4; j++) { From a3a5aa7c639410f4ff8ed776d590f952653f5ed1 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 22 Sep 2023 11:58:53 +0200 Subject: [PATCH 0296/1350] REVIEWED: `LoadFileData()` potential issues with dataSize --- src/utils.c | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/utils.c b/src/utils.c index 27747b757..51d84cfe4 100644 --- a/src/utils.c +++ b/src/utils.c @@ -200,7 +200,7 @@ unsigned char *LoadFileData(const char *fileName, int *dataSize) // WARNING: On binary streams SEEK_END could not be found, // using fseek() and ftell() could not work in some (rare) cases fseek(file, 0, SEEK_END); - int size = ftell(file); + int size = ftell(file); // WARNING: ftell() returns 'long int', maximum size returned is INT_MAX (2147483647 bytes) fseek(file, 0, SEEK_SET); if (size > 0) @@ -210,11 +210,24 @@ unsigned char *LoadFileData(const char *fileName, int *dataSize) if (data != NULL) { // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements] - unsigned int count = (unsigned int)fread(data, sizeof(unsigned char), size, file); - *dataSize = count; + size_t count = fread(data, sizeof(unsigned char), size, file); + + // WARNING: fread() returns a size_t value, usually 'unsigned int' (32bit compilation) and 'unsigned long long' (64bit compilation) + // dataSize is unified along raylib as a 'int' type, so, for file-sizes > INT_MAX (2147483647 bytes) we have a limitation + if (count > 2147483647) + { + TRACELOG(LOG_WARNING, "FILEIO: [%s] File is bigger than 2147483647 bytes, avoid using LoadFileData()", fileName); + + RL_FREE(data); + data = NULL; + } + else + { + *dataSize = (int)count; - if (count != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded", fileName); - else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName); + if ((*dataSize) != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded (%i bytes out of %i)", fileName, dataSize, count); + else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName); + } } else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to allocated memory for file reading", fileName); } @@ -254,7 +267,9 @@ bool SaveFileData(const char *fileName, void *data, int dataSize) if (file != NULL) { - unsigned int count = (unsigned int)fwrite(data, sizeof(unsigned char), dataSize, file); + // WARNING: fwrite() returns a size_t value, usually 'unsigned int' (32bit compilation) and 'unsigned long long' (64bit compilation) + // and expects a size_t input value but as dataSize is limited to INT_MAX (2147483647 bytes), there shouldn't be a problem + int count = (int)fwrite(data, sizeof(unsigned char), dataSize, file); if (count == 0) TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to write file", fileName); else if (count != dataSize) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially written", fileName); From 557aeff253255a7ce2aac3b1872aeefba6ff2a51 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 22 Sep 2023 11:59:52 +0200 Subject: [PATCH 0297/1350] REVIEWED: `glInternalFormat` is unsigned and actually, `rlGetGlTextureFormats()` returns 0 if fails --- src/rlgl.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 9ed484408..bbf52db25 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3012,7 +3012,7 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, TRACELOGD("TEXTURE: Load mipmap level %i (%i x %i), size: %i, offset: %i", i, mipWidth, mipHeight, mipSize, mipOffset); - if (glInternalFormat != -1) + if (glInternalFormat != 0) { if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) glTexImage2D(GL_TEXTURE_2D, i, glInternalFormat, mipWidth, mipHeight, 0, glFormat, glType, dataPtr); #if !defined(GRAPHICS_API_OPENGL_11) @@ -3166,7 +3166,7 @@ unsigned int rlLoadTextureCubemap(const void *data, int size, int format) unsigned int glInternalFormat, glFormat, glType; rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); - if (glInternalFormat != -1) + if (glInternalFormat != 0) { // Load cubemap faces for (unsigned int i = 0; i < 6; i++) @@ -3234,7 +3234,7 @@ void rlUpdateTexture(unsigned int id, int offsetX, int offsetY, int width, int h unsigned int glInternalFormat, glFormat, glType; rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); - if ((glInternalFormat != -1) && (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)) + if ((glInternalFormat != 0) && (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)) { glTexSubImage2D(GL_TEXTURE_2D, 0, offsetX, offsetY, width, height, glFormat, glType, data); } @@ -3378,7 +3378,7 @@ void *rlReadTexturePixels(unsigned int id, int width, int height, int format) rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); unsigned int size = rlGetPixelDataSize(width, height, format); - if ((glInternalFormat != -1) && (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)) + if ((glInternalFormat != 0) && (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB)) { pixels = RL_MALLOC(size); glGetTexImage(GL_TEXTURE_2D, 0, glFormat, glType, pixels); From c8a6093d52ff6bdd17f9df012f6666b74629f595 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 23 Sep 2023 11:13:11 +0200 Subject: [PATCH 0298/1350] Update examples creation requirements --- examples/examples_template.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/examples/examples_template.c b/examples/examples_template.c index 2b962d319..5cf0b98d8 100644 --- a/examples/examples_template.c +++ b/examples/examples_template.c @@ -35,6 +35,23 @@ 9. In case of additional information is required, just come to raylib Discord channel: example-contributions 10. Have fun! + + The following files should be updated when adding a new example, it's planned to create some + script to automatize this process but not available yet. + + - raylib/examples//_example_name.c + - raylib/examples//_example_name.png + - raylib/examples//resources/*.* + - raylib/examples/Makefile + - raylib/examples/Makefile.Web + - raylib/examples/README.md + - raylib/projects/VS2022/examples/_example_name.vcxproj + - raylib/projects/VS2022/raylib.sln + - raylib.com/common/examples.js + - raylib.com/examples//_example_name.html + - raylib.com/examples//_example_name.data + - raylib.com/examples//_example_name.wasm + - raylib.com/examples//_example_name.js */ /******************************************************************************************* From f7c3035b8c5f7b86165ba24dfa868a4527e48a2a Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 24 Sep 2023 23:51:23 +0200 Subject: [PATCH 0299/1350] Update raygui.h --- examples/shapes/raygui.h | 1389 +++++++++++++++++++++++++++----------- 1 file changed, 985 insertions(+), 404 deletions(-) diff --git a/examples/shapes/raygui.h b/examples/shapes/raygui.h index 63a27fa90..26d6bacb1 100644 --- a/examples/shapes/raygui.h +++ b/examples/shapes/raygui.h @@ -1,6 +1,6 @@ /******************************************************************************************* * -* raygui v4.0-dev - A simple and easy-to-use immediate-mode gui library +* raygui v4.0 - A simple and easy-to-use immediate-mode gui library * * DESCRIPTION: * raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also @@ -10,20 +10,26 @@ * - Immediate-mode gui, minimal retained data * - +25 controls provided (basic and advanced) * - Styling system for colors, font and metrics -* - Icons supported, embeds a complete 1-bit icons pack -* - Standalone usage mode option (custom graphics backends) -* - Multiple tools provided for raygui development +* - Icons supported, embedded as a 1-bit icons pack +* - Standalone mode option (custom input/graphics backend) +* - Multiple support tools provided for raygui development * * POSSIBLE IMPROVEMENTS: -* - Redesign functions that require a value as parameter to be returned, pass by reference * - Better standalone mode API for easy plug of custom backends -* - Externalize required inputs in some way, allow user customization +* - Externalize required inputs, allow user easier customization * * LIMITATIONS: -* - No multi-line word-wraped text box support -* - No auto-layout mechanism provided, up to the user to define controls position and size +* - No editable multi-line word-wraped text box supported +* - No auto-layout mechanism, up to the user to define controls position and size * - Standalone mode requires library modification and some user work to plug another backend * +* NOTES: +* - WARNING: GuiLoadStyle() and GuiLoadStyle{Custom}() functions, allocate memory for +* font atlas recs and glyphs, freeing that memory is (usually) up to the user, +* no unload function is explicitly provided... but note that GuiLoadStyleDefaulf() unloads +* by default any previously loaded font (texture, recs, glyphs). +* - Global UI alpha (guiAlpha) is applied inside GuiDrawRectangle() and GuiDrawText() functions +* * CONTROLS PROVIDED: * # Container/separators Controls * - WindowBox --> StatusBar, Panel @@ -31,13 +37,15 @@ * - Line * - Panel --> StatusBar * - ScrollPanel --> StatusBar +* - TabBar --> Button * * # Basic Controls * - Label -* - Button * - LabelButton --> Label +* - Button * - Toggle * - ToggleGroup --> Toggle +* - ToggleSlider * - CheckBox * - ComboBox * - DropdownBox @@ -126,11 +134,24 @@ * Includes custom ricons.h header defining a set of custom icons, * this file can be generated using rGuiIcons tool * +* #define RAYGUI_DEBUG_RECS_BOUNDS +* Draw control bounds rectangles for debug +* * #define RAYGUI_DEBUG_TEXT_BOUNDS * Draw text bounds rectangles for debug * * VERSIONS HISTORY: -* 4.0 (xx-Jun-2023) REDESIGNED: GuiScrollPanel(), get parameters by reference and return result value +* 4.0 (12-Sep-2023) ADDED: GuiToggleSlider() +* ADDED: GuiColorPickerHSV() and GuiColorPanelHSV() +* ADDED: Multiple new icons, mostly compiler related +* ADDED: New DEFAULT properties: TEXT_LINE_SPACING, TEXT_ALIGNMENT_VERTICAL, TEXT_WRAP_MODE +* ADDED: New enum values: GuiTextAlignment, GuiTextAlignmentVertical, GuiTextWrapMode +* ADDED: Support loading styles with custom font charset from external file +* REDESIGNED: GuiTextBox(), support mouse cursor positioning +* REDESIGNED: GuiDrawText(), support multiline and word-wrap modes (read only) +* REDESIGNED: GuiProgressBar() to be more visual, progress affects border color +* REDESIGNED: Global alpha consideration moved to GuiDrawRectangle() and GuiDrawText() +* REDESIGNED: GuiScrollPanel(), get parameters by reference and return result value * REDESIGNED: GuiToggleGroup(), get parameters by reference and return result value * REDESIGNED: GuiComboBox(), get parameters by reference and return result value * REDESIGNED: GuiCheckBox(), get parameters by reference and return result value @@ -146,6 +167,10 @@ * REDESIGNED: GuiGrid(), added extra parameter * REDESIGNED: GuiListViewEx(), change parameters order * REDESIGNED: All controls return result as int value +* REVIEWED: GuiScrollPanel() to avoid smallish scroll-bars +* REVIEWED: All examples and specially controls_test_suite +* RENAMED: gui_file_dialog module to gui_window_file_dialog +* UPDATED: All styles to include ISO-8859-15 charset (as much as possible) * * 3.6 (10-May-2023) ADDED: New icon: SAND_TIMER * ADDED: GuiLoadStyleFromMemory() (binary only) @@ -221,17 +246,43 @@ * 0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria. * * DEPENDENCIES: -* raylib 4.5 Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing +* raylib 4.6-dev Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing * +* STANDALONE MODE: * By default raygui depends on raylib mostly for the inputs and the drawing functionality but that dependency can be disabled * with the config flag RAYGUI_STANDALONE. In that case is up to the user to provide another backend to cover library needs. * +* The following functions should be redefined for a custom backend: +* +* - Vector2 GetMousePosition(void); +* - float GetMouseWheelMove(void); +* - bool IsMouseButtonDown(int button); +* - bool IsMouseButtonPressed(int button); +* - bool IsMouseButtonReleased(int button); +* - bool IsKeyDown(int key); +* - bool IsKeyPressed(int key); +* - int GetCharPressed(void); // -- GuiTextBox(), GuiValueBox() +* +* - void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle() +* - void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker() +* +* - Font GetFontDefault(void); // -- GuiLoadStyleDefault() +* - Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // -- GuiLoadStyle() +* - Texture2D LoadTextureFromImage(Image image); // -- GuiLoadStyle(), required to load texture from embedded font atlas image +* - void SetShapesTexture(Texture2D tex, Rectangle rec); // -- GuiLoadStyle(), required to set shapes rec to font white rec (optimization) +* - char *LoadFileText(const char *fileName); // -- GuiLoadStyle(), required to load charset data +* - void UnloadFileText(char *text); // -- GuiLoadStyle(), required to unload charset data +* - const char *GetDirectoryPath(const char *filePath); // -- GuiLoadStyle(), required to find charset/font file from text .rgs +* - int *LoadCodepoints(const char *text, int *count); // -- GuiLoadStyle(), required to load required font codepoints list +* - void UnloadCodepoints(int *codepoints); // -- GuiLoadStyle(), required to unload codepoints list +* - unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // -- GuiLoadStyle() +* * CONTRIBUTORS: * Ramon Santamaria: Supervision, review, redesign, update and maintenance * Vlad Adrian: Complete rewrite of GuiTextBox() to support extended features (2019) * Sergio Martinez: Review, testing (2015) and redesign of multiple controls (2018) -* Adria Arranz: Testing and Implementation of additional controls (2018) -* Jordi Jorba: Testing and Implementation of additional controls (2018) +* Adria Arranz: Testing and implementation of additional controls (2018) +* Jordi Jorba: Testing and implementation of additional controls (2018) * Albert Martos: Review and testing of the library (2015) * Ian Eito: Review and testing of the library (2015) * Kevin Gato: Initial implementation of basic components (2014) @@ -265,7 +316,7 @@ #define RAYGUI_VERSION_MAJOR 4 #define RAYGUI_VERSION_MINOR 0 #define RAYGUI_VERSION_PATCH 0 -#define RAYGUI_VERSION "4.0-dev" +#define RAYGUI_VERSION "4.0" #if !defined(RAYGUI_STANDALONE) #include "raylib.h" @@ -390,37 +441,69 @@ } Font; #endif + // Style property +// NOTE: Used when exporting style as code for convenience typedef struct GuiStyleProp { - unsigned short controlId; - unsigned short propertyId; - unsigned int propertyValue; + unsigned short controlId; // Control identifier + unsigned short propertyId; // Property identifier + int propertyValue; // Property value } GuiStyleProp; +/* +// Controls text style -NOT USED- +// NOTE: Text style is defined by control +typedef struct GuiTextStyle { + unsigned int size; + int charSpacing; + int lineSpacing; + int alignmentH; + int alignmentV; + int padding; +} GuiTextStyle; +*/ + // Gui control state typedef enum { STATE_NORMAL = 0, STATE_FOCUSED, STATE_PRESSED, - STATE_DISABLED, + STATE_DISABLED } GuiState; // Gui control text alignment typedef enum { TEXT_ALIGN_LEFT = 0, TEXT_ALIGN_CENTER, - TEXT_ALIGN_RIGHT, + TEXT_ALIGN_RIGHT } GuiTextAlignment; +// Gui control text alignment vertical +// NOTE: Text vertical position inside the text bounds +typedef enum { + TEXT_ALIGN_TOP = 0, + TEXT_ALIGN_MIDDLE, + TEXT_ALIGN_BOTTOM +} GuiTextAlignmentVertical; + +// Gui control text wrap mode +// NOTE: Useful for multiline text +typedef enum { + TEXT_WRAP_NONE = 0, + TEXT_WRAP_CHAR, + TEXT_WRAP_WORD +} GuiTextWrapMode; + // Gui controls typedef enum { // Default -> populates to all controls when set DEFAULT = 0, + // Basic controls LABEL, // Used also for: LABELBUTTON BUTTON, TOGGLE, // Used also for: TOGGLEGROUP - SLIDER, // Used also for: SLIDERBAR + SLIDER, // Used also for: SLIDERBAR, TOGGLESLIDER PROGRESSBAR, CHECKBOX, COMBOBOX, @@ -437,37 +520,55 @@ typedef enum { // Gui base properties for every control // NOTE: RAYGUI_MAX_PROPS_BASE properties (by default 16 properties) typedef enum { - BORDER_COLOR_NORMAL = 0, - BASE_COLOR_NORMAL, - TEXT_COLOR_NORMAL, - BORDER_COLOR_FOCUSED, - BASE_COLOR_FOCUSED, - TEXT_COLOR_FOCUSED, - BORDER_COLOR_PRESSED, - BASE_COLOR_PRESSED, - TEXT_COLOR_PRESSED, - BORDER_COLOR_DISABLED, - BASE_COLOR_DISABLED, - TEXT_COLOR_DISABLED, - BORDER_WIDTH, - TEXT_PADDING, - TEXT_ALIGNMENT, - RESERVED + BORDER_COLOR_NORMAL = 0, // Control border color in STATE_NORMAL + BASE_COLOR_NORMAL, // Control base color in STATE_NORMAL + TEXT_COLOR_NORMAL, // Control text color in STATE_NORMAL + BORDER_COLOR_FOCUSED, // Control border color in STATE_FOCUSED + BASE_COLOR_FOCUSED, // Control base color in STATE_FOCUSED + TEXT_COLOR_FOCUSED, // Control text color in STATE_FOCUSED + BORDER_COLOR_PRESSED, // Control border color in STATE_PRESSED + BASE_COLOR_PRESSED, // Control base color in STATE_PRESSED + TEXT_COLOR_PRESSED, // Control text color in STATE_PRESSED + BORDER_COLOR_DISABLED, // Control border color in STATE_DISABLED + BASE_COLOR_DISABLED, // Control base color in STATE_DISABLED + TEXT_COLOR_DISABLED, // Control text color in STATE_DISABLED + BORDER_WIDTH, // Control border size, 0 for no border + //TEXT_SIZE, // Control text size (glyphs max height) -> GLOBAL for all controls + //TEXT_SPACING, // Control text spacing between glyphs -> GLOBAL for all controls + //TEXT_LINE_SPACING // Control text spacing between lines -> GLOBAL for all controls + TEXT_PADDING, // Control text padding, not considering border + TEXT_ALIGNMENT, // Control text horizontal alignment inside control text bound (after border and padding) + //TEXT_WRAP_MODE // Control text wrap-mode inside text bounds -> GLOBAL for all controls } GuiControlProperty; -// Gui extended properties depend on control -// NOTE: RAYGUI_MAX_PROPS_EXTENDED properties (by default 8 properties) -//---------------------------------------------------------------------------------- +// TODO: Which text styling properties should be global or per-control? +// At this moment TEXT_PADDING and TEXT_ALIGNMENT is configured and saved per control while +// TEXT_SIZE, TEXT_SPACING, TEXT_LINE_SPACING, TEXT_ALIGNMENT_VERTICAL, TEXT_WRAP_MODE are global and +// should be configured by user as needed while defining the UI layout + +// Gui extended properties depend on control +// NOTE: RAYGUI_MAX_PROPS_EXTENDED properties (by default, max 8 properties) +//---------------------------------------------------------------------------------- // DEFAULT extended properties // NOTE: Those properties are common to all controls or global +// WARNING: We only have 8 slots for those properties by default!!! -> New global control: TEXT? typedef enum { TEXT_SIZE = 16, // Text size (glyphs max height) TEXT_SPACING, // Text spacing between glyphs LINE_COLOR, // Line control color BACKGROUND_COLOR, // Background color + TEXT_LINE_SPACING, // Text spacing between lines + TEXT_ALIGNMENT_VERTICAL, // Text vertical alignment inside text bounds (after border and padding) + TEXT_WRAP_MODE // Text wrap-mode inside text bounds + //TEXT_DECORATION // Text decoration: 0-None, 1-Underline, 2-Line-through, 3-Overline + //TEXT_DECORATION_THICK // Text decoration line thikness } GuiDefaultProperty; +// Other possible text properties: +// TEXT_WEIGHT // Normal, Italic, Bold -> Requires specific font change +// TEXT_INDENT // Text indentation -> Now using TEXT_PADDING... + // Label //typedef enum { } GuiLabelProperty; @@ -492,12 +593,12 @@ typedef enum { // ScrollBar typedef enum { - ARROWS_SIZE = 16, - ARROWS_VISIBLE, - SCROLL_SLIDER_PADDING, // (SLIDERBAR, SLIDER_PADDING) - SCROLL_SLIDER_SIZE, - SCROLL_PADDING, - SCROLL_SPEED, + ARROWS_SIZE = 16, // ScrollBar arrows size + ARROWS_VISIBLE, // ScrollBar arrows visible + SCROLL_SLIDER_PADDING, // ScrollBar slider internal padding + SCROLL_SLIDER_SIZE, // ScrollBar slider size + SCROLL_PADDING, // ScrollBar scroll padding from arrows + SCROLL_SPEED, // ScrollBar scrolling speed } GuiScrollBarProperty; // CheckBox @@ -519,11 +620,7 @@ typedef enum { // TextBox/TextBoxMulti/ValueBox/Spinner typedef enum { - TEXT_INNER_PADDING = 16, // TextBox/TextBoxMulti/ValueBox/Spinner inner text padding - TEXT_LINES_SPACING, // TextBoxMulti lines separation - TEXT_ALIGNMENT_VERTICAL, // TextBoxMulti vertical alignment: 0-CENTERED, 1-UP, 2-DOWN - TEXT_MULTILINE, // TextBox supports multiple lines - TEXT_WRAP_MODE // TextBox wrap mode for multiline: 0-NO_WRAP, 1-CHAR_WRAP, 2-WORD_WRAP + TEXT_READONLY = 16, // TextBox in read-only mode: 0-text editable, 1-text no-editable } GuiTextBoxProperty; // Spinner @@ -537,7 +634,7 @@ typedef enum { LIST_ITEMS_HEIGHT = 16, // ListView items height LIST_ITEMS_SPACING, // ListView items separation SCROLLBAR_WIDTH, // ListView scrollbar size (usually width) - SCROLLBAR_SIDE, // ListView scrollbar side (0-left, 1-right) + SCROLLBAR_SIDE, // ListView scrollbar side (0-SCROLLBAR_LEFT_SIDE, 1-SCROLLBAR_RIGHT_SIDE) } GuiListViewProperty; // ColorPicker @@ -571,7 +668,7 @@ RAYGUIAPI void GuiDisable(void); // Disable gui c RAYGUIAPI void GuiLock(void); // Lock gui controls (global state) RAYGUIAPI void GuiUnlock(void); // Unlock gui controls (global state) RAYGUIAPI bool GuiIsLocked(void); // Check if gui is locked (global state) -RAYGUIAPI void GuiFade(float alpha); // Set gui controls alpha (global state), alpha goes from 0.0f to 1.0f +RAYGUIAPI void GuiSetAlpha(float alpha); // Set gui controls alpha (global state), alpha goes from 0.0f to 1.0f RAYGUIAPI void GuiSetState(int state); // Set gui state (global state) RAYGUIAPI int GuiGetState(void); // Get gui state (global state) @@ -618,6 +715,7 @@ RAYGUIAPI int GuiButton(Rectangle bounds, const char *text); RAYGUIAPI int GuiLabelButton(Rectangle bounds, const char *text); // Label button control, show true when clicked RAYGUIAPI int GuiToggle(Rectangle bounds, const char *text, bool *active); // Toggle Button control, returns true when active RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int *active); // Toggle Group control, returns active toggle index +RAYGUIAPI int GuiToggleSlider(Rectangle bounds, const char *text, int *active); // Toggle Slider control, returns true when clicked RAYGUIAPI int GuiCheckBox(Rectangle bounds, const char *text, bool *checked); // Check Box control, returns true when active RAYGUIAPI int GuiComboBox(Rectangle bounds, const char *text, int *active); // Combo Box control, returns selected item index @@ -642,6 +740,8 @@ RAYGUIAPI int GuiColorPicker(Rectangle bounds, const char *text, Color *color); RAYGUIAPI int GuiColorPanel(Rectangle bounds, const char *text, Color *color); // Color Panel control RAYGUIAPI int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha); // Color Bar Alpha control RAYGUIAPI int GuiColorBarHue(Rectangle bounds, const char *text, float *value); // Color Bar Hue control +RAYGUIAPI int GuiColorPickerHSV(Rectangle bounds, const char *text, Vector3 *colorHsv); // Color Picker control that avoids conversion to RGB on each call (multiple color controls) +RAYGUIAPI int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv); // Color Panel control that returns HSV color value, used by GuiColorPickerHSV() //---------------------------------------------------------------------------------------------------------- @@ -1134,11 +1234,11 @@ static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS] = 0x78040000, 0x501f600e, 0x0ef44004, 0x12f41284, 0x0ef41284, 0x10140004, 0x7ffc300c, 0x10003000, // ICON_MODE_3D 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe, // ICON_CUBE 0x7fe00000, 0x5ff87ff0, 0x47fe4ffc, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe, // ICON_CUBE_FACE_TOP - 0x7fe00000, 0x50386030, 0x47fe483c, 0x443e443e, 0x443e443e, 0x241e75fe, 0x0c06140e, 0x000007fe, // ICON_CUBE_FACE_LEFT + 0x7fe00000, 0x50386030, 0x47c2483c, 0x443e443e, 0x443e443e, 0x241e75fe, 0x0c06140e, 0x000007fe, // ICON_CUBE_FACE_LEFT 0x7fe00000, 0x50286030, 0x47fe4804, 0x47fe47fe, 0x47fe47fe, 0x27fe77fe, 0x0ffe17fe, 0x000007fe, // ICON_CUBE_FACE_FRONT - 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x3ff27fe2, 0x0ffe1ffa, 0x000007fe, // ICON_CUBE_FACE_BOTTOM + 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x3bf27be2, 0x0bfe1bfa, 0x000007fe, // ICON_CUBE_FACE_BOTTOM 0x7fe00000, 0x70286030, 0x7ffe7804, 0x7c227c02, 0x7c227c22, 0x3c127de2, 0x0c061c0a, 0x000007fe, // ICON_CUBE_FACE_RIGHT - 0x7fe00000, 0x7fe87ff0, 0x7ffe7fe4, 0x7fe27fe2, 0x7fe27fe2, 0x24127fe2, 0x0c06140a, 0x000007fe, // ICON_CUBE_FACE_BACK + 0x7fe00000, 0x6fe85ff0, 0x781e77e4, 0x7be27be2, 0x7be27be2, 0x24127be2, 0x0c06140a, 0x000007fe, // ICON_CUBE_FACE_BACK 0x00000000, 0x2a0233fe, 0x22022602, 0x22022202, 0x2a022602, 0x00a033fe, 0x02080110, 0x00000000, // ICON_CAMERA 0x00000000, 0x200c3ffc, 0x000c000c, 0x3ffc000c, 0x30003000, 0x30003000, 0x3ffc3004, 0x00000000, // ICON_SPECIAL 0x00000000, 0x0022003e, 0x012201e2, 0x0100013e, 0x01000100, 0x79000100, 0x4f004900, 0x00007800, // ICON_LINK_NET @@ -1237,8 +1337,10 @@ static unsigned int *guiIconsPtr = guiIcons; #define RAYGUI_ICON_SIZE 0 #endif -#define RAYGUI_MAX_CONTROLS 16 // Maximum number of standard controls -#define RAYGUI_MAX_PROPS_BASE 16 // Maximum number of standard properties +// WARNING: Those values define the total size of the style data array, +// if changed, previous saved styles could become incompatible +#define RAYGUI_MAX_CONTROLS 16 // Maximum number of controls +#define RAYGUI_MAX_PROPS_BASE 16 // Maximum number of base properties #define RAYGUI_MAX_PROPS_EXTENDED 8 // Maximum number of extended properties //---------------------------------------------------------------------------------- @@ -1254,7 +1356,7 @@ static GuiState guiState = STATE_NORMAL; // Gui global state, if !STATE_N static Font guiFont = { 0 }; // Gui current font (WARNING: highly coupled to raylib) static bool guiLocked = false; // Gui lock state (no inputs processed) -static float guiAlpha = 1.0f; // Gui element transpacency on drawing +static float guiAlpha = 1.0f; // Gui controls transparency static unsigned int guiIconScale = 1; // Gui icon default scale (if icons enabled) @@ -1264,7 +1366,7 @@ static const char *guiTooltipPtr = NULL; // Tooltip string pointer (strin static bool guiSliderDragging = false; // Gui slider drag state (no inputs processed except dragged slider) static Rectangle guiSliderActive = { 0 }; // Gui slider active bounds rectangle, used as an unique identifier -static unsigned int textBoxCursorIndex = 0; // Cursor index, shared by all GuiTextBox*() +static int textBoxCursorIndex = 0; // Cursor index, shared by all GuiTextBox*() //static int blinkCursorFrameCounter = 0; // Frame counter for cursor blinking static int autoCursorCooldownCounter = 0; // Cooldown frame counter for automatic cursor movement on key-down static int autoCursorDelayCounter = 0; // Delay frame counter for automatic cursor movement @@ -1317,25 +1419,33 @@ static int GetCharPressed(void); // -- GuiTextBox(), GuiValueBox() // Drawing required functions //------------------------------------------------------------------------------- -static void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle(), GuiDrawIcon() +static void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle() static void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker() //------------------------------------------------------------------------------- // Text required functions //------------------------------------------------------------------------------- -static Font LoadFontEx(const char *fileName, int fontSize, int *fontChars, int glyphCount); // -- GuiLoadStyle() -static Font GetFontDefault(void); // -- GuiLoadStyleDefault() -static Texture2D LoadTextureFromImage(Image image); // -- GuiLoadStyle() -static void SetShapesTexture(Texture2D tex, Rectangle rec); // -- GuiLoadStyle() -static char *LoadFileText(const char *fileName); // -- GuiLoadStyle() -static const char *GetDirectoryPath(const char *filePath); // -- GuiLoadStyle() +static Font GetFontDefault(void); // -- GuiLoadStyleDefault() +static Font LoadFontEx(const char *fileName, int fontSize, int *codepoints, int codepointCount); // -- GuiLoadStyle(), load font + +static Texture2D LoadTextureFromImage(Image image); // -- GuiLoadStyle(), required to load texture from embedded font atlas image +static void SetShapesTexture(Texture2D tex, Rectangle rec); // -- GuiLoadStyle(), required to set shapes rec to font white rec (optimization) + +static char *LoadFileText(const char *fileName); // -- GuiLoadStyle(), required to load charset data +static void UnloadFileText(char *text); // -- GuiLoadStyle(), required to unload charset data + +static const char *GetDirectoryPath(const char *filePath); // -- GuiLoadStyle(), required to find charset/font file from text .rgs + +static int *LoadCodepoints(const char *text, int *count); // -- GuiLoadStyle(), required to load required font codepoints list +static void UnloadCodepoints(int *codepoints); // -- GuiLoadStyle(), required to unload codepoints list + +static unsigned char *DecompressData(const unsigned char *compData, int compDataSize, int *dataSize); // -- GuiLoadStyle() //------------------------------------------------------------------------------- // raylib functions already implemented in raygui //------------------------------------------------------------------------------- static Color GetColor(int hexValue); // Returns a Color struct from hexadecimal value static int ColorToInt(Color color); // Returns hexadecimal value for a Color -static Color Fade(Color color, float alpha); // Color fade-in or fade-out, alpha goes from 0.0f to 1.0f static bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if point is inside rectangle static const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' static const char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings @@ -1358,7 +1468,7 @@ static int GetTextWidth(const char *text); // Gui get text static Rectangle GetTextBounds(int control, Rectangle bounds); // Get text bounds considering control bounds static const char *GetTextIcon(const char *text, int *iconId); // Get text icon if provided and move text cursor -static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color tint); // Gui draw text using default font +static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, Color tint); // Gui draw text using default font static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color); // Gui draw rectangle using default raygui style static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow); // Split controls text into multiple strings @@ -1368,6 +1478,7 @@ static Vector3 ConvertRGBtoHSV(Vector3 rgb); // Convert color static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue); // Scroll bar control, used by GuiScrollPanel() static void GuiTooltip(Rectangle controlRec); // Draw tooltip using control rec position +static Color GuiFade(Color color, float alpha); // Fade color by an alpha factor //---------------------------------------------------------------------------------- // Gui Setup Functions Definition @@ -1390,7 +1501,7 @@ void GuiUnlock(void) { guiLocked = false; } bool GuiIsLocked(void) { return guiLocked; } // Set gui controls alpha global state -void GuiFade(float alpha) +void GuiSetAlpha(float alpha) { if (alpha < 0.0f) alpha = 0.0f; else if (alpha > 1.0f) alpha = 1.0f; @@ -1416,7 +1527,6 @@ void GuiSetFont(Font font) if (!guiStyleLoaded) GuiLoadStyleDefault(); guiFont = font; - GuiSetStyle(DEFAULT, TEXT_SIZE, font.baseSize); } } @@ -1487,7 +1597,7 @@ int GuiWindowBox(Rectangle bounds, const char *title) GuiSetStyle(BUTTON, BORDER_WIDTH, 1); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); #if defined(RAYGUI_NO_ICONS) - clicked = GuiButton(closeButtonRec, "x"); + result = GuiButton(closeButtonRec, "x"); #else result = GuiButton(closeButtonRec, GuiIconText(ICON_CROSS_SMALL, NULL)); #endif @@ -1510,9 +1620,9 @@ int GuiGroupBox(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha)); - GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, RAYGUI_GROUPBOX_LINE_THICK }, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha)); - GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha)); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, RAYGUI_GROUPBOX_LINE_THICK }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y, RAYGUI_GROUPBOX_LINE_THICK, bounds.height }, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR))); GuiLine(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2, bounds.width, (float)GuiGetStyle(DEFAULT, TEXT_SIZE) }, text); //-------------------------------------------------------------------- @@ -1533,7 +1643,7 @@ int GuiLine(Rectangle bounds, const char *text) int result = 0; GuiState state = guiState; - Color color = Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha); + Color color = GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)); // Draw control //-------------------------------------------------------------------- @@ -1574,15 +1684,15 @@ int GuiPanel(Rectangle bounds, const char *text) { // Move panel bounds after the header bar bounds.y += (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1; - bounds.height -= (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + 1; + bounds.height -= (float)RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 1; } // Draw control //-------------------------------------------------------------------- if (text != NULL) GuiStatusBar(statusBar, text); // Draw panel header as status bar - GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED: LINE_COLOR)), guiAlpha), - Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BASE_COLOR_DISABLED : BACKGROUND_COLOR)), guiAlpha)); + GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED: LINE_COLOR)), + GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BASE_COLOR_DISABLED : BACKGROUND_COLOR))); //-------------------------------------------------------------------- return result; @@ -1595,7 +1705,7 @@ int GuiTabBar(Rectangle bounds, const char **text, int count, int *active) #define RAYGUI_TABBAR_ITEM_WIDTH 160 int result = -1; - GuiState state = guiState; + //GuiState state = guiState; Rectangle tabBounds = { bounds.x, bounds.y, RAYGUI_TABBAR_ITEM_WIDTH, bounds.height }; @@ -1638,7 +1748,7 @@ int GuiTabBar(Rectangle bounds, const char **text, int count, int *active) GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, textAlignment); // Draw tab close button - // NOTE: Only draw close button for curren tab: if (CheckCollisionPointRec(mousePoint, tabBounds)) + // NOTE: Only draw close button for current tab: if (CheckCollisionPointRec(mousePosition, tabBounds)) int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); int tempTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); GuiSetStyle(BUTTON, BORDER_WIDTH, 1); @@ -1663,8 +1773,12 @@ int GuiTabBar(Rectangle bounds, const char **text, int count, int *active) // Scroll Panel control int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view) { + #define RAYGUI_MIN_SCROLLBAR_WIDTH 40 + #define RAYGUI_MIN_SCROLLBAR_HEIGHT 40 + int result = 0; GuiState state = guiState; + float mouseWheelSpeed = 20.0f; // Default movement speed with mouse wheel Rectangle temp = { 0 }; if (view == NULL) view = &temp; @@ -1692,8 +1806,31 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector int horizontalScrollBarWidth = hasHorizontalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0; int verticalScrollBarWidth = hasVerticalScrollBar? GuiGetStyle(LISTVIEW, SCROLLBAR_WIDTH) : 0; - Rectangle horizontalScrollBar = { (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + verticalScrollBarWidth : (float)bounds.x) + GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)bounds.y + bounds.height - horizontalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)bounds.width - verticalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)horizontalScrollBarWidth }; - Rectangle verticalScrollBar = { (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)bounds.x + bounds.width - verticalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH)), (float)bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)verticalScrollBarWidth, (float)bounds.height - horizontalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) }; + Rectangle horizontalScrollBar = { + (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + verticalScrollBarWidth : (float)bounds.x) + GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)bounds.y + bounds.height - horizontalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)bounds.width - verticalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)horizontalScrollBarWidth + }; + Rectangle verticalScrollBar = { + (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)bounds.x + bounds.width - verticalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH)), + (float)bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), + (float)verticalScrollBarWidth, + (float)bounds.height - horizontalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) + }; + + // Make sure scroll bars have a minimum width/height + // NOTE: If content >>> bounds, size could be very small or even 0 + if (horizontalScrollBar.width < RAYGUI_MIN_SCROLLBAR_WIDTH) + { + horizontalScrollBar.width = RAYGUI_MIN_SCROLLBAR_WIDTH; + mouseWheelSpeed = 30.0f; // TODO: Calculate speed increment based on content.height vs bounds.height + } + if (verticalScrollBar.height < RAYGUI_MIN_SCROLLBAR_HEIGHT) + { + verticalScrollBar.height = RAYGUI_MIN_SCROLLBAR_HEIGHT; + mouseWheelSpeed = 30.0f; // TODO: Calculate speed increment based on content.width vs bounds.width + } // Calculate view area (area without the scrollbars) *view = (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? @@ -1736,9 +1873,9 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector #endif float wheelMove = GetMouseWheelMove(); - // Horizontal scroll ((Left Control or Left Shift) + Mouse wheel) - if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_LEFT_SHIFT))) scrollPos.x += wheelMove*20; - else scrollPos.y += wheelMove*20; // Vertical scroll + // Horizontal and vertical scrolling with mouse wheel + if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_LEFT_SHIFT))) scrollPos.x += wheelMove*mouseWheelSpeed; + else scrollPos.y += wheelMove*mouseWheelSpeed; // Vertical scroll } } @@ -1753,7 +1890,7 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector //-------------------------------------------------------------------- if (text != NULL) GuiStatusBar(statusBar, text); // Draw panel header as status bar - GuiDrawRectangle(bounds, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)), guiAlpha)); // Draw background + GuiDrawRectangle(bounds, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background // Save size of the scrollbar slider const int slider = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE); @@ -1780,11 +1917,11 @@ int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector if (hasHorizontalScrollBar && hasVerticalScrollBar) { Rectangle corner = { (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) + 2) : (horizontalScrollBar.x + horizontalScrollBar.width + 2), verticalScrollBar.y + verticalScrollBar.height + 2, (float)horizontalScrollBarWidth - 4, (float)verticalScrollBarWidth - 4 }; - GuiDrawRectangle(corner, 0, BLANK, Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT + (state*3))), guiAlpha)); + GuiDrawRectangle(corner, 0, BLANK, GetColor(GuiGetStyle(LISTVIEW, TEXT + (state*3)))); } // Draw scrollbar lines depending on current state - GuiDrawRectangle(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + (state*3))), guiAlpha), BLANK); + GuiDrawRectangle(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + (state*3))), BLANK); // Set scrollbar slider size back to the way it was before GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, slider); @@ -1808,7 +1945,7 @@ int GuiLabel(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); + GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); //-------------------------------------------------------------------- return result; @@ -1839,8 +1976,8 @@ int GuiButton(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(BUTTON, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(BUTTON, BASE + (state*3))), guiAlpha)); - GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state*3))), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), GetColor(GuiGetStyle(BUTTON, BORDER + (state*3))), GetColor(GuiGetStyle(BUTTON, BASE + (state*3)))); + GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), GetColor(GuiGetStyle(BUTTON, TEXT + (state*3)))); if (state == STATE_FOCUSED) GuiTooltip(bounds); //------------------------------------------------------------------ @@ -1877,7 +2014,7 @@ int GuiLabelButton(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); + GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); //-------------------------------------------------------------------- return pressed; @@ -1916,13 +2053,13 @@ int GuiToggle(Rectangle bounds, const char *text, bool *active) //-------------------------------------------------------------------- if (state == STATE_NORMAL) { - GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, ((*active)? BORDER_COLOR_PRESSED : (BORDER + state*3)))), guiAlpha), Fade(GetColor(GuiGetStyle(TOGGLE, ((*active)? BASE_COLOR_PRESSED : (BASE + state*3)))), guiAlpha)); - GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, ((*active)? TEXT_COLOR_PRESSED : (TEXT + state*3)))), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, ((*active)? BORDER_COLOR_PRESSED : (BORDER + state*3)))), GetColor(GuiGetStyle(TOGGLE, ((*active)? BASE_COLOR_PRESSED : (BASE + state*3))))); + GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TOGGLE, ((*active)? TEXT_COLOR_PRESSED : (TEXT + state*3))))); } else { - GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, BORDER + state*3)), guiAlpha), Fade(GetColor(GuiGetStyle(TOGGLE, BASE + state*3)), guiAlpha)); - GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, TEXT + state*3)), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, BORDER + state*3)), GetColor(GuiGetStyle(TOGGLE, BASE + state*3))); + GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TOGGLE, TEXT + state*3))); } if (state == STATE_FOCUSED) GuiTooltip(bounds); @@ -1980,7 +2117,79 @@ int GuiToggleGroup(Rectangle bounds, const char *text, int *active) return result; } -// Check Box control, returns true when active +// Toggle Slider control extended, returns true when clicked +int GuiToggleSlider(Rectangle bounds, const char *text, int *active) +{ + int result = 0; + GuiState state = guiState; + + int temp = 0; + if (active == NULL) active = &temp; + + //bool toggle = false; // Required for individual toggles + + // Get substrings items from text (items pointers) + int itemCount = 0; + const char **items = GuiTextSplit(text, ';', &itemCount, NULL); + + Rectangle slider = { + 0, // Calculated later depending on the active toggle + bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING), + (bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - (itemCount + 1)*GuiGetStyle(SLIDER, SLIDER_PADDING))/itemCount, + bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) }; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked) + { + Vector2 mousePoint = GetMousePosition(); + + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; + else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + { + state = STATE_PRESSED; + (*active)++; + result = 1; + } + else state = STATE_FOCUSED; + } + + if ((*active) && (state != STATE_FOCUSED)) state = STATE_PRESSED; + } + + if (*active >= itemCount) *active = 0; + slider.x = bounds.x + GuiGetStyle(SLIDER, BORDER_WIDTH) + (*active + 1)*GuiGetStyle(SLIDER, SLIDER_PADDING) + (*active)*slider.width; + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), GetColor(GuiGetStyle(TOGGLE, BORDER + (state*3))), + GetColor(GuiGetStyle(TOGGLE, BASE_COLOR_NORMAL))); + + // Draw internal slider + if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED))); + else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_FOCUSED))); + else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED))); + + // Draw text in slider + if (text != NULL) + { + Rectangle textBounds = { 0 }; + textBounds.width = (float)GetTextWidth(text); + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); + textBounds.x = slider.x + slider.width/2 - textBounds.width/2; + textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + + GuiDrawText(items[*active], textBounds, GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, TEXT + (state*3))), guiAlpha)); + } + //-------------------------------------------------------------------- + + return result; +} + +// Check Box control, returns 1 when state changed int GuiCheckBox(Rectangle bounds, const char *text, bool *checked) { int result = 0; @@ -2019,14 +2228,18 @@ int GuiCheckBox(Rectangle bounds, const char *text, bool *checked) if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; else state = STATE_FOCUSED; - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) *checked = !(*checked); + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) + { + *checked = !(*checked); + result = 1; + } } } //-------------------------------------------------------------------- // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, GuiGetStyle(CHECKBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(CHECKBOX, BORDER + (state*3))), guiAlpha), BLANK); + GuiDrawRectangle(bounds, GuiGetStyle(CHECKBOX, BORDER_WIDTH), GetColor(GuiGetStyle(CHECKBOX, BORDER + (state*3))), BLANK); if (*checked) { @@ -2034,10 +2247,10 @@ int GuiCheckBox(Rectangle bounds, const char *text, bool *checked) bounds.y + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING), bounds.width - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING)), bounds.height - 2*(GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING)) }; - GuiDrawRectangle(check, 0, BLANK, Fade(GetColor(GuiGetStyle(CHECKBOX, TEXT + state*3)), guiAlpha)); + GuiDrawRectangle(check, 0, BLANK, GetColor(GuiGetStyle(CHECKBOX, TEXT + state*3))); } - GuiDrawText(text, textBounds, (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); + GuiDrawText(text, textBounds, (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); //-------------------------------------------------------------------- return result; @@ -2088,8 +2301,8 @@ int GuiComboBox(Rectangle bounds, const char *text, int *active) // Draw control //-------------------------------------------------------------------- // Draw combo box main - GuiDrawRectangle(bounds, GuiGetStyle(COMBOBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COMBOBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(COMBOBOX, BASE + (state*3))), guiAlpha)); - GuiDrawText(items[*active], GetTextBounds(COMBOBOX, bounds), GuiGetStyle(COMBOBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(COMBOBOX, TEXT + (state*3))), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(COMBOBOX, BORDER_WIDTH), GetColor(GuiGetStyle(COMBOBOX, BORDER + (state*3))), GetColor(GuiGetStyle(COMBOBOX, BASE + (state*3)))); + GuiDrawText(items[*active], GetTextBounds(COMBOBOX, bounds), GuiGetStyle(COMBOBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(COMBOBOX, TEXT + (state*3)))); // Draw selector using a custom button // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values @@ -2184,8 +2397,8 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod //-------------------------------------------------------------------- if (editMode) GuiPanel(boundsOpen, NULL); - GuiDrawRectangle(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER + state*3)), guiAlpha), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE + state*3)), guiAlpha)); - GuiDrawText(items[itemSelected], GetTextBounds(DEFAULT, bounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + state*3)), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER + state*3)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE + state*3))); + GuiDrawText(items[itemSelected], GetTextBounds(DROPDOWNBOX, bounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + state*3))); if (editMode) { @@ -2197,25 +2410,25 @@ int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMod if (i == itemSelected) { - GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_PRESSED)), guiAlpha), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_PRESSED)), guiAlpha)); - GuiDrawText(items[i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_PRESSED)), guiAlpha)); + GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_PRESSED))); + GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_PRESSED))); } else if (i == itemFocused) { - GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_FOCUSED)), guiAlpha), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_FOCUSED)), guiAlpha)); - GuiDrawText(items[i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_FOCUSED)), guiAlpha)); + GuiDrawRectangle(itemBounds, GuiGetStyle(DROPDOWNBOX, BORDER_WIDTH), GetColor(GuiGetStyle(DROPDOWNBOX, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(DROPDOWNBOX, BASE_COLOR_FOCUSED))); + GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_FOCUSED))); } - else GuiDrawText(items[i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_NORMAL)), guiAlpha)); + else GuiDrawText(items[i], GetTextBounds(DROPDOWNBOX, itemBounds), GuiGetStyle(DROPDOWNBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(DROPDOWNBOX, TEXT_COLOR_NORMAL))); } } // Draw arrows (using icon if available) #if defined(RAYGUI_NO_ICONS) GuiDrawText("v", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 2, 10, 10 }, - TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); #else GuiDrawText("#120#", RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - GuiGetStyle(DROPDOWNBOX, ARROW_PADDING), bounds.y + bounds.height/2 - 6, 10, 10 }, - TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); // ICON_ARROW_DOWN_FILL + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); // ICON_ARROW_DOWN_FILL #endif //-------------------------------------------------------------------- @@ -2239,12 +2452,12 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) int result = 0; GuiState state = guiState; + bool multiline = false; // TODO: Consider multiline text input + int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); + Rectangle textBounds = GetTextBounds(TEXTBOX, bounds); int textWidth = GetTextWidth(text) - GetTextWidth(text + textBoxCursorIndex); - int textIndexOffset = 0; // Text index offset to start drawing in the box - - int alignmentVertical = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL); - int multiline = GuiGetStyle(TEXTBOX, TEXT_MULTILINE); + int textIndexOffset = 0; // Text index offset to start drawing in the box // Cursor rectangle // NOTE: Position X value should be updated @@ -2255,17 +2468,15 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) (float)GuiGetStyle(DEFAULT, TEXT_SIZE)*2 }; - switch (alignmentVertical) - { - case 0: cursor.y = textBounds.y + textBounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE); break; // CENTERED - case 1: cursor.y = textBounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; break; // UP - case 2: cursor.y = textBounds.y + textBounds.height; break; // DOWN - default: break; - } - if (cursor.height >= bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2; if (cursor.y < (bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH))) cursor.y = bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH); + // Mouse cursor rectangle + // NOTE: Initialized outside of screen + Rectangle mouseCursor = cursor; + mouseCursor.x = -1; + mouseCursor.width = 1; + // Auto-cursor movement logic // NOTE: Cursor moves automatically when key down after some time if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCooldownCounter++; @@ -2281,9 +2492,14 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) + // WARNING: Text editing is only supported under certain conditions: + if ((state != STATE_DISABLED) && // Control not disabled + !GuiGetStyle(TEXTBOX, TEXT_READONLY) && // TextBox not on read-only mode + !guiLocked && // Gui not locked + !guiSliderDragging && // No gui slider on dragging + (wrapMode == TEXT_WRAP_NONE)) // No wrap mode { - Vector2 mousePoint = GetMousePosition(); + Vector2 mousePosition = GetMousePosition(); if (editMode) { @@ -2301,7 +2517,7 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) textWidth = GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex); } - unsigned int textLength = (unsigned int)strlen(text); // Get current text length + int textLength = (int)strlen(text); // Get current text length int codepoint = GetCharPressed(); // Get Unicode codepoint if (multiline && IsKeyPressed(KEY_ENTER)) codepoint = (int)'\n'; @@ -2329,16 +2545,10 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) } // Move cursor to start - if ((textLength > 0) && IsKeyPressed(KEY_HOME)) - { - textBoxCursorIndex = 0; - } + if ((textLength > 0) && IsKeyPressed(KEY_HOME)) textBoxCursorIndex = 0; // Move cursor to end - if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) - { - textBoxCursorIndex = textLength; - } + if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) textBoxCursorIndex = textLength; // Delete codepoint from text, after current cursor position if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && (autoCursorCooldownCounter >= RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)))) @@ -2359,7 +2569,7 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) text[textLength] = '\0'; } } - + // Delete codepoint from text, before current cursor position if ((textLength > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && (autoCursorCooldownCounter >= RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)))) { @@ -2386,7 +2596,6 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) } // Move cursor position with keys - //if (IsKeyDown(KEY_LEFT) && autoCursorMode) if (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && (autoCursorCooldownCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN))) { autoCursorDelayCounter++; @@ -2412,19 +2621,59 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) } } - // TODO: Get cursor rectangle from mouse position - //cursor = GetCursorFromMousePosition(bounds, text, mouse); // Gui style considered internally, including wrapMode + // Move cursor position with mouse + if (CheckCollisionPointRec(mousePosition, textBounds)) // Mouse hover text + { + float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/(float)guiFont.baseSize; + int codepoint = 0; + int codepointSize = 0; + int codepointIndex = 0; + float glyphWidth = 0.0f; + float widthToMouseX = 0; + int mouseCursorIndex = 0; - // TODO: Get cursor rectangle from buffer index - //cursor = GetCursorFromIndex(bounds, text, index); // Gui style considered internally, including wrapMode + for (int i = textIndexOffset; i < textLength; i++) + { + codepoint = GetCodepointNext(&text[i], &codepointSize); + codepointIndex = GetGlyphIndex(guiFont, codepoint); + + if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor); + else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor); + + if (mousePosition.x <= (textBounds.x + (widthToMouseX + glyphWidth/2))) + { + mouseCursor.x = textBounds.x + widthToMouseX; + mouseCursorIndex = i; + break; + } + + widthToMouseX += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + } + + // Check if mouse cursor is at the last position + int textEndWidth = GetTextWidth(text + textIndexOffset); + if (GetMousePosition().x >= (textBounds.x + textEndWidth - glyphWidth/2)) + { + mouseCursor.x = textBounds.x + textEndWidth; + mouseCursorIndex = strlen(text); + } + + // Place cursor at required index on mouse click + if ((mouseCursor.x >= 0) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + cursor.x = mouseCursor.x; + textBoxCursorIndex = mouseCursorIndex; + } + } + else mouseCursor.x = -1; // Recalculate cursor position.y depending on textBoxCursorIndex cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING); //if (multiline) cursor.y = GetTextLines() - // Finish text editing on ENTER (if not multiline mode) or mouse click outside bounds + // Finish text editing on ENTER or mouse click outside bounds if ((!multiline && IsKeyPressed(KEY_ENTER)) || - (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) + (!CheckCollisionPointRec(mousePosition, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) { textBoxCursorIndex = 0; // GLOBAL: Reset the shared cursor index result = 1; @@ -2432,7 +2681,7 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) } else { - if (CheckCollisionPointRec(mousePoint, bounds)) + if (CheckCollisionPointRec(mousePosition, bounds)) { state = STATE_FOCUSED; @@ -2450,23 +2699,26 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) //-------------------------------------------------------------------- if (state == STATE_PRESSED) { - GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED))); } else if (state == STATE_DISABLED) { - GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED))); } - else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), BLANK); + else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), BLANK); // Draw text considering index offset if required // NOTE: Text index offset depends on cursor position - GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha)); + GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3)))); // Draw cursor - if (editMode) + if (editMode && !GuiGetStyle(TEXTBOX, TEXT_READONLY)) { //if (autoCursorMode || ((blinkCursorFrameCounter/40)%2 == 0)) - GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha)); + GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED))); + + // Draw mouse position cursor (if required) + if (mouseCursor.x >= 0) GuiDrawRectangle(mouseCursor, 0, BLANK, GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED))); } else if (state == STATE_FOCUSED) GuiTooltip(bounds); //-------------------------------------------------------------------- @@ -2475,22 +2727,23 @@ int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) } /* -// Text Box control with multiple lines -// NOTE: It's a regular GuiTextBox() but enabling multiline support, -// unfortunately cursor placement is not working properly so the function is removed +// Text Box control with multiple lines and word-wrap +// NOTE: This text-box is readonly, no editing supported by default bool GuiTextBoxMulti(Rectangle bounds, char *text, int bufferSize, bool editMode) { bool pressed = false; - GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL, 1); - GuiSetStyle(TEXTBOX, TEXT_MULTILINE, 1); + GuiSetStyle(TEXTBOX, TEXT_READONLY, 1); + GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_WORD); // WARNING: If wrap mode enabled, text editing is not supported + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_TOP); // TODO: Implement methods to calculate cursor position properly pressed = GuiTextBox(bounds, text, bufferSize, editMode); - GuiSetStyle(TEXTBOX, TEXT_MULTILINE, 0); - GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL, 0); - + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE); + GuiSetStyle(DEFAULT, TEXT_WRAP_MODE, TEXT_WRAP_NONE); + GuiSetStyle(TEXTBOX, TEXT_READONLY, 0); + return pressed; } */ @@ -2562,7 +2815,7 @@ int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); // Draw text label if provided - GuiDrawText(text, textBounds, (GuiGetStyle(SPINNER, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); + GuiDrawText(text, textBounds, (GuiGetStyle(SPINNER, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); //-------------------------------------------------------------------- *value = tempValue; @@ -2661,20 +2914,19 @@ int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, in if (state == STATE_PRESSED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_PRESSED)); else if (state == STATE_DISABLED) baseColor = GetColor(GuiGetStyle(VALUEBOX, BASE_COLOR_DISABLED)); - // WARNING: BLANK color does not work properly with Fade() - GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), guiAlpha), baseColor); - GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3))), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(VALUEBOX, BORDER_WIDTH), GetColor(GuiGetStyle(VALUEBOX, BORDER + (state*3))), baseColor); + GuiDrawText(textValue, GetTextBounds(VALUEBOX, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(VALUEBOX, TEXT + (state*3)))); // Draw cursor if (editMode) { // NOTE: ValueBox internal text is always centered Rectangle cursor = { bounds.x + GetTextWidth(textValue)/2 + bounds.width/2 + 1, bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4, bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH) }; - GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)), guiAlpha)); + GuiDrawRectangle(cursor, 0, BLANK, GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED))); } // Draw text label if provided - GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); + GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(LABEL, TEXT + (state*3)))); //-------------------------------------------------------------------- return result; @@ -2718,7 +2970,9 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, { if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) { - // Get equivalent value and slider position from mousePoint.x + state = STATE_PRESSED; + + // Get equivalent value and slider position from mousePosition.x *value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue; } } @@ -2736,11 +2990,14 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, guiSliderDragging = true; guiSliderActive = bounds; // Store bounds as an identifier when dragging starts - // Get equivalent value and slider position from mousePoint.x - *value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue; + if (!CheckCollisionPointRec(mousePoint, slider)) + { + // Get equivalent value and slider position from mousePosition.x + *value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue; - if (sliderWidth > 0) slider.x = mousePoint.x - slider.width/2; // Slider - else if (sliderWidth == 0) slider.width = (float)sliderValue; // SliderBar + if (sliderWidth > 0) slider.x = mousePoint.x - slider.width/2; // Slider + else if (sliderWidth == 0) slider.width = (float)sliderValue; // SliderBar + } } else state = STATE_FOCUSED; } @@ -2763,11 +3020,12 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(SLIDER, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(SLIDER, BORDER_WIDTH), GetColor(GuiGetStyle(SLIDER, BORDER + (state*3))), GetColor(GuiGetStyle(SLIDER, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED))); // Draw slider internal bar (depends on state) - if ((state == STATE_NORMAL) || (state == STATE_PRESSED)) GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED)), guiAlpha)); - else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED)), guiAlpha)); + if (state == STATE_NORMAL) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BASE_COLOR_PRESSED))); + else if (state == STATE_FOCUSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_FOCUSED))); + else if (state == STATE_PRESSED) GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, TEXT_COLOR_PRESSED))); // Draw left/right text if provided if (textLeft != NULL) @@ -2778,7 +3036,7 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, textBounds.x = bounds.x - textBounds.width - GuiGetStyle(SLIDER, TEXT_PADDING); textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; - GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha)); + GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3)))); } if (textRight != NULL) @@ -2789,7 +3047,7 @@ int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, textBounds.x = bounds.x + bounds.width + GuiGetStyle(SLIDER, TEXT_PADDING); textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; - GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, Fade(GetColor(GuiGetStyle(SLIDER, TEXT + (state*3))), guiAlpha)); + GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(SLIDER, TEXT + (state*3)))); } //-------------------------------------------------------------------- @@ -2817,6 +3075,7 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight float temp = (maxValue - minValue)/2.0f; if (value == NULL) value = &temp; + // Progress bar Rectangle progress = { bounds.x + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) + GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING), 0, bounds.height - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) }; @@ -2825,16 +3084,39 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight //-------------------------------------------------------------------- if (*value > maxValue) *value = maxValue; - if (state != STATE_DISABLED) progress.width = ((float)(*value/(maxValue - minValue))*(float)(bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH))); + // WARNING: Working with floats could lead to rounding issues + if ((state != STATE_DISABLED)) progress.width = (float)(*value/(maxValue - minValue))*bounds.width - ((*value >= maxValue) ? (float)(2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH)) : 0.0f); //-------------------------------------------------------------------- // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(PROGRESSBAR, BORDER + (state*3))), guiAlpha), BLANK); + if (state == STATE_DISABLED) + { + GuiDrawRectangle(bounds, GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), GetColor(GuiGetStyle(PROGRESSBAR, BORDER + (state*3))), BLANK); + } + else + { + if (*value > minValue) + { + // Draw progress bar with colored border, more visual + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height - 2 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, (int)progress.width + (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED))); + } + else GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL))); - // Draw slider internal progress bar (depends on state) - if ((state == STATE_NORMAL) || (state == STATE_PRESSED)) GuiDrawRectangle(progress, 0, BLANK, Fade(GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED)), guiAlpha)); - else if (state == STATE_FOCUSED) GuiDrawRectangle(progress, 0, BLANK, Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT_COLOR_FOCUSED)), guiAlpha)); + if (*value >= maxValue) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + progress.width + 1, bounds.y, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_FOCUSED))); + else + { + // Draw borders not yet reached by value + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + 1, bounds.y, bounds.width - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + (int)progress.width + 1, bounds.y + bounds.height - 1, bounds.width - (int)progress.width - 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL))); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + bounds.width - 1, bounds.y + 1, (float)GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.height - 2 }, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BORDER_COLOR_NORMAL))); + } + + // Draw slider internal progress bar (depends on state) + GuiDrawRectangle(progress, 0, BLANK, GetColor(GuiGetStyle(PROGRESSBAR, BASE_COLOR_PRESSED))); + } // Draw left/right text if provided if (textLeft != NULL) @@ -2845,7 +3127,7 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight textBounds.x = bounds.x - textBounds.width - GuiGetStyle(PROGRESSBAR, TEXT_PADDING); textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; - GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3))), guiAlpha)); + GuiDrawText(textLeft, textBounds, TEXT_ALIGN_RIGHT, GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3)))); } if (textRight != NULL) @@ -2856,7 +3138,7 @@ int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight textBounds.x = bounds.x + bounds.width + GuiGetStyle(PROGRESSBAR, TEXT_PADDING); textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; - GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, Fade(GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3))), guiAlpha)); + GuiDrawText(textRight, textBounds, TEXT_ALIGN_LEFT, GetColor(GuiGetStyle(PROGRESSBAR, TEXT + (state*3)))); } //-------------------------------------------------------------------- @@ -2871,9 +3153,8 @@ int GuiStatusBar(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, GuiGetStyle(STATUSBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(STATUSBAR, (state != STATE_DISABLED)? BORDER_COLOR_NORMAL : BORDER_COLOR_DISABLED)), guiAlpha), - Fade(GetColor(GuiGetStyle(STATUSBAR, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); - GuiDrawText(text, GetTextBounds(STATUSBAR, bounds), GuiGetStyle(STATUSBAR, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(STATUSBAR, (state != STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(STATUSBAR, BORDER_WIDTH), GetColor(GuiGetStyle(STATUSBAR, BORDER + (state*3))), GetColor(GuiGetStyle(STATUSBAR, BASE + (state*3)))); + GuiDrawText(text, GetTextBounds(STATUSBAR, bounds), GuiGetStyle(STATUSBAR, TEXT_ALIGNMENT), GetColor(GuiGetStyle(STATUSBAR, TEXT + (state*3)))); //-------------------------------------------------------------------- return result; @@ -2902,8 +3183,8 @@ int GuiDummyRec(Rectangle bounds, const char *text) // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); - GuiDrawText(text, GetTextBounds(DEFAULT, bounds), TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(BUTTON, (state != STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha)); + GuiDrawRectangle(bounds, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED))); + GuiDrawText(text, GetTextBounds(DEFAULT, bounds), TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(BUTTON, (state != STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED))); //------------------------------------------------------------------ return result; @@ -2930,7 +3211,7 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd GuiState state = guiState; int itemFocused = (focus == NULL)? -1 : *focus; - int itemSelected = *active; + int itemSelected = (active == NULL)? -1 : *active; // Check if we need a scroll bar bool useScrollBar = false; @@ -3002,35 +3283,35 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), guiAlpha), GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background + GuiDrawRectangle(bounds, GuiGetStyle(DEFAULT, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background // Draw visible items for (int i = 0; ((i < visibleItems) && (text != NULL)); i++) { if (state == STATE_DISABLED) { - if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), guiAlpha), Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED)), guiAlpha)); + if ((startIndex + i) == itemSelected) GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_DISABLED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_DISABLED))); - GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED)), guiAlpha)); + GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_DISABLED))); } else { - if ((startIndex + i) == itemSelected) + if (((startIndex + i) == itemSelected) && (active != NULL)) { // Draw item selected - GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), guiAlpha), Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED)), guiAlpha)); - GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED)), guiAlpha)); + GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_PRESSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_PRESSED))); + GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_PRESSED))); } - else if ((startIndex + i) == itemFocused) + else if (((startIndex + i) == itemFocused)) // && (focus != NULL)) // NOTE: We want items focused, despite not returned! { // Draw item focused - GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), guiAlpha), Fade(GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED)), guiAlpha)); - GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED)), guiAlpha)); + GuiDrawRectangle(itemBounds, GuiGetStyle(LISTVIEW, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER_COLOR_FOCUSED)), GetColor(GuiGetStyle(LISTVIEW, BASE_COLOR_FOCUSED))); + GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_FOCUSED))); } else { // Draw item normal - GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL)), guiAlpha)); + GuiDrawText(text[startIndex + i], GetTextBounds(DEFAULT, itemBounds), GuiGetStyle(LISTVIEW, TEXT_ALIGNMENT), GetColor(GuiGetStyle(LISTVIEW, TEXT_COLOR_NORMAL))); } } @@ -3062,10 +3343,10 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollInd } //-------------------------------------------------------------------- + if (active != NULL) *active = itemSelected; if (focus != NULL) *focus = itemFocused; if (scrollIndex != NULL) *scrollIndex = startIndex; - *active = itemSelected; return result; } @@ -3137,14 +3418,14 @@ int GuiColorPanel(Rectangle bounds, const char *text, Color *color) // Draw color picker: selector Rectangle selector = { pickerSelector.x - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, pickerSelector.y - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE) }; - GuiDrawRectangle(selector, 0, BLANK, Fade(colWhite, guiAlpha)); + GuiDrawRectangle(selector, 0, BLANK, colWhite); } else { DrawRectangleGradientEx(bounds, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.6f), guiAlpha)); } - GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha), BLANK); + GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK); //-------------------------------------------------------------------- return result; @@ -3174,6 +3455,8 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) { if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) { + state = STATE_PRESSED; + *alpha = (mousePoint.x - bounds.x)/bounds.width; if (*alpha <= 0.0f) *alpha = 0.0f; if (*alpha >= 1.0f) *alpha = 1.0f; @@ -3217,7 +3500,7 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) for (int y = 0; y < checksY; y++) { Rectangle check = { bounds.x + x*RAYGUI_COLORBARALPHA_CHECKED_SIZE, bounds.y + y*RAYGUI_COLORBARALPHA_CHECKED_SIZE, RAYGUI_COLORBARALPHA_CHECKED_SIZE, RAYGUI_COLORBARALPHA_CHECKED_SIZE }; - GuiDrawRectangle(check, 0, BLANK, ((x + y)%2)? Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.4f), guiAlpha) : Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.4f), guiAlpha)); + GuiDrawRectangle(check, 0, BLANK, ((x + y)%2)? Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.4f) : Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.4f)); } } @@ -3225,10 +3508,10 @@ int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) } else DrawRectangleGradientEx(bounds, Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha)); - GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha), BLANK); + GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK); // Draw alpha bar: selector - GuiDrawRectangle(selector, 0, BLANK, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); + GuiDrawRectangle(selector, 0, BLANK, GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3))); //-------------------------------------------------------------------- return result; @@ -3258,6 +3541,8 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) { if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) { + state = STATE_PRESSED; + *hue = (mousePoint.y - bounds.y)*360/bounds.height; if (*hue <= 0.0f) *hue = 0.0f; if (*hue >= 359.0f) *hue = 359.0f; @@ -3303,7 +3588,8 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) if (state != STATE_DISABLED) { // Draw hue bar:color bars - DrawRectangleGradientV((int)bounds.x, (int)(bounds.y), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha)); + // TODO: Use directly DrawRectangleGradientEx(bounds, color1, color2, color2, color1); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha)); DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + bounds.height/6), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 0, 255 }, guiAlpha)); DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 2*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 255, 255 }, guiAlpha)); DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 3*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 255, 255 }, guiAlpha)); @@ -3312,10 +3598,10 @@ int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) } else DrawRectangleGradientV((int)bounds.x, (int)bounds.y, (int)bounds.width, (int)bounds.height, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha)); - GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha), BLANK); + GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK); // Draw hue bar: selector - GuiDrawRectangle(selector, 0, BLANK, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); + GuiDrawRectangle(selector, 0, BLANK, GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3))); //-------------------------------------------------------------------- return result; @@ -3340,9 +3626,9 @@ int GuiColorPicker(Rectangle bounds, const char *text, Color *color) //Rectangle boundsAlpha = { bounds.x, bounds.y + bounds.height + GuiGetStyle(COLORPICKER, BARS_PADDING), bounds.width, GuiGetStyle(COLORPICKER, BARS_THICK) }; Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ (*color).r/255.0f, (*color).g/255.0f, (*color).b/255.0f }); - + GuiColorBarHue(boundsHue, NULL, &hsv.x); - + //color.a = (unsigned char)(GuiColorBarAlpha(boundsAlpha, (float)color.a/255.0f)*255.0f); Vector3 rgb = ConvertHSVtoRGB(hsv); @@ -3351,6 +3637,105 @@ int GuiColorPicker(Rectangle bounds, const char *text, Color *color) return result; } +// Color Picker control that avoids conversion to RGB and back to HSV on each call, thus avoiding jittering. +// The user can call ConvertHSVtoRGB() to convert *colorHsv value to RGB. +// NOTE: It's divided in multiple controls: +// int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) +// int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) +// float GuiColorBarHue(Rectangle bounds, float value) +// NOTE: bounds define GuiColorPanelHSV() size +int GuiColorPickerHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) +{ + int result = 0; + + Vector3 tempHsv = { 0 }; + + if (colorHsv == NULL) + { + const Vector3 tempColor = { 200.0f/255.0f, 0.0f, 0.0f }; + tempHsv = ConvertRGBtoHSV(tempColor); + colorHsv = &tempHsv; + } + + GuiColorPanelHSV(bounds, NULL, colorHsv); + + const Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height }; + + GuiColorBarHue(boundsHue, NULL, &colorHsv->x); + + return result; +} + +// Color Panel control, returns HSV color value in *colorHsv. +// Used by GuiColorPickerHSV() +int GuiColorPanelHSV(Rectangle bounds, const char *text, Vector3 *colorHsv) +{ + int result = 0; + GuiState state = guiState; + Vector2 pickerSelector = { 0 }; + + const Color colWhite = { 255, 255, 255, 255 }; + const Color colBlack = { 0, 0, 0, 255 }; + + pickerSelector.x = bounds.x + (float)colorHsv->y*bounds.width; // HSV: Saturation + pickerSelector.y = bounds.y + (1.0f - (float)colorHsv->z)*bounds.height; // HSV: Value + + float hue = -1.0f; + Vector3 maxHue = { hue >= 0.0f ? hue : colorHsv->x, 1.0f, 1.0f }; + Vector3 rgbHue = ConvertHSVtoRGB(maxHue); + Color maxHueCol = { (unsigned char)(255.0f*rgbHue.x), + (unsigned char)(255.0f*rgbHue.y), + (unsigned char)(255.0f*rgbHue.z), 255 }; + + // Update control + //-------------------------------------------------------------------- + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) + { + Vector2 mousePoint = GetMousePosition(); + + if (CheckCollisionPointRec(mousePoint, bounds)) + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + state = STATE_PRESSED; + pickerSelector = mousePoint; + + // Calculate color from picker + Vector2 colorPick = { pickerSelector.x - bounds.x, pickerSelector.y - bounds.y }; + + colorPick.x /= (float)bounds.width; // Get normalized value on x + colorPick.y /= (float)bounds.height; // Get normalized value on y + + colorHsv->y = colorPick.x; + colorHsv->z = 1.0f - colorPick.y; + } + else state = STATE_FOCUSED; + } + } + //-------------------------------------------------------------------- + + // Draw control + //-------------------------------------------------------------------- + if (state != STATE_DISABLED) + { + DrawRectangleGradientEx(bounds, Fade(colWhite, guiAlpha), Fade(colWhite, guiAlpha), Fade(maxHueCol, guiAlpha), Fade(maxHueCol, guiAlpha)); + DrawRectangleGradientEx(bounds, Fade(colBlack, 0), Fade(colBlack, guiAlpha), Fade(colBlack, guiAlpha), Fade(colBlack, 0)); + + // Draw color picker: selector + Rectangle selector = { pickerSelector.x - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, pickerSelector.y - GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE)/2, (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE), (float)GuiGetStyle(COLORPICKER, COLOR_SELECTOR_SIZE) }; + GuiDrawRectangle(selector, 0, BLANK, colWhite); + } + else + { + DrawRectangleGradientEx(bounds, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(colBlack, 0.6f), guiAlpha), Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), 0.6f), guiAlpha)); + } + + GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), BLANK); + //-------------------------------------------------------------------- + + return result; +} + // Message Box control int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons) { @@ -3371,7 +3756,7 @@ int GuiMessageBox(Rectangle bounds, const char *title, const char *message, cons buttonBounds.width = (bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount; buttonBounds.height = RAYGUI_MESSAGEBOX_BUTTON_HEIGHT; - int textWidth = GetTextWidth(message); + int textWidth = GetTextWidth(message) + 2; Rectangle textBounds = { 0 }; textBounds.x = bounds.x + bounds.width/2 - textWidth/2; @@ -3511,8 +3896,9 @@ int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vect Vector2 mousePoint = GetMousePosition(); Vector2 currentMouseCell = { 0 }; - int linesV = ((int)(bounds.width/spacing))*subdivs + 1; - int linesH = ((int)(bounds.height/spacing))*subdivs + 1; + float spaceWidth = spacing/(float)subdivs; + int linesV = (int)(bounds.width/spaceWidth) + 1; + int linesH = (int)(bounds.height/spaceWidth) + 1; // Update control //-------------------------------------------------------------------- @@ -3539,14 +3925,14 @@ int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vect for (int i = 0; i < linesV; i++) { Rectangle lineV = { bounds.x + spacing*i/subdivs, bounds.y, 1, bounds.height }; - GuiDrawRectangle(lineV, 0, BLANK, ((i%subdivs) == 0) ? Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA*4) : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA)); + GuiDrawRectangle(lineV, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA)); } // Draw horizontal grid lines for (int i = 0; i < linesH; i++) { Rectangle lineH = { bounds.x, bounds.y + spacing*i/subdivs, bounds.width, 1 }; - GuiDrawRectangle(lineH, 0, BLANK, ((i%subdivs) == 0) ? Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA*4) : Fade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA)); + GuiDrawRectangle(lineH, 0, BLANK, ((i%subdivs) == 0)? GuiFade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA*4) : GuiFade(GetColor(GuiGetStyle(DEFAULT, LINE_COLOR)), RAYGUI_GRID_ALPHA)); } } } break; @@ -3620,34 +4006,37 @@ void GuiLoadStyle(const char *fileName) sscanf(buffer, "f %d %s %[^\r\n]s", &fontSize, charmapFileName, fontFileName); Font font = { 0 }; + int *codepoints = NULL; + int codepointCount = 0; if (charmapFileName[0] != '0') { - // Load characters from charmap file, - // expected '\n' separated list of integer values - char *charValues = LoadFileText(charmapFileName); - if (charValues != NULL) - { - int glyphCount = 0; - const char **chars = TextSplit(charValues, '\n', &glyphCount); - - int *values = (int *)RAYGUI_MALLOC(glyphCount*sizeof(int)); - for (int i = 0; i < glyphCount; i++) values[i] = TextToInteger(chars[i]); - - if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture); - font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, values, glyphCount); - if (font.texture.id == 0) font = GetFontDefault(); - - RAYGUI_FREE(values); - } + // Load text data from file + // NOTE: Expected an UTF-8 array of codepoints, no separation + char *textData = LoadFileText(TextFormat("%s/%s", GetDirectoryPath(fileName), charmapFileName)); + codepoints = LoadCodepoints(textData, &codepointCount); + UnloadFileText(textData); } - else + + if (fontFileName[0] != '\0') { + // In case a font is already loaded and it is not default internal font, unload it if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture); - font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, NULL, 0); - if (font.texture.id == 0) font = GetFontDefault(); + + if (codepointCount > 0) font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, codepoints, codepointCount); + else font = LoadFontEx(TextFormat("%s/%s", GetDirectoryPath(fileName), fontFileName), fontSize, NULL, 0); // Default to 95 standard codepoints } + // If font texture not properly loaded, revert to default font and size/spacing + if (font.texture.id == 0) + { + font = GetFontDefault(); + GuiSetStyle(DEFAULT, TEXT_SIZE, 10); + GuiSetStyle(DEFAULT, TEXT_SPACING, 1); + } + + UnloadCodepoints(codepoints); + if ((font.texture.id > 0) && (font.glyphCount > 0)) GuiSetFont(font); } break; @@ -3674,12 +4063,12 @@ void GuiLoadStyle(const char *fileName) if (fileDataSize > 0) { - unsigned char *fileData = (unsigned char *)RL_MALLOC(fileDataSize*sizeof(unsigned char)); + unsigned char *fileData = (unsigned char *)RAYGUI_MALLOC(fileDataSize*sizeof(unsigned char)); fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile); GuiLoadStyleFromMemory(fileData, fileDataSize); - RL_FREE(fileData); + RAYGUI_FREE(fileData); } fclose(rgsFile); @@ -3695,6 +4084,8 @@ void GuiLoadStyleDefault(void) guiStyleLoaded = true; // Initialize default LIGHT style property values + // WARNING: Default value are applied to all controls on set but + // they can be overwritten later on for every custom control GuiSetStyle(DEFAULT, BORDER_COLOR_NORMAL, 0x838383ff); GuiSetStyle(DEFAULT, BASE_COLOR_NORMAL, 0xc9c9c9ff); GuiSetStyle(DEFAULT, TEXT_COLOR_NORMAL, 0x686868ff); @@ -3707,17 +4098,29 @@ void GuiLoadStyleDefault(void) GuiSetStyle(DEFAULT, BORDER_COLOR_DISABLED, 0xb5c1c2ff); GuiSetStyle(DEFAULT, BASE_COLOR_DISABLED, 0xe6e9e9ff); GuiSetStyle(DEFAULT, TEXT_COLOR_DISABLED, 0xaeb7b8ff); - GuiSetStyle(DEFAULT, BORDER_WIDTH, 1); // WARNING: Some controls use other values - GuiSetStyle(DEFAULT, TEXT_PADDING, 0); // WARNING: Some controls use other values - GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); // WARNING: Some controls use other values + GuiSetStyle(DEFAULT, BORDER_WIDTH, 1); + GuiSetStyle(DEFAULT, TEXT_PADDING, 0); + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + + // Initialize default extended property values + // NOTE: By default, extended property values are initialized to 0 + GuiSetStyle(DEFAULT, TEXT_SIZE, 10); // DEFAULT, shared by all controls + GuiSetStyle(DEFAULT, TEXT_SPACING, 1); // DEFAULT, shared by all controls + GuiSetStyle(DEFAULT, LINE_COLOR, 0x90abb5ff); // DEFAULT specific property + GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0xf5f5f5ff); // DEFAULT specific property + GuiSetStyle(DEFAULT, TEXT_LINE_SPACING, 15); // DEFAULT, 15 pixels between lines + GuiSetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL, TEXT_ALIGN_MIDDLE); // DEFAULT, text aligned vertically to middle of text-bounds // Initialize control-specific property values // NOTE: Those properties are in default list but require specific values by control type GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); GuiSetStyle(BUTTON, BORDER_WIDTH, 2); GuiSetStyle(SLIDER, TEXT_PADDING, 4); + GuiSetStyle(PROGRESSBAR, TEXT_PADDING, 4); GuiSetStyle(CHECKBOX, TEXT_PADDING, 4); GuiSetStyle(CHECKBOX, TEXT_ALIGNMENT, TEXT_ALIGN_RIGHT); + GuiSetStyle(DROPDOWNBOX, TEXT_PADDING, 0); + GuiSetStyle(DROPDOWNBOX, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); GuiSetStyle(TEXTBOX, TEXT_PADDING, 4); GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); GuiSetStyle(VALUEBOX, TEXT_PADDING, 0); @@ -3729,10 +4132,6 @@ void GuiLoadStyleDefault(void) // Initialize extended property values // NOTE: By default, extended property values are initialized to 0 - GuiSetStyle(DEFAULT, TEXT_SIZE, 10); // DEFAULT, shared by all controls - GuiSetStyle(DEFAULT, TEXT_SPACING, 1); // DEFAULT, shared by all controls - GuiSetStyle(DEFAULT, LINE_COLOR, 0x90abb5ff); // DEFAULT specific property - GuiSetStyle(DEFAULT, BACKGROUND_COLOR, 0xf5f5f5ff); // DEFAULT specific property GuiSetStyle(TOGGLE, GROUP_PADDING, 2); GuiSetStyle(SLIDER, SLIDER_WIDTH, 16); GuiSetStyle(SLIDER, SLIDER_PADDING, 1); @@ -3742,8 +4141,6 @@ void GuiLoadStyleDefault(void) GuiSetStyle(COMBOBOX, COMBO_BUTTON_SPACING, 2); GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16); GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 2); - GuiSetStyle(TEXTBOX, TEXT_LINES_SPACING, (int)((float)GuiGetStyle(DEFAULT, TEXT_SIZE)*1.5f)); - GuiSetStyle(TEXTBOX, TEXT_INNER_PADDING, 4); GuiSetStyle(SPINNER, SPIN_BUTTON_WIDTH, 24); GuiSetStyle(SPINNER, SPIN_BUTTON_SPACING, 2); GuiSetStyle(SCROLLBAR, BORDER_WIDTH, 0); @@ -3767,12 +4164,17 @@ void GuiLoadStyleDefault(void) { // Unload previous font texture UnloadTexture(guiFont.texture); + RL_FREE(guiFont.recs); + RL_FREE(guiFont.glyphs); + guiFont.recs = NULL; + guiFont.glyphs = NULL; // Setup default raylib font guiFont = GetFontDefault(); // NOTE: Default raylib font character 95 is a white square Rectangle whiteChar = guiFont.recs[95]; + // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering SetShapesTexture(guiFont.texture, RAYGUI_CLITERAL(Rectangle){ whiteChar.x + 1, whiteChar.y + 1, whiteChar.width - 2, whiteChar.height - 2 }); } @@ -3901,7 +4303,7 @@ void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color) if (BIT_CHECK(guiIconsPtr[iconId*RAYGUI_ICON_DATA_ELEMENTS + i], k)) { #if !defined(RAYGUI_STANDALONE) - DrawRectangle(posX + (k%RAYGUI_ICON_SIZE)*pixelSize, posY + y*pixelSize, pixelSize, pixelSize, color); + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ (float)posX + (k%RAYGUI_ICON_SIZE)*pixelSize, (float)posY + y*pixelSize, (float)pixelSize, (float)pixelSize }, 0, BLANK, color); #endif } @@ -3922,7 +4324,8 @@ void GuiSetIconScale(int scale) // Module specific Functions Definition //---------------------------------------------------------------------------------- -// Load style from memory (binary only) +// Load style from memory +// WARNING: Binary files only static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize) { unsigned char *fileDataPtr = (unsigned char *)fileData; @@ -3977,7 +4380,6 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize) { Font font = { 0 }; int fontType = 0; // 0-Normal, 1-SDF - Rectangle whiteRec = { 0 }; memcpy(&font.baseSize, fileDataPtr, sizeof(int)); memcpy(&font.glyphCount, fileDataPtr + 4, sizeof(int)); @@ -3985,7 +4387,8 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize) fileDataPtr += 12; // Load font white rectangle - memcpy(&whiteRec, fileDataPtr, sizeof(Rectangle)); + Rectangle fontWhiteRec = { 0 }; + memcpy(&fontWhiteRec, fileDataPtr, sizeof(Rectangle)); fileDataPtr += 16; // Load font image parameters @@ -4002,7 +4405,7 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize) memcpy(&imFont.format, fileDataPtr + 4 + 4, sizeof(int)); fileDataPtr += 12; - if (fontImageCompSize < fontImageUncompSize) + if ((fontImageCompSize > 0) && (fontImageCompSize != fontImageUncompSize)) { // Compressed font atlas image data (DEFLATE), it requires DecompressData() int dataUncompSize = 0; @@ -4027,34 +4430,117 @@ static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize) if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture); font.texture = LoadTextureFromImage(imFont); - if (font.texture.id == 0) font = GetFontDefault(); RAYGUI_FREE(imFont.data); - // Load font recs data - font.recs = (Rectangle *)RAYGUI_CALLOC(font.glyphCount, sizeof(Rectangle)); - for (int i = 0; i < font.glyphCount; i++) + // Validate font atlas texture was loaded correctly + if (font.texture.id != 0) { - memcpy(&font.recs[i], fileDataPtr, sizeof(Rectangle)); - fileDataPtr += sizeof(Rectangle); - } + // Load font recs data + int recsDataSize = font.glyphCount*sizeof(Rectangle); + int recsDataCompressedSize = 0; - // Load font chars info data - font.glyphs = (GlyphInfo *)RAYGUI_CALLOC(font.glyphCount, sizeof(GlyphInfo)); - for (int i = 0; i < font.glyphCount; i++) - { - memcpy(&font.glyphs[i].value, fileDataPtr, sizeof(int)); - memcpy(&font.glyphs[i].offsetX, fileDataPtr + 4, sizeof(int)); - memcpy(&font.glyphs[i].offsetY, fileDataPtr + 8, sizeof(int)); - memcpy(&font.glyphs[i].advanceX, fileDataPtr + 12, sizeof(int)); - fileDataPtr += 16; + // WARNING: Version 400 adds the compression size parameter + if (version >= 400) + { + // RGS files version 400 support compressed recs data + memcpy(&recsDataCompressedSize, fileDataPtr, sizeof(int)); + fileDataPtr += sizeof(int); + } + + if ((recsDataCompressedSize > 0) && (recsDataCompressedSize != recsDataSize)) + { + // Recs data is compressed, uncompress it + unsigned char *recsDataCompressed = (unsigned char *)RAYGUI_MALLOC(recsDataCompressedSize); + + memcpy(recsDataCompressed, fileDataPtr, recsDataCompressedSize); + fileDataPtr += recsDataCompressedSize; + + int recsDataUncompSize = 0; + font.recs = (Rectangle *)DecompressData(recsDataCompressed, recsDataCompressedSize, &recsDataUncompSize); + + // Security check, data uncompressed size must match the expected original data size + if (recsDataUncompSize != recsDataSize) RAYGUI_LOG("WARNING: Uncompressed font recs data could be corrupted"); + + RAYGUI_FREE(recsDataCompressed); + } + else + { + // Recs data is uncompressed + font.recs = (Rectangle *)RAYGUI_CALLOC(font.glyphCount, sizeof(Rectangle)); + for (int i = 0; i < font.glyphCount; i++) + { + memcpy(&font.recs[i], fileDataPtr, sizeof(Rectangle)); + fileDataPtr += sizeof(Rectangle); + } + } + + // Load font glyphs info data + int glyphsDataSize = font.glyphCount*16; // 16 bytes data per glyph + int glyphsDataCompressedSize = 0; + + // WARNING: Version 400 adds the compression size parameter + if (version >= 400) + { + // RGS files version 400 support compressed glyphs data + memcpy(&glyphsDataCompressedSize, fileDataPtr, sizeof(int)); + fileDataPtr += sizeof(int); + } + + // Allocate required glyphs space to fill with data + font.glyphs = (GlyphInfo *)RAYGUI_CALLOC(font.glyphCount, sizeof(GlyphInfo)); + + if ((glyphsDataCompressedSize > 0) && (glyphsDataCompressedSize != glyphsDataSize)) + { + // Glyphs data is compressed, uncompress it + unsigned char *glypsDataCompressed = (unsigned char *)RAYGUI_MALLOC(glyphsDataCompressedSize); + + memcpy(glypsDataCompressed, fileDataPtr, glyphsDataCompressedSize); + fileDataPtr += glyphsDataCompressedSize; + + int glyphsDataUncompSize = 0; + unsigned char *glyphsDataUncomp = DecompressData(glypsDataCompressed, glyphsDataCompressedSize, &glyphsDataUncompSize); + + // Security check, data uncompressed size must match the expected original data size + if (glyphsDataUncompSize != glyphsDataSize) RAYGUI_LOG("WARNING: Uncompressed font glyphs data could be corrupted"); + + unsigned char *glyphsDataUncompPtr = glyphsDataUncomp; + + for (int i = 0; i < font.glyphCount; i++) + { + memcpy(&font.glyphs[i].value, glyphsDataUncompPtr, sizeof(int)); + memcpy(&font.glyphs[i].offsetX, glyphsDataUncompPtr + 4, sizeof(int)); + memcpy(&font.glyphs[i].offsetY, glyphsDataUncompPtr + 8, sizeof(int)); + memcpy(&font.glyphs[i].advanceX, glyphsDataUncompPtr + 12, sizeof(int)); + glyphsDataUncompPtr += 16; + } + + RAYGUI_FREE(glypsDataCompressed); + RAYGUI_FREE(glyphsDataUncomp); + } + else + { + // Glyphs data is uncompressed + for (int i = 0; i < font.glyphCount; i++) + { + memcpy(&font.glyphs[i].value, fileDataPtr, sizeof(int)); + memcpy(&font.glyphs[i].offsetX, fileDataPtr + 4, sizeof(int)); + memcpy(&font.glyphs[i].offsetY, fileDataPtr + 8, sizeof(int)); + memcpy(&font.glyphs[i].advanceX, fileDataPtr + 12, sizeof(int)); + fileDataPtr += 16; + } + } } + else font = GetFontDefault(); // Fallback in case of errors loading font atlas texture GuiSetFont(font); // Set font texture source rectangle to be used as white texture to draw shapes - // NOTE: This way, all gui can be draw using a single draw call - if ((whiteRec.width != 0) && (whiteRec.height != 0)) SetShapesTexture(font.texture, whiteRec); + // NOTE: It makes possible to draw shapes and text (full UI) in a single draw call + if ((fontWhiteRec.x > 0) && + (fontWhiteRec.y > 0) && + (fontWhiteRec.width > 0) && + (fontWhiteRec.height > 0)) SetShapesTexture(font.texture, fontWhiteRec); } #endif } @@ -4109,10 +4595,10 @@ static int GetTextWidth(const char *text) int codepoint = GetCodepointNext(&text[i], &codepointSize); int codepointIndex = GetGlyphIndex(guiFont, codepoint); - if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); - else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor + GuiGetStyle(DEFAULT, TEXT_SPACING)); + if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor); + else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor); - textSize.x += glyphWidth; + textSize.x += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); } } @@ -4130,15 +4616,23 @@ static Rectangle GetTextBounds(int control, Rectangle bounds) textBounds.x = bounds.x + GuiGetStyle(control, BORDER_WIDTH); textBounds.y = bounds.y + GuiGetStyle(control, BORDER_WIDTH) + GuiGetStyle(control, TEXT_PADDING); textBounds.width = bounds.width - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING); - textBounds.height = bounds.height - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING); + textBounds.height = bounds.height - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING); // NOTE: Text is processed line per line! - // Consider TEXT_PADDING properly, depends on control type and TEXT_ALIGNMENT - // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, LISTVIEW (scrollbar?) - // More special cases (label on side): CHECKBOX, SLIDER, VALUEBOX, SPINNER + // Depending on control, TEXT_PADDING and TEXT_ALIGNMENT properties could affect the text-bounds switch (control) { + case COMBOBOX: + case DROPDOWNBOX: + case LISTVIEW: + // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, LISTVIEW + case SLIDER: + case CHECKBOX: + case VALUEBOX: + case SPINNER: + // TODO: More special cases (label on side): SLIDER, CHECKBOX, VALUEBOX, SPINNER default: { + // TODO: WARNING: TEXT_ALIGNMENT is already considered in GuiDrawText() if (GuiGetStyle(control, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT) textBounds.x -= GuiGetStyle(control, TEXT_PADDING); else textBounds.x += GuiGetStyle(control, TEXT_PADDING); } @@ -4192,13 +4686,13 @@ const char **GetTextLines(const char *text, int *count) lines[0] = text; int len = 0; *count = 1; - int lineSize = 0; // Stores current line size, not returned + //int lineSize = 0; // Stores current line size, not returned for (int i = 0, k = 0; (i < textSize) && (*count < RAYGUI_MAX_TEXT_LINES); i++) { if (text[i] == '\n') { - lineSize = len; + //lineSize = len; k++; lines[k] = &text[i + 1]; // WARNING: next value is valid? len = 0; @@ -4212,8 +4706,37 @@ const char **GetTextLines(const char *text, int *count) return lines; } +// Get text width to next space for provided string +static float GetNextSpaceWidth(const char *text, int *nextSpaceIndex) +{ + float width = 0; + int codepointByteCount = 0; + int codepoint = 0; + int index = 0; + float glyphWidth = 0; + float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize; + + for (int i = 0; text[i] != '\0'; i++) + { + if (text[i] != ' ') + { + codepoint = GetCodepoint(&text[i], &codepointByteCount); + index = GetGlyphIndex(guiFont, codepoint); + glyphWidth = (guiFont.glyphs[index].advanceX == 0)? guiFont.recs[index].width*scaleFactor : guiFont.glyphs[index].advanceX*scaleFactor; + width += (glyphWidth + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + } + else + { + *nextSpaceIndex = i; + break; + } + } + + return width; +} + // Gui draw text using default font -static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color tint) +static void GuiDrawText(const char *text, Rectangle textBounds, int alignment, Color tint) { #define TEXT_VALIGN_PIXEL_OFFSET(h) ((int)h%2) // Vertical alignment for pixel perfect @@ -4221,119 +4744,172 @@ static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color #define ICON_TEXT_PADDING 4 #endif - int alignmentVertical = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL); + if ((text == NULL) || (text[0] == '\0')) return; // Security check - // We process the text lines one by one - if ((text != NULL) && (text[0] != '\0')) + // PROCEDURE: + // - Text is processed line per line + // - For every line, horizontal alignment is defined + // - For all text, vertical alignment is defined (multiline text only) + // - For every line, wordwrap mode is checked (useful for GuitextBox(), read-only) + + // Get text lines (using '\n' as delimiter) to be processed individually + // WARNING: We can't use GuiTextSplit() function because it can be already used + // before the GuiDrawText() call and its buffer is static, it would be overriden :( + int lineCount = 0; + const char **lines = GetTextLines(text, &lineCount); + + // Text style variables + //int alignment = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT); + int alignmentVertical = GuiGetStyle(DEFAULT, TEXT_ALIGNMENT_VERTICAL); + int wrapMode = GuiGetStyle(DEFAULT, TEXT_WRAP_MODE); // Wrap-mode only available in read-only mode, no for text editing + + // TODO: WARNING: This totalHeight is not valid for vertical alignment in case of word-wrap + float totalHeight = (float)(lineCount*GuiGetStyle(DEFAULT, TEXT_SIZE) + (lineCount - 1)*GuiGetStyle(DEFAULT, TEXT_SIZE)/2); + float posOffsetY = 0.0f; + + for (int i = 0; i < lineCount; i++) { - // Get text lines ('\n' delimiter) to process lines individually - // NOTE: We can't use GuiTextSplit() because it can be already used before calling - // GuiDrawText() and static buffer would be overriden :( - int lineCount = 0; - const char **lines = GetTextLines(text, &lineCount); + int iconId = 0; + lines[i] = GetTextIcon(lines[i], &iconId); // Check text for icon and move cursor - //Rectangle textBounds = GetTextBounds(LABEL, bounds); - float totalHeight = (float)(lineCount*GuiGetStyle(DEFAULT, TEXT_SIZE) + (lineCount - 1)*GuiGetStyle(DEFAULT, TEXT_SIZE)/2); - float posOffsetY = 0; + // Get text position depending on alignment and iconId + //--------------------------------------------------------------------------------- + Vector2 textBoundsPosition = { textBounds.x, textBounds.y }; - for (int i = 0; i < lineCount; i++) + // NOTE: We get text size after icon has been processed + // WARNING: GetTextWidth() also processes text icon to get width! -> Really needed? + int textSizeX = GetTextWidth(lines[i]); + + // If text requires an icon, add size to measure + if (iconId >= 0) { - int iconId = 0; - lines[i] = GetTextIcon(lines[i], &iconId); // Check text for icon and move cursor + textSizeX += RAYGUI_ICON_SIZE*guiIconScale; - // Get text position depending on alignment and iconId - //--------------------------------------------------------------------------------- - Vector2 position = { bounds.x, bounds.y }; - - // NOTE: We get text size after icon has been processed - // WARNING: GetTextWidth() also processes text icon to get width! -> Really needed? - int textSizeX = GetTextWidth(lines[i]); - - // If text requires an icon, add size to measure - if (iconId >= 0) - { - textSizeX += RAYGUI_ICON_SIZE*guiIconScale; - - // WARNING: If only icon provided, text could be pointing to EOF character: '\0' - if ((lines[i] != NULL) && (lines[i][0] != '\0')) textSizeX += ICON_TEXT_PADDING; - } - - // Check guiTextAlign global variables - switch (alignment) - { - case TEXT_ALIGN_LEFT: position.x = bounds.x; break; - case TEXT_ALIGN_CENTER: position.x = bounds.x + bounds.width/2 - textSizeX/2; break; - case TEXT_ALIGN_RIGHT: position.x = bounds.x + bounds.width - textSizeX; break; - default: break; - } - - switch (alignmentVertical) - { - case 0: position.y = bounds.y + posOffsetY + bounds.height/2 - totalHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); break; // CENTERED - case 1: position.y = bounds.y + posOffsetY; break; // UP - case 2: position.y = bounds.y + posOffsetY + bounds.height - totalHeight + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); break; // DOWN - default: break; - } - - // NOTE: Make sure we get pixel-perfect coordinates, - // In case of decimals we got weird text positioning - position.x = (float)((int)position.x); - position.y = (float)((int)position.y); - //--------------------------------------------------------------------------------- - - // Draw text (with icon if available) - //--------------------------------------------------------------------------------- + // WARNING: If only icon provided, text could be pointing to EOF character: '\0' #if !defined(RAYGUI_NO_ICONS) - if (iconId >= 0) - { - // NOTE: We consider icon height, probably different than text size - GuiDrawIcon(iconId, (int)position.x, (int)(bounds.y + bounds.height/2 - RAYGUI_ICON_SIZE*guiIconScale/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height)), guiIconScale, tint); - position.x += (RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); - } + if ((lines[i] != NULL) && (lines[i][0] != '\0')) textSizeX += ICON_TEXT_PADDING; #endif - //DrawTextEx(guiFont, text, position, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING), tint); + } - // Get size in bytes of text, - // considering end of line and line break - int size = 0; - for (int c = 0; (lines[i][c] != '\0') && (lines[i][c] != '\n'); c++, size++){ } - float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize; + // Check guiTextAlign global variables + switch (alignment) + { + case TEXT_ALIGN_LEFT: textBoundsPosition.x = textBounds.x; break; + case TEXT_ALIGN_CENTER: textBoundsPosition.x = textBounds.x + textBounds.width/2 - textSizeX/2; break; + case TEXT_ALIGN_RIGHT: textBoundsPosition.x = textBounds.x + textBounds.width - textSizeX; break; + default: break; + } - int textOffsetY = 0; - float textOffsetX = 0.0f; - for (int c = 0, codepointSize = 0; c < size; c += codepointSize) + switch (alignmentVertical) + { + // Only valid in case of wordWrap = 0; + case TEXT_ALIGN_TOP: textBoundsPosition.y = textBounds.y + posOffsetY; break; + case TEXT_ALIGN_MIDDLE: textBoundsPosition.y = textBounds.y + posOffsetY + textBounds.height/2 - totalHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height); break; + case TEXT_ALIGN_BOTTOM: textBoundsPosition.y = textBounds.y + posOffsetY + textBounds.height - totalHeight + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height); break; + default: break; + } + + // NOTE: Make sure we get pixel-perfect coordinates, + // In case of decimals we got weird text positioning + textBoundsPosition.x = (float)((int)textBoundsPosition.x); + textBoundsPosition.y = (float)((int)textBoundsPosition.y); + //--------------------------------------------------------------------------------- + + // Draw text (with icon if available) + //--------------------------------------------------------------------------------- +#if !defined(RAYGUI_NO_ICONS) + if (iconId >= 0) + { + // NOTE: We consider icon height, probably different than text size + GuiDrawIcon(iconId, (int)textBoundsPosition.x, (int)(textBounds.y + textBounds.height/2 - RAYGUI_ICON_SIZE*guiIconScale/2 + TEXT_VALIGN_PIXEL_OFFSET(textBounds.height)), guiIconScale, tint); + textBoundsPosition.x += (RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); + } +#endif + // Get size in bytes of text, + // considering end of line and line break + int lineSize = 0; + for (int c = 0; (lines[i][c] != '\0') && (lines[i][c] != '\n') && (lines[i][c] != '\r'); c++, lineSize++){ } + float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize; + + int textOffsetY = 0; + float textOffsetX = 0.0f; + float glyphWidth = 0; + for (int c = 0, codepointSize = 0; c < lineSize; c += codepointSize) + { + int codepoint = GetCodepointNext(&lines[i][c], &codepointSize); + int index = GetGlyphIndex(guiFont, codepoint); + + // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) + // but we need to draw all of the bad bytes using the '?' symbol moving one byte + if (codepoint == 0x3f) codepointSize = 1; // TODO: Review not recognized codepoints size + + // Wrap mode text measuring to space to validate if it can be drawn or + // a new line is required + if (wrapMode == TEXT_WRAP_CHAR) { - int codepoint = GetCodepointNext(&lines[i][c], &codepointSize); - int index = GetGlyphIndex(guiFont, codepoint); + // Get glyph width to check if it goes out of bounds + if (guiFont.glyphs[index].advanceX == 0) glyphWidth = ((float)guiFont.recs[index].width*scaleFactor); + else glyphWidth = (float)guiFont.glyphs[index].advanceX*scaleFactor; - // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) - // but we need to draw all of the bad bytes using the '?' symbol moving one byte - if (codepoint == 0x3f) codepointSize = 1; - - if (codepoint == '\n') break; // WARNING: Lines are already processed manually, no need to keep drawing after this codepoint - else + // Jump to next line if current character reach end of the box limits + if ((textOffsetX + glyphWidth) > textBounds.width) { - if ((codepoint != ' ') && (codepoint != '\t')) - { - // Draw only required text glyphs fitting the bounds.width - if (textOffsetX < (bounds.width - guiFont.recs[index].width)) - { - DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ position.x + textOffsetX, position.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), tint); - } - } - - if (guiFont.glyphs[index].advanceX == 0) textOffsetX += ((float)guiFont.recs[index].width*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); - else textOffsetX += ((float)guiFont.glyphs[index].advanceX*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + textOffsetX = 0.0f; + textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); } } + else if (wrapMode == TEXT_WRAP_WORD) + { + // Get width to next space in line + int nextSpaceIndex = 0; + float nextSpaceWidth = GetNextSpaceWidth(lines[i] + c, &nextSpaceIndex); - // TODO: Allow users to set line spacing for text: GuiSetStyle(TEXTBOX, TEXT_LINES_SPACING, x) - posOffsetY += (float)GuiGetStyle(DEFAULT, TEXT_SIZE)*1.5f; - //--------------------------------------------------------------------------------- + if ((textOffsetX + nextSpaceWidth) > textBounds.width) + { + textOffsetX = 0.0f; + textOffsetY += GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); + } + + // TODO: Consider case: (nextSpaceWidth >= textBounds.width) + } + + if (codepoint == '\n') break; // WARNING: Lines are already processed manually, no need to keep drawing after this codepoint + else + { + // TODO: There are multiple types of spaces in Unicode, + // maybe it's a good idea to add support for more: http://jkorpela.fi/chars/spaces.html + if ((codepoint != ' ') && (codepoint != '\t')) // Do not draw codepoints with no glyph + { + if (wrapMode == TEXT_WRAP_NONE) + { + // Draw only required text glyphs fitting the textBounds.width + if (textOffsetX <= (textBounds.width - glyphWidth)) + { + DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha)); + } + } + else if ((wrapMode == TEXT_WRAP_CHAR) || (wrapMode == TEXT_WRAP_WORD)) + { + // Draw only glyphs inside the bounds + if ((textBoundsPosition.y + textOffsetY) <= (textBounds.y + textBounds.height - GuiGetStyle(DEFAULT, TEXT_SIZE))) + { + DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ textBoundsPosition.x + textOffsetX, textBoundsPosition.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), GuiFade(tint, guiAlpha)); + } + } + } + + if (guiFont.glyphs[index].advanceX == 0) textOffsetX += ((float)guiFont.recs[index].width*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + else textOffsetX += ((float)guiFont.glyphs[index].advanceX*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + } } + + if (wrapMode == TEXT_WRAP_NONE) posOffsetY += (float)GuiGetStyle(DEFAULT, TEXT_LINE_SPACING); + else if ((wrapMode == TEXT_WRAP_CHAR) || (wrapMode == TEXT_WRAP_WORD)) posOffsetY += (textOffsetY + (float)GuiGetStyle(DEFAULT, TEXT_LINE_SPACING)); + //--------------------------------------------------------------------------------- } + #if defined(RAYGUI_DEBUG_TEXT_BOUNDS) - GuiDrawRectangle(bounds, 0, WHITE, Fade(RED, 0.4f)); + GuiDrawRectangle(textBounds, 0, WHITE, Fade(BLUE, 0.4f)); #endif } @@ -4343,17 +4919,21 @@ static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, if (color.a > 0) { // Draw rectangle filled with color - DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, color); + DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, GuiFade(color, guiAlpha)); } if (borderWidth > 0) { // Draw rectangle border lines with color - DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, borderWidth, borderColor); - DrawRectangle((int)rec.x, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, borderColor); - DrawRectangle((int)rec.x + (int)rec.width - borderWidth, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, borderColor); - DrawRectangle((int)rec.x, (int)rec.y + (int)rec.height - borderWidth, (int)rec.width, borderWidth, borderColor); + DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, borderWidth, GuiFade(borderColor, guiAlpha)); + DrawRectangle((int)rec.x, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, GuiFade(borderColor, guiAlpha)); + DrawRectangle((int)rec.x + (int)rec.width - borderWidth, (int)rec.y + borderWidth, borderWidth, (int)rec.height - 2*borderWidth, GuiFade(borderColor, guiAlpha)); + DrawRectangle((int)rec.x, (int)rec.y + (int)rec.height - borderWidth, (int)rec.width, borderWidth, GuiFade(borderColor, guiAlpha)); } + +#if defined(RAYGUI_DEBUG_RECS_BOUNDS) + DrawRectangle((int)rec.x, (int)rec.y, (int)rec.width, (int)rec.height, Fade(RED, 0.4f)); +#endif } // Draw tooltip using control bounds @@ -4629,12 +5209,16 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) if (guiSliderDragging) // Keep dragging outside of bounds { - if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON) && + !CheckCollisionPointRec(mousePoint, arrowUpLeft) && + !CheckCollisionPointRec(mousePoint, arrowDownRight)) { if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) { - if (isVertical) value += (int)(GetMouseDelta().y/(scrollbar.height - slider.height)*valueRange); - else value += (int)(GetMouseDelta().x/(scrollbar.width - slider.width)*valueRange); + state = STATE_PRESSED; + + if (isVertical) value = (int)(((float)(mousePoint.y - scrollbar.y - slider.height/2)*valueRange)/(scrollbar.height - slider.height) + minValue); + else value = (int)(((float)(mousePoint.x - scrollbar.x - slider.width/2)*valueRange)/(scrollbar.width - slider.width) + minValue); } } else @@ -4646,8 +5230,6 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) else if (CheckCollisionPointRec(mousePoint, bounds)) { state = STATE_FOCUSED; - guiSliderDragging = true; - guiSliderActive = bounds; // Store bounds as an identifier when dragging starts // Handle mouse wheel int wheel = (int)GetMouseWheelMove(); @@ -4656,6 +5238,9 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) // Handle mouse button down if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { + guiSliderDragging = true; + guiSliderActive = bounds; // Store bounds as an identifier when dragging starts + // Check arrows click if (CheckCollisionPointRec(mousePoint, arrowUpLeft)) value -= valueRange/GuiGetStyle(SCROLLBAR, SCROLL_SPEED); else if (CheckCollisionPointRec(mousePoint, arrowDownRight)) value += valueRange/GuiGetStyle(SCROLLBAR, SCROLL_SPEED); @@ -4668,11 +5253,6 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) state = STATE_PRESSED; } - else if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) - { - if (isVertical) value += (int)(GetMouseDelta().y/(scrollbar.height - slider.height)*valueRange); - else value += (int)(GetMouseDelta().x/(scrollbar.width - slider.width)*valueRange); - } // Keyboard control on mouse hover scrollbar /* @@ -4697,10 +5277,10 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) // Draw control //-------------------------------------------------------------------- - GuiDrawRectangle(bounds, GuiGetStyle(SCROLLBAR, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), guiAlpha), Fade(GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED)), guiAlpha)); // Draw the background + GuiDrawRectangle(bounds, GuiGetStyle(SCROLLBAR, BORDER_WIDTH), GetColor(GuiGetStyle(LISTVIEW, BORDER + state*3)), GetColor(GuiGetStyle(DEFAULT, BORDER_COLOR_DISABLED))); // Draw the background - GuiDrawRectangle(scrollbar, 0, BLANK, Fade(GetColor(GuiGetStyle(BUTTON, BASE_COLOR_NORMAL)), guiAlpha)); // Draw the scrollbar active area background - GuiDrawRectangle(slider, 0, BLANK, Fade(GetColor(GuiGetStyle(SLIDER, BORDER + state*3)), guiAlpha)); // Draw the slider bar + GuiDrawRectangle(scrollbar, 0, BLANK, GetColor(GuiGetStyle(BUTTON, BASE_COLOR_NORMAL))); // Draw the scrollbar active area background + GuiDrawRectangle(slider, 0, BLANK, GetColor(GuiGetStyle(SLIDER, BORDER + state*3))); // Draw the slider bar // Draw arrows (using icon if available) if (GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE)) @@ -4708,17 +5288,17 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) #if defined(RAYGUI_NO_ICONS) GuiDrawText(isVertical? "^" : "<", RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, - TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); GuiDrawText(isVertical? "v" : ">", RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, - TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3)))); #else GuiDrawText(isVertical? "#121#" : "#118#", RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, - TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3)), guiAlpha)); // ICON_ARROW_UP_FILL / ICON_ARROW_LEFT_FILL + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3))); // ICON_ARROW_UP_FILL / ICON_ARROW_LEFT_FILL GuiDrawText(isVertical? "#120#" : "#119#", RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, - TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3)), guiAlpha)); // ICON_ARROW_DOWN_FILL / ICON_ARROW_RIGHT_FILL + TEXT_ALIGN_CENTER, GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3))); // ICON_ARROW_DOWN_FILL / ICON_ARROW_RIGHT_FILL #endif } //-------------------------------------------------------------------- @@ -4726,6 +5306,18 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) return value; } +// Color fade-in or fade-out, alpha goes from 0.0f to 1.0f +// WARNING: It multiplies current alpha by alpha scale factor +static Color GuiFade(Color color, float alpha) +{ + if (alpha < 0.0f) alpha = 0.0f; + else if (alpha > 1.0f) alpha = 1.0f; + + Color result = { color.r, color.g, color.b, (unsigned char)(color.a*alpha) }; + + return result; +} + #if defined(RAYGUI_STANDALONE) // Returns a Color struct from hexadecimal value static Color GetColor(int hexValue) @@ -4757,17 +5349,6 @@ static bool CheckCollisionPointRec(Vector2 point, Rectangle rec) return collision; } -// Color fade-in or fade-out, alpha goes from 0.0f to 1.0f -static Color Fade(Color color, float alpha) -{ - if (alpha < 0.0f) alpha = 0.0f; - else if (alpha > 1.0f) alpha = 1.0f; - - Color result = { color.r, color.g, color.b, (unsigned char)(255.0f*alpha) }; - - return result; -} - // Formatting of text with variables to 'embed' static const char *TextFormat(const char *text, ...) { From f2389a1e5504feea58f2ac8b482bea1db99684df Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 26 Sep 2023 12:54:24 +0200 Subject: [PATCH 0300/1350] Remove trail spaces --- src/rlgl.h | 8 ++++---- src/rtextures.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index bbf52db25..7a0d7f862 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2998,7 +2998,7 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipWidth = width; int mipHeight = height; int mipOffset = 0; // Mipmap data offset, only used for tracelog - + // NOTE: Added pointer math separately from function to avoid UBSAN complaining unsigned char *dataPtr = (unsigned char *)data; @@ -3403,7 +3403,7 @@ void *rlReadTexturePixels(unsigned int id, int width, int height, int format) // Attach our texture to FBO glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, id, 0); - + // We read data as RGBA because FBO texture is configured as RGBA, despite binding another texture format pixels = (unsigned char *)RL_MALLOC(rlGetPixelDataSize(width, height, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8)); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels); @@ -3697,7 +3697,7 @@ void rlDrawVertexArrayElements(int offset, int count, const void *buffer) // NOTE: Added pointer math separately from function to avoid UBSAN complaining unsigned short *bufferPtr = (unsigned short *)buffer; if (offset > 0) bufferPtr += offset; - + glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)bufferPtr); } @@ -3716,7 +3716,7 @@ void rlDrawVertexArrayElementsInstanced(int offset, int count, const void *buffe // NOTE: Added pointer math separately from function to avoid UBSAN complaining unsigned short *bufferPtr = (unsigned short *)buffer; if (offset > 0) bufferPtr += offset; - + glDrawElementsInstanced(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (const unsigned short *)bufferPtr, instances); #endif } diff --git a/src/rtextures.c b/src/rtextures.c index 8dc49fac5..fa8d91ea4 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -325,7 +325,7 @@ Image LoadImageSvg(const char *fileNameOrString, int width, int height) #if defined(SUPPORT_FILEFORMAT_SVG) bool isSvgStringValid = false; - + // Validate fileName or string if (fileNameOrString != NULL) { @@ -355,7 +355,7 @@ Image LoadImageSvg(const char *fileNameOrString, int width, int height) if (isSvgStringValid) { struct NSVGimage *svgImage = nsvgParse(fileData, "px", 96.0f); - + unsigned char *img = RL_MALLOC(width*height*4); // Calculate scales for both the width and the height From 115ba2b079188d79bee86335972a890e79f1e517 Mon Sep 17 00:00:00 2001 From: WraithGlade <8360085+WraithGlade@users.noreply.github.com> Date: Tue, 26 Sep 2023 12:59:40 -0400 Subject: [PATCH 0301/1350] Factor's Raylib bindings mention being at v4.5 (#3350) I looked at the linked Factor binding page for Raylib and found that the file [summary.txt](https://github.com/factor/factor/blob/master/extra/raylib/summary.txt) has the following text in it: ``` Bindings for Raylib 4.5 ``` The bindings have also been updated within the last week. This implies that Factor's Raylib bindings are either already up to date with v4.5 or will be soon. Thus, I have submitted this likely correction to Raylib's bindings table. (PS: There are other Factor Raylib bindings on the internet that are less up-to-date, so don't confuse those with this.) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 9fe86919d..e3de68251 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -23,7 +23,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-d | **4.5** | [D](https://dlang.org/) | Zlib | https://github.com/schveiguy/raylib-d | | dlang_raylib | 4.0 | [D](https://dlang.org) | MPL-2.0 |https://github.com/rc-05/dlang_raylib | | rayex | 3.7 | [elixir](https://elixir-lang.org/) | Apache-2.0 | https://github.com/shiryel/rayex | -| raylib-factor | 4.0 | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | +| raylib-factor | **4.5** | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | | raylib-freebasic | **4.5** | [FreeBASIC](https://www.freebasic.net/) | MIT | https://github.com/WIITD/raylib-freebasic | | fortran-raylib | **4.5** | [Fortran](https://fortran-lang.org/) | ISC | https://github.com/interkosmos/fortran-raylib | | raylib for Pascal | **4.5** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | Modified Zlib | https://github.com/tinyBigGAMES/raylib | From 9ff47fc807d268d184504e5d0672bac8320367a5 Mon Sep 17 00:00:00 2001 From: XXIV <13811862+thechampagne@users.noreply.github.com> Date: Wed, 27 Sep 2023 21:57:47 +0300 Subject: [PATCH 0302/1350] fix BINDINGS.md (#3358) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index e3de68251..da585f41c 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -44,7 +44,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | | Raylib.nelua | **4.5** | [nelua](https://nelua.io/) | Zlib | https://github.com/Its-Kenta/Raylib-Nelua | | NimraylibNow! | 4.2 | [Nim](https://nim-lang.org/) | MIT | https://github.com/greenfork/nimraylib_now | -| raylib-bindings | **4.5** | [Ruby](https://www.ruby-lang.org/en/) | https://github.com/vaiorabbit/raylib-bindings | +| raylib-bindings | **4.5** | [Ruby](https://www.ruby-lang.org/en/) | Zlib | https://github.com/vaiorabbit/raylib-bindings | | raylib-Forever | auto | [Nim](https://nim-lang.org/) | ? | https://github.com/Guevara-chan/Raylib-Forever | | naylib | auto | [Nim](https://nim-lang.org/) | MIT | https://github.com/planetis-m/naylib | | node-raylib | **4.5** | [Node.js](https://nodejs.org/en/) | Zlib | https://github.com/RobLoach/node-raylib | From 8d5a90ea3c41119df3ee0b52630463ee55463876 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Wed, 27 Sep 2023 14:47:18 -0700 Subject: [PATCH 0303/1350] Expose rcamera functions to the dll so they can be picked up by dll users and bindings that need the dll (#3355) --- src/rcamera.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/rcamera.h b/src/rcamera.h index de169fc39..c999370f5 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -46,6 +46,20 @@ // Defines and Macros //---------------------------------------------------------------------------------- // Function specifiers definition + +// Function specifiers in case library is build/used as a shared library (Windows) +// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll +#if defined(_WIN32) +#if defined(BUILD_LIBTYPE_SHARED) +#if defined(__TINYC__) +#define __declspec(x) __attribute__((x)) +#endif +#define RLAPI __declspec(dllexport) // We are building the library as a Win32 shared library (.dll) +#elif defined(USE_LIBTYPE_SHARED) +#define RLAPI __declspec(dllimport) // We are using the library as a Win32 shared library (.dll) +#endif +#endif + #ifndef RLAPI #define RLAPI // Functions defined as 'extern' by default (implicit specifiers) #endif From 411d0ee4371831a90082907f881eb34c87fb4c51 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 29 Sep 2023 00:28:03 +0200 Subject: [PATCH 0304/1350] Update raylib_parser.c --- parser/raylib_parser.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/parser/raylib_parser.c b/parser/raylib_parser.c index 94ff95626..e1ff37918 100644 --- a/parser/raylib_parser.c +++ b/parser/raylib_parser.c @@ -169,10 +169,12 @@ static FunctionInfo *funcs = NULL; // Command line variables static char apiDefine[32] = { 0 }; // Functions define (i.e. RLAPI for raylib.h, RMDEF for raymath.h, etc.) static char truncAfter[32] = { 0 }; // Truncate marker (i.e. "RLGL IMPLEMENTATION" for rlgl.h) -static char inFileName[512] = { 0 }; // Input file name (required in case of provided through CLI) -static char outFileName[512] = { 0 }; // Output file name (required for file save/export) static int outputFormat = DEFAULT; +// NOTE: Max length depends on OS, in Windows MAX_PATH = 256 +static char inFileName[512] = { 0 }; // Input file name (required in case of drag & drop over executable) +static char outFileName[512] = { 0 }; // Output file name (required for file save/export) + //---------------------------------------------------------------------------------- // Module Functions Declaration //---------------------------------------------------------------------------------- @@ -499,7 +501,7 @@ int main(int argc, char* argv[]) (ch == '/') || (ch == ' ') || (ch == '\t')) continue; - + // Read number operand else if (isdigit(ch)) { @@ -534,7 +536,7 @@ int main(int argc, char* argv[]) int numberType; if (isFloat) numberType = valuePtr[c - 1] == 'f' ? FLOAT_MATH : DOUBLE_MATH; else numberType = valuePtr[c - 1] == 'L' ? LONG_MATH : INT_MATH; - + if (numberType > largestType) largestType = numberType; } else @@ -654,7 +656,7 @@ int main(int argc, char* argv[]) { if (structs[i].fieldName[originalIndex][c] == ',') additionalFields++; } - + if (additionalFields > 0) { int originalLength = -1; @@ -702,7 +704,7 @@ int main(int argc, char* argv[]) { if (structs[i].fieldType[originalIndex][c] == ',') additionalFields++; } - + if (additionalFields > 0) { // Copy original name to last additional field From 73b54c862e9034b7158980c0e6eb3b264fac4729 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 29 Sep 2023 00:28:16 +0200 Subject: [PATCH 0305/1350] Update raylib.h --- src/raylib.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 635b3dfb7..f52f3460d 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1331,7 +1331,7 @@ RLAPI TextureCubemap LoadTextureCubemap(Image image, int layout); RLAPI RenderTexture2D LoadRenderTexture(int width, int height); // Load texture for rendering (framebuffer) RLAPI bool IsTextureReady(Texture2D texture); // Check if a texture is ready RLAPI void UnloadTexture(Texture2D texture); // Unload texture from GPU memory (VRAM) -RLAPI bool IsRenderTextureReady(RenderTexture2D target); // Check if a render texture is ready +RLAPI bool IsRenderTextureReady(RenderTexture2D target); // Check if a render texture is ready RLAPI void UnloadRenderTexture(RenderTexture2D target); // Unload render texture from GPU memory (VRAM) RLAPI void UpdateTexture(Texture2D texture, const void *pixels); // Update GPU texture with new data RLAPI void UpdateTextureRec(Texture2D texture, Rectangle rec, const void *pixels); // Update GPU texture rectangle with new data @@ -1379,7 +1379,7 @@ RLAPI Font LoadFontFromMemory(const char *fileType, const unsigned char *fileDat RLAPI bool IsFontReady(Font font); // Check if a font is ready RLAPI GlyphInfo *LoadFontData(const unsigned char *fileData, int dataSize, int fontSize, int *codepoints, int codepointCount, int type); // Load font data for further use RLAPI Image GenImageFontAtlas(const GlyphInfo *glyphs, Rectangle **glyphRecs, int glyphCount, int fontSize, int padding, int packMethod); // Generate image font atlas using chars info -RLAPI void UnloadFontData(GlyphInfo *glyphs, int glyphCount); // Unload font chars info data (RAM) +RLAPI void UnloadFontData(GlyphInfo *glyphs, int glyphCount); // Unload font chars info data (RAM) RLAPI void UnloadFont(Font font); // Unload font from GPU memory (VRAM) RLAPI bool ExportFontAsCode(Font font, const char *fileName); // Export font as code file, returns true on success @@ -1541,7 +1541,7 @@ RLAPI Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileDat RLAPI bool IsWaveReady(Wave wave); // Checks if wave data is ready RLAPI Sound LoadSound(const char *fileName); // Load sound from file RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data -RLAPI Sound LoadSoundAlias(Sound source); // Create a new sound that shares the same sample data as the source sound, does not own the sound data +RLAPI Sound LoadSoundAlias(Sound source); // Create a new sound that shares the same sample data as the source sound, does not own the sound data RLAPI bool IsSoundReady(Sound sound); // Checks if a sound is ready RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data RLAPI void UnloadWave(Wave wave); // Unload wave data @@ -1598,7 +1598,7 @@ RLAPI void SetAudioStreamVolume(AudioStream stream, float volume); // Set vol RLAPI void SetAudioStreamPitch(AudioStream stream, float pitch); // Set pitch for audio stream (1.0 is base level) RLAPI void SetAudioStreamPan(AudioStream stream, float pan); // Set pan for audio stream (0.5 is centered) RLAPI void SetAudioStreamBufferSizeDefault(int size); // Default size for new audio streams -RLAPI void SetAudioStreamCallback(AudioStream stream, AudioCallback callback); // Audio thread callback to request new data +RLAPI void SetAudioStreamCallback(AudioStream stream, AudioCallback callback); // Audio thread callback to request new data RLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream, receives the samples as s RLAPI void DetachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Detach audio stream processor from stream From c3a1c8c8eb8c9b7fac20fa6993e4663a84be17f1 Mon Sep 17 00:00:00 2001 From: BLUELOVETH Date: Mon, 2 Oct 2023 04:56:43 +0800 Subject: [PATCH 0306/1350] Update BINDINGS.md with raylib-pkpy-bindings (#3361) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index da585f41c..47a1c0a27 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -59,6 +59,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylibpyctbg | **4.5** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylibpyctbg | | raylib-py | **4.5** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylib-py | | raylib-python-ctypes | 4.6-dev | [Python](https://www.python.org/) | MIT | https://github.com/sDos280/raylib-python-ctypes | +| raylib-pkpy-bindings | 4.6-dev | [pocketpy](https://pocketpy.dev/) | MIT | https://github.com/blueloveTH/pkpy-bindings | | raylib-php | **4.5** | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/joseph-montanez/raylib-php | | raylib-phpcpp | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/oraoto/raylib-phpcpp | | raylibr | 4.0 | [R](https://www.r-project.org) | MIT | https://github.com/jeroenjanssens/raylibr | From 7351240218849b1579db962f30e42fb982400c7e Mon Sep 17 00:00:00 2001 From: Brian E <72316548+Brian-ED@users.noreply.github.com> Date: Sun, 1 Oct 2023 22:00:25 +0100 Subject: [PATCH 0307/1350] [rcore] reveiwed GetWorldToScreenEx (#3351) * reveiwed GetWorldToScreenEx Used the inputted "width" instead of global CORE.window. Used Vector3Transform instead of quaternion. * reverted accidental unrelated change * reverted Vector3Transform back * fixed mistyped result --------- Co-authored-by: Brian-E --- src/rcore.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 26ff79d9a..3ba0404d7 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2952,7 +2952,7 @@ Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int heigh } else if (camera.projection == CAMERA_ORTHOGRAPHIC) { - float aspect = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height; + double aspect = ((double)width/(double)height); double top = camera.fovy/2.0; double right = top*aspect; @@ -2963,8 +2963,6 @@ Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int heigh // Calculate view matrix from camera look at (and transpose it) Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up); - // TODO: Why not use Vector3Transform(Vector3 v, Matrix mat)? - // Convert world position vector to quaternion Quaternion worldPos = { position.x, position.y, position.z, 1.0f }; From bc15c19518968878b68bbfe8eac3fe4297f11770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Almeida?= <60551627+luis605@users.noreply.github.com> Date: Sun, 1 Oct 2023 22:01:59 +0100 Subject: [PATCH 0308/1350] Texture Tiling Example - luis605 (#3353) * Texture Tiling Example - luis605 * Removed SetTraceLogLevel(LOG_WARNING); --- .../resources/shaders/glsl330/tiling.fs | 14 +++ examples/shaders/shader_texture_tiling.c | 94 +++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 examples/shaders/resources/shaders/glsl330/tiling.fs create mode 100644 examples/shaders/shader_texture_tiling.c diff --git a/examples/shaders/resources/shaders/glsl330/tiling.fs b/examples/shaders/resources/shaders/glsl330/tiling.fs new file mode 100644 index 000000000..6e7f52434 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/tiling.fs @@ -0,0 +1,14 @@ +#version 330 core + +uniform sampler2D diffuseMap; +uniform vec2 tiling; + +in vec2 fragTexCoord; + +out vec4 fragColor; + +void main() +{ + vec2 texCoord = fragTexCoord * tiling; + fragColor = texture(diffuseMap, texCoord); +} diff --git a/examples/shaders/shader_texture_tiling.c b/examples/shaders/shader_texture_tiling.c new file mode 100644 index 000000000..868d6b8f1 --- /dev/null +++ b/examples/shaders/shader_texture_tiling.c @@ -0,0 +1,94 @@ +/******************************************************************************************* +* +* raylib [textures] example - Texture Tiling +* +* Example demonstrates how to tile a texture on a 3D model using raylib. +* +* Example contributed by Luís Almeida (https://github.com/luis605) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2023 Luís Almeida (https://github.com/luis605) +* +********************************************************************************************/ + +#include "raylib.h" + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ + +int main(void) +{ + const int screenWidth = 800; + const int screenHeight = 600; + + // Initialization + //-------------------------------------------------------------------------------------- + InitWindow(screenWidth, screenHeight, "Raylib Texture Tiling"); + + SetTargetFPS(60); + + // Load a texture + Texture2D texture = LoadTexture("resources/raylib_logo.png"); + + // Create a cube mesh + Mesh cube = GenMeshCube(1.0f, 1.0f, 1.0f); + + // Load the texture onto the GPU + Model model = LoadModelFromMesh(cube); + model.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = texture; + + // Set the tiling of the texture + float tiling[2] = {3.0f, 3.0f}; + Shader shader = LoadShader(0, "resources/shaders/glsl330/tiling.fs"); // Create a custom shader in a .glsl file + SetShaderValue(shader, GetShaderLocation(shader, "tiling"), tiling, SHADER_UNIFORM_VEC2); + model.materials[0].shader = shader; + + // Camera setup + Camera camera = { 0 }; + camera.position = (Vector3){ 3.0f, 3.0f, 3.0f }; + camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; + camera.fovy = 45.0f; + camera.projection = CAMERA_PERSPECTIVE; + + // Main game loop + while (!WindowShouldClose()) + { + // Update + //---------------------------------------------------------------------------------- + + BeginDrawing(); + ClearBackground(RAYWHITE); + UpdateCamera(&camera, CAMERA_FREE); + + // Draw the model + { + BeginMode3D(camera); + BeginShaderMode(shader); + + DrawModel(model, (Vector3){ 0.0f, 0.0f, 0.0f }, 5.0f, WHITE); + + EndShaderMode(); + EndMode3D(); + } + + DrawText("Use mouse to rotate the camera", 10, 10, 20, DARKGRAY); + + EndDrawing(); + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + + UnloadTexture(texture); // Unload texture + UnloadModel(model); // Unload model + UnloadShader(shader); // Unload shader + + + CloseWindow(); // Close window and OpenGL context + + return 0; +} From da5407b77651dc3ffb6ff9df466ac0ec57d82e4f Mon Sep 17 00:00:00 2001 From: DaveH355 <101005658+DaveH355@users.noreply.github.com> Date: Sun, 8 Oct 2023 03:02:05 +0800 Subject: [PATCH 0309/1350] Optimize m3d mesh creation (#3363) * Optimize m3d mesh creation * Avoid qsort() in rmodels.c * Revert "Avoid qsort() in rmodels.c" This reverts commit dc1bd559fdda8d338d480dd7d9c3d77bb1ec5ac2. * Add comment --- src/rmodels.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/rmodels.c b/src/rmodels.c index f9ceec137..c25fe3342 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5567,6 +5567,15 @@ static Model LoadVOX(const char *fileName) unsigned char *m3d_loaderhook(char *fn, unsigned int *len) { return LoadFileData((const char *)fn, (int *)len); } void m3d_freehook(void *data) { UnloadFileData((unsigned char *)data); } +// Comparison function for qsort +static int m3d_compare_faces(const void *a, const void *b) +{ + m3df_t *fa = (m3df_t *)a; + m3df_t *fb = (m3df_t *)b; + + return (fa->materialid - fb->materialid); +} + // Load M3D mesh data static Model LoadM3D(const char *fileName) { @@ -5614,6 +5623,9 @@ static Model LoadM3D(const char *fileName) // We always need a default material, so we add +1 model.materialCount++; + // failsafe, model should already have faces grouped by material + qsort(m3d->face, m3d->numface, sizeof(m3df_t), m3d_compare_faces); + model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); model.materials = (Material *)RL_CALLOC(model.materialCount + 1, sizeof(Material)); From 52ba44c47407fe14ba57c312045b6200099b9117 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 7 Oct 2023 21:07:50 +0200 Subject: [PATCH 0310/1350] REVIEWED: #3363 --- src/rmodels.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index c25fe3342..27fe4d85f 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5567,15 +5567,6 @@ static Model LoadVOX(const char *fileName) unsigned char *m3d_loaderhook(char *fn, unsigned int *len) { return LoadFileData((const char *)fn, (int *)len); } void m3d_freehook(void *data) { UnloadFileData((unsigned char *)data); } -// Comparison function for qsort -static int m3d_compare_faces(const void *a, const void *b) -{ - m3df_t *fa = (m3df_t *)a; - m3df_t *fb = (m3df_t *)b; - - return (fa->materialid - fb->materialid); -} - // Load M3D mesh data static Model LoadM3D(const char *fileName) { @@ -5623,8 +5614,19 @@ static Model LoadM3D(const char *fileName) // We always need a default material, so we add +1 model.materialCount++; - // failsafe, model should already have faces grouped by material + // WARNING: Sorting is not needed, valid M3D model files should already be sorted + // faces should already by grouped by material + // Just keeping the sorting function for reference (Check PR #3363) + /* + static int m3d_compare_faces(const void *a, const void *b) + { + m3df_t *fa = (m3df_t *)a; + m3df_t *fb = (m3df_t *)b; + return (fa->materialid - fb->materialid); + } + qsort(m3d->face, m3d->numface, sizeof(m3df_t), m3d_compare_faces); + */ model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); @@ -5692,7 +5694,7 @@ static Model LoadM3D(const char *fileName) model.meshes[k].vertices[l*9 + 7] = m3d->vertex[m3d->face[i].vertex[2]].y*m3d->scale; model.meshes[k].vertices[l*9 + 8] = m3d->vertex[m3d->face[i].vertex[2]].z*m3d->scale; - // without vertex color (full transparency), we use the default color + // Without vertex color (full transparency), we use the default color if (model.meshes[k].colors != NULL) { if (m3d->vertex[m3d->face[i].vertex[0]].color & 0xFF000000) From 97c2744a16ad7e6cf6b546a7a6416ec6b21e3a6c Mon Sep 17 00:00:00 2001 From: BLUELOVETH Date: Sun, 8 Oct 2023 03:10:27 +0800 Subject: [PATCH 0311/1350] Update `raylib_api.*` (#3379) --- parser/output/raylib_api.json | 281 ++++++-- parser/output/raylib_api.lua | 209 ++++-- parser/output/raylib_api.txt | 1158 +++++++++++++++++---------------- parser/output/raylib_api.xml | 126 ++-- 4 files changed, 1088 insertions(+), 686 deletions(-) diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index b42f03a83..ef94c3d86 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -1428,6 +1428,11 @@ "value": 16384, "description": "Set to support mouse passthrough, only supported when FLAG_WINDOW_UNDECORATED" }, + { + "name": "FLAG_BORDERLESS_WINDOWED_MODE", + "value": 32768, + "description": "Set to run program in borderless windowed mode" + }, { "name": "FLAG_MSAA_4X_HINT", "value": 32, @@ -2605,58 +2610,73 @@ "description": "32*4 bpp (4 channels - float)" }, { - "name": "PIXELFORMAT_COMPRESSED_DXT1_RGB", + "name": "PIXELFORMAT_UNCOMPRESSED_R16", "value": 11, + "description": "16 bpp (1 channel - half float)" + }, + { + "name": "PIXELFORMAT_UNCOMPRESSED_R16G16B16", + "value": 12, + "description": "16*3 bpp (3 channels - half float)" + }, + { + "name": "PIXELFORMAT_UNCOMPRESSED_R16G16B16A16", + "value": 13, + "description": "16*4 bpp (4 channels - half float)" + }, + { + "name": "PIXELFORMAT_COMPRESSED_DXT1_RGB", + "value": 14, "description": "4 bpp (no alpha)" }, { "name": "PIXELFORMAT_COMPRESSED_DXT1_RGBA", - "value": 12, + "value": 15, "description": "4 bpp (1 bit alpha)" }, { "name": "PIXELFORMAT_COMPRESSED_DXT3_RGBA", - "value": 13, + "value": 16, "description": "8 bpp" }, { "name": "PIXELFORMAT_COMPRESSED_DXT5_RGBA", - "value": 14, - "description": "8 bpp" - }, - { - "name": "PIXELFORMAT_COMPRESSED_ETC1_RGB", - "value": 15, - "description": "4 bpp" - }, - { - "name": "PIXELFORMAT_COMPRESSED_ETC2_RGB", - "value": 16, - "description": "4 bpp" - }, - { - "name": "PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA", "value": 17, "description": "8 bpp" }, { - "name": "PIXELFORMAT_COMPRESSED_PVRT_RGB", + "name": "PIXELFORMAT_COMPRESSED_ETC1_RGB", "value": 18, "description": "4 bpp" }, { - "name": "PIXELFORMAT_COMPRESSED_PVRT_RGBA", + "name": "PIXELFORMAT_COMPRESSED_ETC2_RGB", "value": 19, "description": "4 bpp" }, { - "name": "PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA", + "name": "PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA", "value": 20, "description": "8 bpp" }, { - "name": "PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA", + "name": "PIXELFORMAT_COMPRESSED_PVRT_RGB", "value": 21, + "description": "4 bpp" + }, + { + "name": "PIXELFORMAT_COMPRESSED_PVRT_RGBA", + "value": 22, + "description": "4 bpp" + }, + { + "name": "PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA", + "value": 23, + "description": "8 bpp" + }, + { + "name": "PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA", + "value": 24, "description": "2 bpp" } ] @@ -2986,8 +3006,8 @@ "name": "fileName" }, { - "type": "unsigned int *", - "name": "bytesRead" + "type": "int *", + "name": "dataSize" } ] }, @@ -3005,8 +3025,8 @@ "name": "data" }, { - "type": "unsigned int", - "name": "bytesToWrite" + "type": "int", + "name": "dataSize" } ] }, @@ -3155,6 +3175,11 @@ "description": "Toggle window state: fullscreen/windowed (only PLATFORM_DESKTOP)", "returnType": "void" }, + { + "name": "ToggleBorderlessWindowed", + "description": "Toggle window state: borderless windowed (only PLATFORM_DESKTOP)", + "returnType": "void" + }, { "name": "MaximizeWindow", "description": "Set window state: maximized, if resizable (only PLATFORM_DESKTOP)", @@ -3198,7 +3223,7 @@ }, { "name": "SetWindowTitle", - "description": "Set title for window (only PLATFORM_DESKTOP)", + "description": "Set title for window (only PLATFORM_DESKTOP and PLATFORM_WEB)", "returnType": "void", "params": [ { @@ -3224,7 +3249,7 @@ }, { "name": "SetWindowMonitor", - "description": "Set monitor for the current window (fullscreen mode)", + "description": "Set monitor for the current window", "returnType": "void", "params": [ { @@ -3248,6 +3273,21 @@ } ] }, + { + "name": "SetWindowMaxSize", + "description": "Set window maximum dimensions (for FLAG_WINDOW_RESIZABLE)", + "returnType": "void", + "params": [ + { + "type": "int", + "name": "width" + }, + { + "type": "int", + "name": "height" + } + ] + }, { "name": "SetWindowSize", "description": "Set window dimensions", @@ -3274,6 +3314,11 @@ } ] }, + { + "name": "SetWindowFocused", + "description": "Set window focused (only PLATFORM_DESKTOP)", + "returnType": "void" + }, { "name": "GetWindowHandle", "description": "Get native window handle", @@ -3387,7 +3432,7 @@ }, { "name": "GetMonitorName", - "description": "Get the human-readable, UTF-8 encoded name of the primary monitor", + "description": "Get the human-readable, UTF-8 encoded name of the specified monitor", "returnType": "const char *", "params": [ { @@ -4132,8 +4177,8 @@ "name": "fileName" }, { - "type": "unsigned int *", - "name": "bytesRead" + "type": "int *", + "name": "dataSize" } ] }, @@ -4162,8 +4207,8 @@ "name": "data" }, { - "type": "unsigned int", - "name": "bytesToWrite" + "type": "int", + "name": "dataSize" } ] }, @@ -4177,8 +4222,8 @@ "name": "data" }, { - "type": "unsigned int", - "name": "size" + "type": "int", + "name": "dataSize" }, { "type": "const char *", @@ -4333,7 +4378,7 @@ }, { "name": "GetApplicationDirectory", - "description": "Get the directory if the running application (uses static string)", + "description": "Get the directory of the running application (uses static string)", "returnType": "const char *" }, { @@ -4514,6 +4559,17 @@ } ] }, + { + "name": "IsKeyPressedRepeat", + "description": "Check if a key has been pressed again (Only PLATFORM_DESKTOP)", + "returnType": "bool", + "params": [ + { + "type": "int", + "name": "key" + } + ] + }, { "name": "IsKeyDown", "description": "Check if a key is being pressed", @@ -4876,7 +4932,7 @@ "returnType": "bool", "params": [ { - "type": "int", + "type": "unsigned int", "name": "gesture" } ] @@ -5148,6 +5204,52 @@ } ] }, + { + "name": "DrawLineBSpline", + "description": "Draw a B-Spline line, minimum 4 points", + "returnType": "void", + "params": [ + { + "type": "Vector2 *", + "name": "points" + }, + { + "type": "int", + "name": "pointCount" + }, + { + "type": "float", + "name": "thick" + }, + { + "type": "Color", + "name": "color" + } + ] + }, + { + "name": "DrawLineCatmullRom", + "description": "Draw a Catmull Rom spline line, minimum 4 points", + "returnType": "void", + "params": [ + { + "type": "Vector2 *", + "name": "points" + }, + { + "type": "int", + "name": "pointCount" + }, + { + "type": "float", + "name": "thick" + }, + { + "type": "Color", + "name": "color" + } + ] + }, { "name": "DrawLineStrip", "description": "Draw lines sequence", @@ -6119,6 +6221,25 @@ } ] }, + { + "name": "LoadImageSvg", + "description": "Load image from SVG file data or string with specified size", + "returnType": "Image", + "params": [ + { + "type": "const char *", + "name": "fileNameOrString" + }, + { + "type": "int", + "name": "width" + }, + { + "type": "int", + "name": "height" + } + ] + }, { "name": "LoadImageAnim", "description": "Load image sequence from file (frames appended to image.data)", @@ -6206,6 +6327,25 @@ } ] }, + { + "name": "ExportImageToMemory", + "description": "Export image to memory buffer", + "returnType": "unsigned char *", + "params": [ + { + "type": "Image", + "name": "image" + }, + { + "type": "const char *", + "name": "fileType" + }, + { + "type": "int *", + "name": "fileSize" + } + ] + }, { "name": "ExportImageAsCode", "description": "Export image as code file defining an array of bytes, returns true on success", @@ -6759,7 +6899,7 @@ }, { "name": "ImageRotate", - "description": "Rotate image by input angle in degrees (-359 to 359) ", + "description": "Rotate image by input angle in degrees (-359 to 359)", "returnType": "void", "params": [ { @@ -7930,7 +8070,7 @@ }, { "name": "LoadFontEx", - "description": "Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set", + "description": "Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character setFont", "returnType": "Font", "params": [ { @@ -7943,11 +8083,11 @@ }, { "type": "int *", - "name": "fontChars" + "name": "codepoints" }, { "type": "int", - "name": "glyphCount" + "name": "codepointCount" } ] }, @@ -7993,11 +8133,11 @@ }, { "type": "int *", - "name": "fontChars" + "name": "codepoints" }, { "type": "int", - "name": "glyphCount" + "name": "codepointCount" } ] }, @@ -8031,11 +8171,11 @@ }, { "type": "int *", - "name": "fontChars" + "name": "codepoints" }, { "type": "int", - "name": "glyphCount" + "name": "codepointCount" }, { "type": "int", @@ -8050,11 +8190,11 @@ "params": [ { "type": "const GlyphInfo *", - "name": "chars" + "name": "glyphs" }, { "type": "Rectangle **", - "name": "recs" + "name": "glyphRecs" }, { "type": "int", @@ -8081,7 +8221,7 @@ "params": [ { "type": "GlyphInfo *", - "name": "chars" + "name": "glyphs" }, { "type": "int", @@ -8269,7 +8409,7 @@ }, { "type": "int", - "name": "count" + "name": "codepointCount" }, { "type": "Vector2", @@ -8289,6 +8429,17 @@ } ] }, + { + "name": "SetTextLineSpacing", + "description": "Set vertical line spacing when drawing with line-breaks", + "returnType": "void", + "params": [ + { + "type": "int", + "name": "spacing" + } + ] + }, { "name": "MeasureText", "description": "Measure string width for default font", @@ -9937,7 +10088,7 @@ "name": "fileName" }, { - "type": "unsigned int *", + "type": "int *", "name": "animCount" } ] @@ -9982,8 +10133,8 @@ "name": "animations" }, { - "type": "unsigned int", - "name": "count" + "type": "int", + "name": "animCount" } ] }, @@ -10251,6 +10402,17 @@ } ] }, + { + "name": "LoadSoundAlias", + "description": "Create a new sound that shares the same sample data as the source sound, does not own the sound data", + "returnType": "Sound", + "params": [ + { + "type": "Sound", + "name": "source" + } + ] + }, { "name": "IsSoundReady", "description": "Checks if a sound is ready", @@ -10303,6 +10465,17 @@ } ] }, + { + "name": "UnloadSoundAlias", + "description": "Unload a sound alias (does not deallocate sample data)", + "returnType": "void", + "params": [ + { + "type": "Sound", + "name": "alias" + } + ] + }, { "name": "ExportWave", "description": "Export wave data to file, returns true on success", @@ -10907,7 +11080,7 @@ }, { "name": "AttachAudioStreamProcessor", - "description": "Attach audio stream processor to stream", + "description": "Attach audio stream processor to stream, receives the samples as s", "returnType": "void", "params": [ { @@ -10937,7 +11110,7 @@ }, { "name": "AttachAudioMixedProcessor", - "description": "Attach audio stream processor to the entire audio pipeline", + "description": "Attach audio stream processor to the entire audio pipeline, receives the samples as s", "returnType": "void", "params": [ { diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index ed11ac08f..26b2564a5 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -1428,6 +1428,11 @@ return { value = 16384, description = "Set to support mouse passthrough, only supported when FLAG_WINDOW_UNDECORATED" }, + { + name = "FLAG_BORDERLESS_WINDOWED_MODE", + value = 32768, + description = "Set to run program in borderless windowed mode" + }, { name = "FLAG_MSAA_4X_HINT", value = 32, @@ -2605,58 +2610,73 @@ return { description = "32*4 bpp (4 channels - float)" }, { - name = "PIXELFORMAT_COMPRESSED_DXT1_RGB", + name = "PIXELFORMAT_UNCOMPRESSED_R16", value = 11, + description = "16 bpp (1 channel - half float)" + }, + { + name = "PIXELFORMAT_UNCOMPRESSED_R16G16B16", + value = 12, + description = "16*3 bpp (3 channels - half float)" + }, + { + name = "PIXELFORMAT_UNCOMPRESSED_R16G16B16A16", + value = 13, + description = "16*4 bpp (4 channels - half float)" + }, + { + name = "PIXELFORMAT_COMPRESSED_DXT1_RGB", + value = 14, description = "4 bpp (no alpha)" }, { name = "PIXELFORMAT_COMPRESSED_DXT1_RGBA", - value = 12, + value = 15, description = "4 bpp (1 bit alpha)" }, { name = "PIXELFORMAT_COMPRESSED_DXT3_RGBA", - value = 13, + value = 16, description = "8 bpp" }, { name = "PIXELFORMAT_COMPRESSED_DXT5_RGBA", - value = 14, - description = "8 bpp" - }, - { - name = "PIXELFORMAT_COMPRESSED_ETC1_RGB", - value = 15, - description = "4 bpp" - }, - { - name = "PIXELFORMAT_COMPRESSED_ETC2_RGB", - value = 16, - description = "4 bpp" - }, - { - name = "PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA", value = 17, description = "8 bpp" }, { - name = "PIXELFORMAT_COMPRESSED_PVRT_RGB", + name = "PIXELFORMAT_COMPRESSED_ETC1_RGB", value = 18, description = "4 bpp" }, { - name = "PIXELFORMAT_COMPRESSED_PVRT_RGBA", + name = "PIXELFORMAT_COMPRESSED_ETC2_RGB", value = 19, description = "4 bpp" }, { - name = "PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA", + name = "PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA", value = 20, description = "8 bpp" }, { - name = "PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA", + name = "PIXELFORMAT_COMPRESSED_PVRT_RGB", value = 21, + description = "4 bpp" + }, + { + name = "PIXELFORMAT_COMPRESSED_PVRT_RGBA", + value = 22, + description = "4 bpp" + }, + { + name = "PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA", + value = 23, + description = "8 bpp" + }, + { + name = "PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA", + value = 24, description = "2 bpp" } } @@ -2973,7 +2993,7 @@ return { returnType = "unsigned char *", params = { {type = "const char *", name = "fileName"}, - {type = "unsigned int *", name = "bytesRead"} + {type = "int *", name = "dataSize"} } }, { @@ -2983,7 +3003,7 @@ return { params = { {type = "const char *", name = "fileName"}, {type = "void *", name = "data"}, - {type = "unsigned int", name = "bytesToWrite"} + {type = "int", name = "dataSize"} } }, { @@ -3098,6 +3118,11 @@ return { description = "Toggle window state: fullscreen/windowed (only PLATFORM_DESKTOP)", returnType = "void" }, + { + name = "ToggleBorderlessWindowed", + description = "Toggle window state: borderless windowed (only PLATFORM_DESKTOP)", + returnType = "void" + }, { name = "MaximizeWindow", description = "Set window state: maximized, if resizable (only PLATFORM_DESKTOP)", @@ -3132,7 +3157,7 @@ return { }, { name = "SetWindowTitle", - description = "Set title for window (only PLATFORM_DESKTOP)", + description = "Set title for window (only PLATFORM_DESKTOP and PLATFORM_WEB)", returnType = "void", params = { {type = "const char *", name = "title"} @@ -3149,7 +3174,7 @@ return { }, { name = "SetWindowMonitor", - description = "Set monitor for the current window (fullscreen mode)", + description = "Set monitor for the current window", returnType = "void", params = { {type = "int", name = "monitor"} @@ -3164,6 +3189,15 @@ return { {type = "int", name = "height"} } }, + { + name = "SetWindowMaxSize", + description = "Set window maximum dimensions (for FLAG_WINDOW_RESIZABLE)", + returnType = "void", + params = { + {type = "int", name = "width"}, + {type = "int", name = "height"} + } + }, { name = "SetWindowSize", description = "Set window dimensions", @@ -3181,6 +3215,11 @@ return { {type = "float", name = "opacity"} } }, + { + name = "SetWindowFocused", + description = "Set window focused (only PLATFORM_DESKTOP)", + returnType = "void" + }, { name = "GetWindowHandle", description = "Get native window handle", @@ -3276,7 +3315,7 @@ return { }, { name = "GetMonitorName", - description = "Get the human-readable, UTF-8 encoded name of the primary monitor", + description = "Get the human-readable, UTF-8 encoded name of the specified monitor", returnType = "const char *", params = { {type = "int", name = "monitor"} @@ -3792,7 +3831,7 @@ return { returnType = "unsigned char *", params = { {type = "const char *", name = "fileName"}, - {type = "unsigned int *", name = "bytesRead"} + {type = "int *", name = "dataSize"} } }, { @@ -3810,7 +3849,7 @@ return { params = { {type = "const char *", name = "fileName"}, {type = "void *", name = "data"}, - {type = "unsigned int", name = "bytesToWrite"} + {type = "int", name = "dataSize"} } }, { @@ -3819,7 +3858,7 @@ return { returnType = "bool", params = { {type = "const unsigned char *", name = "data"}, - {type = "unsigned int", name = "size"}, + {type = "int", name = "dataSize"}, {type = "const char *", name = "fileName"} } }, @@ -3928,7 +3967,7 @@ return { }, { name = "GetApplicationDirectory", - description = "Get the directory if the running application (uses static string)", + description = "Get the directory of the running application (uses static string)", returnType = "const char *" }, { @@ -4046,6 +4085,14 @@ return { {type = "int", name = "key"} } }, + { + name = "IsKeyPressedRepeat", + description = "Check if a key has been pressed again (Only PLATFORM_DESKTOP)", + returnType = "bool", + params = { + {type = "int", name = "key"} + } + }, { name = "IsKeyDown", description = "Check if a key is being pressed", @@ -4311,7 +4358,7 @@ return { description = "Check if a gesture have been detected", returnType = "bool", params = { - {type = "int", name = "gesture"} + {type = "unsigned int", name = "gesture"} } }, { @@ -4461,6 +4508,28 @@ return { {type = "Color", name = "color"} } }, + { + name = "DrawLineBSpline", + description = "Draw a B-Spline line, minimum 4 points", + returnType = "void", + params = { + {type = "Vector2 *", name = "points"}, + {type = "int", name = "pointCount"}, + {type = "float", name = "thick"}, + {type = "Color", name = "color"} + } + }, + { + name = "DrawLineCatmullRom", + description = "Draw a Catmull Rom spline line, minimum 4 points", + returnType = "void", + params = { + {type = "Vector2 *", name = "points"}, + {type = "int", name = "pointCount"}, + {type = "float", name = "thick"}, + {type = "Color", name = "color"} + } + }, { name = "DrawLineStrip", description = "Draw lines sequence", @@ -4919,6 +4988,16 @@ return { {type = "int", name = "headerSize"} } }, + { + name = "LoadImageSvg", + description = "Load image from SVG file data or string with specified size", + returnType = "Image", + params = { + {type = "const char *", name = "fileNameOrString"}, + {type = "int", name = "width"}, + {type = "int", name = "height"} + } + }, { name = "LoadImageAnim", description = "Load image sequence from file (frames appended to image.data)", @@ -4976,6 +5055,16 @@ return { {type = "const char *", name = "fileName"} } }, + { + name = "ExportImageToMemory", + description = "Export image to memory buffer", + returnType = "unsigned char *", + params = { + {type = "Image", name = "image"}, + {type = "const char *", name = "fileType"}, + {type = "int *", name = "fileSize"} + } + }, { name = "ExportImageAsCode", description = "Export image as code file defining an array of bytes, returns true on success", @@ -5268,7 +5357,7 @@ return { }, { name = "ImageRotate", - description = "Rotate image by input angle in degrees (-359 to 359) ", + description = "Rotate image by input angle in degrees (-359 to 359)", returnType = "void", params = { {type = "Image *", name = "image"}, @@ -5911,13 +6000,13 @@ return { }, { name = "LoadFontEx", - description = "Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set", + description = "Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character setFont", returnType = "Font", params = { {type = "const char *", name = "fileName"}, {type = "int", name = "fontSize"}, - {type = "int *", name = "fontChars"}, - {type = "int", name = "glyphCount"} + {type = "int *", name = "codepoints"}, + {type = "int", name = "codepointCount"} } }, { @@ -5939,8 +6028,8 @@ return { {type = "const unsigned char *", name = "fileData"}, {type = "int", name = "dataSize"}, {type = "int", name = "fontSize"}, - {type = "int *", name = "fontChars"}, - {type = "int", name = "glyphCount"} + {type = "int *", name = "codepoints"}, + {type = "int", name = "codepointCount"} } }, { @@ -5959,8 +6048,8 @@ return { {type = "const unsigned char *", name = "fileData"}, {type = "int", name = "dataSize"}, {type = "int", name = "fontSize"}, - {type = "int *", name = "fontChars"}, - {type = "int", name = "glyphCount"}, + {type = "int *", name = "codepoints"}, + {type = "int", name = "codepointCount"}, {type = "int", name = "type"} } }, @@ -5969,8 +6058,8 @@ return { description = "Generate image font atlas using chars info", returnType = "Image", params = { - {type = "const GlyphInfo *", name = "chars"}, - {type = "Rectangle **", name = "recs"}, + {type = "const GlyphInfo *", name = "glyphs"}, + {type = "Rectangle **", name = "glyphRecs"}, {type = "int", name = "glyphCount"}, {type = "int", name = "fontSize"}, {type = "int", name = "padding"}, @@ -5982,7 +6071,7 @@ return { description = "Unload font chars info data (RAM)", returnType = "void", params = { - {type = "GlyphInfo *", name = "chars"}, + {type = "GlyphInfo *", name = "glyphs"}, {type = "int", name = "glyphCount"} } }, @@ -6071,13 +6160,21 @@ return { params = { {type = "Font", name = "font"}, {type = "const int *", name = "codepoints"}, - {type = "int", name = "count"}, + {type = "int", name = "codepointCount"}, {type = "Vector2", name = "position"}, {type = "float", name = "fontSize"}, {type = "float", name = "spacing"}, {type = "Color", name = "tint"} } }, + { + name = "SetTextLineSpacing", + description = "Set vertical line spacing when drawing with line-breaks", + returnType = "void", + params = { + {type = "int", name = "spacing"} + } + }, { name = "MeasureText", description = "Measure string width for default font", @@ -6954,7 +7051,7 @@ return { returnType = "ModelAnimation *", params = { {type = "const char *", name = "fileName"}, - {type = "unsigned int *", name = "animCount"} + {type = "int *", name = "animCount"} } }, { @@ -6981,7 +7078,7 @@ return { returnType = "void", params = { {type = "ModelAnimation *", name = "animations"}, - {type = "unsigned int", name = "count"} + {type = "int", name = "animCount"} } }, { @@ -7140,6 +7237,14 @@ return { {type = "Wave", name = "wave"} } }, + { + name = "LoadSoundAlias", + description = "Create a new sound that shares the same sample data as the source sound, does not own the sound data", + returnType = "Sound", + params = { + {type = "Sound", name = "source"} + } + }, { name = "IsSoundReady", description = "Checks if a sound is ready", @@ -7174,6 +7279,14 @@ return { {type = "Sound", name = "sound"} } }, + { + name = "UnloadSoundAlias", + description = "Unload a sound alias (does not deallocate sample data)", + returnType = "void", + params = { + {type = "Sound", name = "alias"} + } + }, { name = "ExportWave", description = "Export wave data to file, returns true on success", @@ -7568,7 +7681,7 @@ return { }, { name = "AttachAudioStreamProcessor", - description = "Attach audio stream processor to stream", + description = "Attach audio stream processor to stream, receives the samples as s", returnType = "void", params = { {type = "AudioStream", name = "stream"}, @@ -7586,7 +7699,7 @@ return { }, { name = "AttachAudioMixedProcessor", - description = "Attach audio stream processor to the entire audio pipeline", + description = "Attach audio stream processor to the entire audio pipeline, receives the samples as s", returnType = "void", params = { {type = "AudioCallback", name = "processor"} diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index 0d0ccccf1..725d6ae91 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -566,7 +566,7 @@ Alias 005: Camera Enums found: 21 -Enum 01: ConfigFlags (15 values) +Enum 01: ConfigFlags (16 values) Name: ConfigFlags Description: System/Window config flags Value[FLAG_VSYNC_HINT]: 64 @@ -582,6 +582,7 @@ Enum 01: ConfigFlags (15 values) Value[FLAG_WINDOW_TRANSPARENT]: 16 Value[FLAG_WINDOW_HIGHDPI]: 8192 Value[FLAG_WINDOW_MOUSE_PASSTHROUGH]: 16384 + Value[FLAG_BORDERLESS_WINDOWED_MODE]: 32768 Value[FLAG_MSAA_4X_HINT]: 32 Value[FLAG_INTERLACED_HINT]: 65536 Enum 02: TraceLogLevel (8 values) @@ -824,7 +825,7 @@ Enum 11: ShaderAttributeDataType (4 values) Value[SHADER_ATTRIB_VEC2]: 1 Value[SHADER_ATTRIB_VEC3]: 2 Value[SHADER_ATTRIB_VEC4]: 3 -Enum 12: PixelFormat (21 values) +Enum 12: PixelFormat (24 values) Name: PixelFormat Description: Pixel formats Value[PIXELFORMAT_UNCOMPRESSED_GRAYSCALE]: 1 @@ -837,17 +838,20 @@ Enum 12: PixelFormat (21 values) Value[PIXELFORMAT_UNCOMPRESSED_R32]: 8 Value[PIXELFORMAT_UNCOMPRESSED_R32G32B32]: 9 Value[PIXELFORMAT_UNCOMPRESSED_R32G32B32A32]: 10 - Value[PIXELFORMAT_COMPRESSED_DXT1_RGB]: 11 - Value[PIXELFORMAT_COMPRESSED_DXT1_RGBA]: 12 - Value[PIXELFORMAT_COMPRESSED_DXT3_RGBA]: 13 - Value[PIXELFORMAT_COMPRESSED_DXT5_RGBA]: 14 - Value[PIXELFORMAT_COMPRESSED_ETC1_RGB]: 15 - Value[PIXELFORMAT_COMPRESSED_ETC2_RGB]: 16 - Value[PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA]: 17 - Value[PIXELFORMAT_COMPRESSED_PVRT_RGB]: 18 - Value[PIXELFORMAT_COMPRESSED_PVRT_RGBA]: 19 - Value[PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA]: 20 - Value[PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA]: 21 + Value[PIXELFORMAT_UNCOMPRESSED_R16]: 11 + Value[PIXELFORMAT_UNCOMPRESSED_R16G16B16]: 12 + Value[PIXELFORMAT_UNCOMPRESSED_R16G16B16A16]: 13 + Value[PIXELFORMAT_COMPRESSED_DXT1_RGB]: 14 + Value[PIXELFORMAT_COMPRESSED_DXT1_RGBA]: 15 + Value[PIXELFORMAT_COMPRESSED_DXT3_RGBA]: 16 + Value[PIXELFORMAT_COMPRESSED_DXT5_RGBA]: 17 + Value[PIXELFORMAT_COMPRESSED_ETC1_RGB]: 18 + Value[PIXELFORMAT_COMPRESSED_ETC2_RGB]: 19 + Value[PIXELFORMAT_COMPRESSED_ETC2_EAC_RGBA]: 20 + Value[PIXELFORMAT_COMPRESSED_PVRT_RGB]: 21 + Value[PIXELFORMAT_COMPRESSED_PVRT_RGBA]: 22 + Value[PIXELFORMAT_COMPRESSED_ASTC_4x4_RGBA]: 23 + Value[PIXELFORMAT_COMPRESSED_ASTC_8x8_RGBA]: 24 Enum 13: TextureFilter (6 values) Name: TextureFilter Description: Texture parameters: filter mode @@ -938,14 +942,14 @@ Callback 002: LoadFileDataCallback() (2 input parameters) Return type: unsigned char * Description: FileIO: Load binary data Param[1]: fileName (type: const char *) - Param[2]: bytesRead (type: unsigned int *) + Param[2]: dataSize (type: int *) Callback 003: SaveFileDataCallback() (3 input parameters) Name: SaveFileDataCallback Return type: bool Description: FileIO: Save binary data Param[1]: fileName (type: const char *) Param[2]: data (type: void *) - Param[3]: bytesToWrite (type: unsigned int) + Param[3]: dataSize (type: int) Callback 004: LoadFileTextCallback() (1 input parameters) Name: LoadFileTextCallback Return type: char * @@ -964,7 +968,7 @@ Callback 006: AudioCallback() (2 input parameters) Param[1]: bufferData (type: void *) Param[2]: frames (type: unsigned int) -Functions found: 518 +Functions found: 529 Function 001: InitWindow() (3 input parameters) Name: InitWindow @@ -1038,276 +1042,292 @@ Function 014: ToggleFullscreen() (0 input parameters) Return type: void Description: Toggle window state: fullscreen/windowed (only PLATFORM_DESKTOP) No input parameters -Function 015: MaximizeWindow() (0 input parameters) +Function 015: ToggleBorderlessWindowed() (0 input parameters) + Name: ToggleBorderlessWindowed + Return type: void + Description: Toggle window state: borderless windowed (only PLATFORM_DESKTOP) + No input parameters +Function 016: MaximizeWindow() (0 input parameters) Name: MaximizeWindow Return type: void Description: Set window state: maximized, if resizable (only PLATFORM_DESKTOP) No input parameters -Function 016: MinimizeWindow() (0 input parameters) +Function 017: MinimizeWindow() (0 input parameters) Name: MinimizeWindow Return type: void Description: Set window state: minimized, if resizable (only PLATFORM_DESKTOP) No input parameters -Function 017: RestoreWindow() (0 input parameters) +Function 018: RestoreWindow() (0 input parameters) Name: RestoreWindow Return type: void Description: Set window state: not minimized/maximized (only PLATFORM_DESKTOP) No input parameters -Function 018: SetWindowIcon() (1 input parameters) +Function 019: SetWindowIcon() (1 input parameters) Name: SetWindowIcon Return type: void Description: Set icon for window (single image, RGBA 32bit, only PLATFORM_DESKTOP) Param[1]: image (type: Image) -Function 019: SetWindowIcons() (2 input parameters) +Function 020: SetWindowIcons() (2 input parameters) Name: SetWindowIcons Return type: void Description: Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP) Param[1]: images (type: Image *) Param[2]: count (type: int) -Function 020: SetWindowTitle() (1 input parameters) +Function 021: SetWindowTitle() (1 input parameters) Name: SetWindowTitle Return type: void - Description: Set title for window (only PLATFORM_DESKTOP) + Description: Set title for window (only PLATFORM_DESKTOP and PLATFORM_WEB) Param[1]: title (type: const char *) -Function 021: SetWindowPosition() (2 input parameters) +Function 022: SetWindowPosition() (2 input parameters) Name: SetWindowPosition Return type: void Description: Set window position on screen (only PLATFORM_DESKTOP) Param[1]: x (type: int) Param[2]: y (type: int) -Function 022: SetWindowMonitor() (1 input parameters) +Function 023: SetWindowMonitor() (1 input parameters) Name: SetWindowMonitor Return type: void - Description: Set monitor for the current window (fullscreen mode) + Description: Set monitor for the current window Param[1]: monitor (type: int) -Function 023: SetWindowMinSize() (2 input parameters) +Function 024: SetWindowMinSize() (2 input parameters) Name: SetWindowMinSize Return type: void Description: Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE) Param[1]: width (type: int) Param[2]: height (type: int) -Function 024: SetWindowSize() (2 input parameters) +Function 025: SetWindowMaxSize() (2 input parameters) + Name: SetWindowMaxSize + Return type: void + Description: Set window maximum dimensions (for FLAG_WINDOW_RESIZABLE) + Param[1]: width (type: int) + Param[2]: height (type: int) +Function 026: SetWindowSize() (2 input parameters) Name: SetWindowSize Return type: void Description: Set window dimensions Param[1]: width (type: int) Param[2]: height (type: int) -Function 025: SetWindowOpacity() (1 input parameters) +Function 027: SetWindowOpacity() (1 input parameters) Name: SetWindowOpacity Return type: void Description: Set window opacity [0.0f..1.0f] (only PLATFORM_DESKTOP) Param[1]: opacity (type: float) -Function 026: GetWindowHandle() (0 input parameters) +Function 028: SetWindowFocused() (0 input parameters) + Name: SetWindowFocused + Return type: void + Description: Set window focused (only PLATFORM_DESKTOP) + No input parameters +Function 029: GetWindowHandle() (0 input parameters) Name: GetWindowHandle Return type: void * Description: Get native window handle No input parameters -Function 027: GetScreenWidth() (0 input parameters) +Function 030: GetScreenWidth() (0 input parameters) Name: GetScreenWidth Return type: int Description: Get current screen width No input parameters -Function 028: GetScreenHeight() (0 input parameters) +Function 031: GetScreenHeight() (0 input parameters) Name: GetScreenHeight Return type: int Description: Get current screen height No input parameters -Function 029: GetRenderWidth() (0 input parameters) +Function 032: GetRenderWidth() (0 input parameters) Name: GetRenderWidth Return type: int Description: Get current render width (it considers HiDPI) No input parameters -Function 030: GetRenderHeight() (0 input parameters) +Function 033: GetRenderHeight() (0 input parameters) Name: GetRenderHeight Return type: int Description: Get current render height (it considers HiDPI) No input parameters -Function 031: GetMonitorCount() (0 input parameters) +Function 034: GetMonitorCount() (0 input parameters) Name: GetMonitorCount Return type: int Description: Get number of connected monitors No input parameters -Function 032: GetCurrentMonitor() (0 input parameters) +Function 035: GetCurrentMonitor() (0 input parameters) Name: GetCurrentMonitor Return type: int Description: Get current connected monitor No input parameters -Function 033: GetMonitorPosition() (1 input parameters) +Function 036: GetMonitorPosition() (1 input parameters) Name: GetMonitorPosition Return type: Vector2 Description: Get specified monitor position Param[1]: monitor (type: int) -Function 034: GetMonitorWidth() (1 input parameters) +Function 037: GetMonitorWidth() (1 input parameters) Name: GetMonitorWidth Return type: int Description: Get specified monitor width (current video mode used by monitor) Param[1]: monitor (type: int) -Function 035: GetMonitorHeight() (1 input parameters) +Function 038: GetMonitorHeight() (1 input parameters) Name: GetMonitorHeight Return type: int Description: Get specified monitor height (current video mode used by monitor) Param[1]: monitor (type: int) -Function 036: GetMonitorPhysicalWidth() (1 input parameters) +Function 039: GetMonitorPhysicalWidth() (1 input parameters) Name: GetMonitorPhysicalWidth Return type: int Description: Get specified monitor physical width in millimetres Param[1]: monitor (type: int) -Function 037: GetMonitorPhysicalHeight() (1 input parameters) +Function 040: GetMonitorPhysicalHeight() (1 input parameters) Name: GetMonitorPhysicalHeight Return type: int Description: Get specified monitor physical height in millimetres Param[1]: monitor (type: int) -Function 038: GetMonitorRefreshRate() (1 input parameters) +Function 041: GetMonitorRefreshRate() (1 input parameters) Name: GetMonitorRefreshRate Return type: int Description: Get specified monitor refresh rate Param[1]: monitor (type: int) -Function 039: GetWindowPosition() (0 input parameters) +Function 042: GetWindowPosition() (0 input parameters) Name: GetWindowPosition Return type: Vector2 Description: Get window position XY on monitor No input parameters -Function 040: GetWindowScaleDPI() (0 input parameters) +Function 043: GetWindowScaleDPI() (0 input parameters) Name: GetWindowScaleDPI Return type: Vector2 Description: Get window scale DPI factor No input parameters -Function 041: GetMonitorName() (1 input parameters) +Function 044: GetMonitorName() (1 input parameters) Name: GetMonitorName Return type: const char * - Description: Get the human-readable, UTF-8 encoded name of the primary monitor + Description: Get the human-readable, UTF-8 encoded name of the specified monitor Param[1]: monitor (type: int) -Function 042: SetClipboardText() (1 input parameters) +Function 045: SetClipboardText() (1 input parameters) Name: SetClipboardText Return type: void Description: Set clipboard text content Param[1]: text (type: const char *) -Function 043: GetClipboardText() (0 input parameters) +Function 046: GetClipboardText() (0 input parameters) Name: GetClipboardText Return type: const char * Description: Get clipboard text content No input parameters -Function 044: EnableEventWaiting() (0 input parameters) +Function 047: EnableEventWaiting() (0 input parameters) Name: EnableEventWaiting Return type: void Description: Enable waiting for events on EndDrawing(), no automatic event polling No input parameters -Function 045: DisableEventWaiting() (0 input parameters) +Function 048: DisableEventWaiting() (0 input parameters) Name: DisableEventWaiting Return type: void Description: Disable waiting for events on EndDrawing(), automatic events polling No input parameters -Function 046: SwapScreenBuffer() (0 input parameters) +Function 049: SwapScreenBuffer() (0 input parameters) Name: SwapScreenBuffer Return type: void Description: Swap back buffer with front buffer (screen drawing) No input parameters -Function 047: PollInputEvents() (0 input parameters) +Function 050: PollInputEvents() (0 input parameters) Name: PollInputEvents Return type: void Description: Register all input events No input parameters -Function 048: WaitTime() (1 input parameters) +Function 051: WaitTime() (1 input parameters) Name: WaitTime Return type: void Description: Wait for some time (halt program execution) Param[1]: seconds (type: double) -Function 049: ShowCursor() (0 input parameters) +Function 052: ShowCursor() (0 input parameters) Name: ShowCursor Return type: void Description: Shows cursor No input parameters -Function 050: HideCursor() (0 input parameters) +Function 053: HideCursor() (0 input parameters) Name: HideCursor Return type: void Description: Hides cursor No input parameters -Function 051: IsCursorHidden() (0 input parameters) +Function 054: IsCursorHidden() (0 input parameters) Name: IsCursorHidden Return type: bool Description: Check if cursor is not visible No input parameters -Function 052: EnableCursor() (0 input parameters) +Function 055: EnableCursor() (0 input parameters) Name: EnableCursor Return type: void Description: Enables cursor (unlock cursor) No input parameters -Function 053: DisableCursor() (0 input parameters) +Function 056: DisableCursor() (0 input parameters) Name: DisableCursor Return type: void Description: Disables cursor (lock cursor) No input parameters -Function 054: IsCursorOnScreen() (0 input parameters) +Function 057: IsCursorOnScreen() (0 input parameters) Name: IsCursorOnScreen Return type: bool Description: Check if cursor is on the screen No input parameters -Function 055: ClearBackground() (1 input parameters) +Function 058: ClearBackground() (1 input parameters) Name: ClearBackground Return type: void Description: Set background color (framebuffer clear color) Param[1]: color (type: Color) -Function 056: BeginDrawing() (0 input parameters) +Function 059: BeginDrawing() (0 input parameters) Name: BeginDrawing Return type: void Description: Setup canvas (framebuffer) to start drawing No input parameters -Function 057: EndDrawing() (0 input parameters) +Function 060: EndDrawing() (0 input parameters) Name: EndDrawing Return type: void Description: End canvas drawing and swap buffers (double buffering) No input parameters -Function 058: BeginMode2D() (1 input parameters) +Function 061: BeginMode2D() (1 input parameters) Name: BeginMode2D Return type: void Description: Begin 2D mode with custom camera (2D) Param[1]: camera (type: Camera2D) -Function 059: EndMode2D() (0 input parameters) +Function 062: EndMode2D() (0 input parameters) Name: EndMode2D Return type: void Description: Ends 2D mode with custom camera No input parameters -Function 060: BeginMode3D() (1 input parameters) +Function 063: BeginMode3D() (1 input parameters) Name: BeginMode3D Return type: void Description: Begin 3D mode with custom camera (3D) Param[1]: camera (type: Camera3D) -Function 061: EndMode3D() (0 input parameters) +Function 064: EndMode3D() (0 input parameters) Name: EndMode3D Return type: void Description: Ends 3D mode and returns to default 2D orthographic mode No input parameters -Function 062: BeginTextureMode() (1 input parameters) +Function 065: BeginTextureMode() (1 input parameters) Name: BeginTextureMode Return type: void Description: Begin drawing to render texture Param[1]: target (type: RenderTexture2D) -Function 063: EndTextureMode() (0 input parameters) +Function 066: EndTextureMode() (0 input parameters) Name: EndTextureMode Return type: void Description: Ends drawing to render texture No input parameters -Function 064: BeginShaderMode() (1 input parameters) +Function 067: BeginShaderMode() (1 input parameters) Name: BeginShaderMode Return type: void Description: Begin custom shader drawing Param[1]: shader (type: Shader) -Function 065: EndShaderMode() (0 input parameters) +Function 068: EndShaderMode() (0 input parameters) Name: EndShaderMode Return type: void Description: End custom shader drawing (use default shader) No input parameters -Function 066: BeginBlendMode() (1 input parameters) +Function 069: BeginBlendMode() (1 input parameters) Name: BeginBlendMode Return type: void Description: Begin blending mode (alpha, additive, multiplied, subtract, custom) Param[1]: mode (type: int) -Function 067: EndBlendMode() (0 input parameters) +Function 070: EndBlendMode() (0 input parameters) Name: EndBlendMode Return type: void Description: End blending mode (reset to default: alpha blending) No input parameters -Function 068: BeginScissorMode() (4 input parameters) +Function 071: BeginScissorMode() (4 input parameters) Name: BeginScissorMode Return type: void Description: Begin scissor mode (define screen area for following drawing) @@ -1315,61 +1335,61 @@ Function 068: BeginScissorMode() (4 input parameters) Param[2]: y (type: int) Param[3]: width (type: int) Param[4]: height (type: int) -Function 069: EndScissorMode() (0 input parameters) +Function 072: EndScissorMode() (0 input parameters) Name: EndScissorMode Return type: void Description: End scissor mode No input parameters -Function 070: BeginVrStereoMode() (1 input parameters) +Function 073: BeginVrStereoMode() (1 input parameters) Name: BeginVrStereoMode Return type: void Description: Begin stereo rendering (requires VR simulator) Param[1]: config (type: VrStereoConfig) -Function 071: EndVrStereoMode() (0 input parameters) +Function 074: EndVrStereoMode() (0 input parameters) Name: EndVrStereoMode Return type: void Description: End stereo rendering (requires VR simulator) No input parameters -Function 072: LoadVrStereoConfig() (1 input parameters) +Function 075: LoadVrStereoConfig() (1 input parameters) Name: LoadVrStereoConfig Return type: VrStereoConfig Description: Load VR stereo config for VR simulator device parameters Param[1]: device (type: VrDeviceInfo) -Function 073: UnloadVrStereoConfig() (1 input parameters) +Function 076: UnloadVrStereoConfig() (1 input parameters) Name: UnloadVrStereoConfig Return type: void Description: Unload VR stereo config Param[1]: config (type: VrStereoConfig) -Function 074: LoadShader() (2 input parameters) +Function 077: LoadShader() (2 input parameters) Name: LoadShader Return type: Shader Description: Load shader from files and bind default locations Param[1]: vsFileName (type: const char *) Param[2]: fsFileName (type: const char *) -Function 075: LoadShaderFromMemory() (2 input parameters) +Function 078: LoadShaderFromMemory() (2 input parameters) Name: LoadShaderFromMemory Return type: Shader Description: Load shader from code strings and bind default locations Param[1]: vsCode (type: const char *) Param[2]: fsCode (type: const char *) -Function 076: IsShaderReady() (1 input parameters) +Function 079: IsShaderReady() (1 input parameters) Name: IsShaderReady Return type: bool Description: Check if a shader is ready Param[1]: shader (type: Shader) -Function 077: GetShaderLocation() (2 input parameters) +Function 080: GetShaderLocation() (2 input parameters) Name: GetShaderLocation Return type: int Description: Get shader uniform location Param[1]: shader (type: Shader) Param[2]: uniformName (type: const char *) -Function 078: GetShaderLocationAttrib() (2 input parameters) +Function 081: GetShaderLocationAttrib() (2 input parameters) Name: GetShaderLocationAttrib Return type: int Description: Get shader attribute location Param[1]: shader (type: Shader) Param[2]: attribName (type: const char *) -Function 079: SetShaderValue() (4 input parameters) +Function 082: SetShaderValue() (4 input parameters) Name: SetShaderValue Return type: void Description: Set shader uniform value @@ -1377,7 +1397,7 @@ Function 079: SetShaderValue() (4 input parameters) Param[2]: locIndex (type: int) Param[3]: value (type: const void *) Param[4]: uniformType (type: int) -Function 080: SetShaderValueV() (5 input parameters) +Function 083: SetShaderValueV() (5 input parameters) Name: SetShaderValueV Return type: void Description: Set shader uniform value vector @@ -1386,54 +1406,54 @@ Function 080: SetShaderValueV() (5 input parameters) Param[3]: value (type: const void *) Param[4]: uniformType (type: int) Param[5]: count (type: int) -Function 081: SetShaderValueMatrix() (3 input parameters) +Function 084: SetShaderValueMatrix() (3 input parameters) Name: SetShaderValueMatrix Return type: void Description: Set shader uniform value (matrix 4x4) Param[1]: shader (type: Shader) Param[2]: locIndex (type: int) Param[3]: mat (type: Matrix) -Function 082: SetShaderValueTexture() (3 input parameters) +Function 085: SetShaderValueTexture() (3 input parameters) Name: SetShaderValueTexture Return type: void Description: Set shader uniform value for texture (sampler2d) Param[1]: shader (type: Shader) Param[2]: locIndex (type: int) Param[3]: texture (type: Texture2D) -Function 083: UnloadShader() (1 input parameters) +Function 086: UnloadShader() (1 input parameters) Name: UnloadShader Return type: void Description: Unload shader from GPU memory (VRAM) Param[1]: shader (type: Shader) -Function 084: GetMouseRay() (2 input parameters) +Function 087: GetMouseRay() (2 input parameters) Name: GetMouseRay Return type: Ray Description: Get a ray trace from mouse position Param[1]: mousePosition (type: Vector2) Param[2]: camera (type: Camera) -Function 085: GetCameraMatrix() (1 input parameters) +Function 088: GetCameraMatrix() (1 input parameters) Name: GetCameraMatrix Return type: Matrix Description: Get camera transform matrix (view matrix) Param[1]: camera (type: Camera) -Function 086: GetCameraMatrix2D() (1 input parameters) +Function 089: GetCameraMatrix2D() (1 input parameters) Name: GetCameraMatrix2D Return type: Matrix Description: Get camera 2d transform matrix Param[1]: camera (type: Camera2D) -Function 087: GetWorldToScreen() (2 input parameters) +Function 090: GetWorldToScreen() (2 input parameters) Name: GetWorldToScreen Return type: Vector2 Description: Get the screen space position for a 3d world space position Param[1]: position (type: Vector3) Param[2]: camera (type: Camera) -Function 088: GetScreenToWorld2D() (2 input parameters) +Function 091: GetScreenToWorld2D() (2 input parameters) Name: GetScreenToWorld2D Return type: Vector2 Description: Get the world space position for a 2d camera screen space position Param[1]: position (type: Vector2) Param[2]: camera (type: Camera2D) -Function 089: GetWorldToScreenEx() (4 input parameters) +Function 092: GetWorldToScreenEx() (4 input parameters) Name: GetWorldToScreenEx Return type: Vector2 Description: Get size position for a 3d world space position @@ -1441,517 +1461,522 @@ Function 089: GetWorldToScreenEx() (4 input parameters) Param[2]: camera (type: Camera) Param[3]: width (type: int) Param[4]: height (type: int) -Function 090: GetWorldToScreen2D() (2 input parameters) +Function 093: GetWorldToScreen2D() (2 input parameters) Name: GetWorldToScreen2D Return type: Vector2 Description: Get the screen space position for a 2d camera world space position Param[1]: position (type: Vector2) Param[2]: camera (type: Camera2D) -Function 091: SetTargetFPS() (1 input parameters) +Function 094: SetTargetFPS() (1 input parameters) Name: SetTargetFPS Return type: void Description: Set target FPS (maximum) Param[1]: fps (type: int) -Function 092: GetFPS() (0 input parameters) +Function 095: GetFPS() (0 input parameters) Name: GetFPS Return type: int Description: Get current FPS No input parameters -Function 093: GetFrameTime() (0 input parameters) +Function 096: GetFrameTime() (0 input parameters) Name: GetFrameTime Return type: float Description: Get time in seconds for last frame drawn (delta time) No input parameters -Function 094: GetTime() (0 input parameters) +Function 097: GetTime() (0 input parameters) Name: GetTime Return type: double Description: Get elapsed time in seconds since InitWindow() No input parameters -Function 095: GetRandomValue() (2 input parameters) +Function 098: GetRandomValue() (2 input parameters) Name: GetRandomValue Return type: int Description: Get a random value between min and max (both included) Param[1]: min (type: int) Param[2]: max (type: int) -Function 096: SetRandomSeed() (1 input parameters) +Function 099: SetRandomSeed() (1 input parameters) Name: SetRandomSeed Return type: void Description: Set the seed for the random number generator Param[1]: seed (type: unsigned int) -Function 097: TakeScreenshot() (1 input parameters) +Function 100: TakeScreenshot() (1 input parameters) Name: TakeScreenshot Return type: void Description: Takes a screenshot of current screen (filename extension defines format) Param[1]: fileName (type: const char *) -Function 098: SetConfigFlags() (1 input parameters) +Function 101: SetConfigFlags() (1 input parameters) Name: SetConfigFlags Return type: void Description: Setup init configuration flags (view FLAGS) Param[1]: flags (type: unsigned int) -Function 099: TraceLog() (3 input parameters) +Function 102: TraceLog() (3 input parameters) Name: TraceLog Return type: void Description: Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...) Param[1]: logLevel (type: int) Param[2]: text (type: const char *) Param[3]: args (type: ...) -Function 100: SetTraceLogLevel() (1 input parameters) +Function 103: SetTraceLogLevel() (1 input parameters) Name: SetTraceLogLevel Return type: void Description: Set the current threshold (minimum) log level Param[1]: logLevel (type: int) -Function 101: MemAlloc() (1 input parameters) +Function 104: MemAlloc() (1 input parameters) Name: MemAlloc Return type: void * Description: Internal memory allocator Param[1]: size (type: unsigned int) -Function 102: MemRealloc() (2 input parameters) +Function 105: MemRealloc() (2 input parameters) Name: MemRealloc Return type: void * Description: Internal memory reallocator Param[1]: ptr (type: void *) Param[2]: size (type: unsigned int) -Function 103: MemFree() (1 input parameters) +Function 106: MemFree() (1 input parameters) Name: MemFree Return type: void Description: Internal memory free Param[1]: ptr (type: void *) -Function 104: OpenURL() (1 input parameters) +Function 107: OpenURL() (1 input parameters) Name: OpenURL Return type: void Description: Open URL with default system browser (if available) Param[1]: url (type: const char *) -Function 105: SetTraceLogCallback() (1 input parameters) +Function 108: SetTraceLogCallback() (1 input parameters) Name: SetTraceLogCallback Return type: void Description: Set custom trace log Param[1]: callback (type: TraceLogCallback) -Function 106: SetLoadFileDataCallback() (1 input parameters) +Function 109: SetLoadFileDataCallback() (1 input parameters) Name: SetLoadFileDataCallback Return type: void Description: Set custom file binary data loader Param[1]: callback (type: LoadFileDataCallback) -Function 107: SetSaveFileDataCallback() (1 input parameters) +Function 110: SetSaveFileDataCallback() (1 input parameters) Name: SetSaveFileDataCallback Return type: void Description: Set custom file binary data saver Param[1]: callback (type: SaveFileDataCallback) -Function 108: SetLoadFileTextCallback() (1 input parameters) +Function 111: SetLoadFileTextCallback() (1 input parameters) Name: SetLoadFileTextCallback Return type: void Description: Set custom file text data loader Param[1]: callback (type: LoadFileTextCallback) -Function 109: SetSaveFileTextCallback() (1 input parameters) +Function 112: SetSaveFileTextCallback() (1 input parameters) Name: SetSaveFileTextCallback Return type: void Description: Set custom file text data saver Param[1]: callback (type: SaveFileTextCallback) -Function 110: LoadFileData() (2 input parameters) +Function 113: LoadFileData() (2 input parameters) Name: LoadFileData Return type: unsigned char * Description: Load file data as byte array (read) Param[1]: fileName (type: const char *) - Param[2]: bytesRead (type: unsigned int *) -Function 111: UnloadFileData() (1 input parameters) + Param[2]: dataSize (type: int *) +Function 114: UnloadFileData() (1 input parameters) Name: UnloadFileData Return type: void Description: Unload file data allocated by LoadFileData() Param[1]: data (type: unsigned char *) -Function 112: SaveFileData() (3 input parameters) +Function 115: SaveFileData() (3 input parameters) Name: SaveFileData Return type: bool Description: Save data to file from byte array (write), returns true on success Param[1]: fileName (type: const char *) Param[2]: data (type: void *) - Param[3]: bytesToWrite (type: unsigned int) -Function 113: ExportDataAsCode() (3 input parameters) + Param[3]: dataSize (type: int) +Function 116: ExportDataAsCode() (3 input parameters) Name: ExportDataAsCode Return type: bool Description: Export data to code (.h), returns true on success Param[1]: data (type: const unsigned char *) - Param[2]: size (type: unsigned int) + Param[2]: dataSize (type: int) Param[3]: fileName (type: const char *) -Function 114: LoadFileText() (1 input parameters) +Function 117: LoadFileText() (1 input parameters) Name: LoadFileText Return type: char * Description: Load text data from file (read), returns a '\0' terminated string Param[1]: fileName (type: const char *) -Function 115: UnloadFileText() (1 input parameters) +Function 118: UnloadFileText() (1 input parameters) Name: UnloadFileText Return type: void Description: Unload file text data allocated by LoadFileText() Param[1]: text (type: char *) -Function 116: SaveFileText() (2 input parameters) +Function 119: SaveFileText() (2 input parameters) Name: SaveFileText Return type: bool Description: Save text data to file (write), string must be '\0' terminated, returns true on success Param[1]: fileName (type: const char *) Param[2]: text (type: char *) -Function 117: FileExists() (1 input parameters) +Function 120: FileExists() (1 input parameters) Name: FileExists Return type: bool Description: Check if file exists Param[1]: fileName (type: const char *) -Function 118: DirectoryExists() (1 input parameters) +Function 121: DirectoryExists() (1 input parameters) Name: DirectoryExists Return type: bool Description: Check if a directory path exists Param[1]: dirPath (type: const char *) -Function 119: IsFileExtension() (2 input parameters) +Function 122: IsFileExtension() (2 input parameters) Name: IsFileExtension Return type: bool Description: Check file extension (including point: .png, .wav) Param[1]: fileName (type: const char *) Param[2]: ext (type: const char *) -Function 120: GetFileLength() (1 input parameters) +Function 123: GetFileLength() (1 input parameters) Name: GetFileLength Return type: int Description: Get file length in bytes (NOTE: GetFileSize() conflicts with windows.h) Param[1]: fileName (type: const char *) -Function 121: GetFileExtension() (1 input parameters) +Function 124: GetFileExtension() (1 input parameters) Name: GetFileExtension Return type: const char * Description: Get pointer to extension for a filename string (includes dot: '.png') Param[1]: fileName (type: const char *) -Function 122: GetFileName() (1 input parameters) +Function 125: GetFileName() (1 input parameters) Name: GetFileName Return type: const char * Description: Get pointer to filename for a path string Param[1]: filePath (type: const char *) -Function 123: GetFileNameWithoutExt() (1 input parameters) +Function 126: GetFileNameWithoutExt() (1 input parameters) Name: GetFileNameWithoutExt Return type: const char * Description: Get filename string without extension (uses static string) Param[1]: filePath (type: const char *) -Function 124: GetDirectoryPath() (1 input parameters) +Function 127: GetDirectoryPath() (1 input parameters) Name: GetDirectoryPath Return type: const char * Description: Get full path for a given fileName with path (uses static string) Param[1]: filePath (type: const char *) -Function 125: GetPrevDirectoryPath() (1 input parameters) +Function 128: GetPrevDirectoryPath() (1 input parameters) Name: GetPrevDirectoryPath Return type: const char * Description: Get previous directory path for a given path (uses static string) Param[1]: dirPath (type: const char *) -Function 126: GetWorkingDirectory() (0 input parameters) +Function 129: GetWorkingDirectory() (0 input parameters) Name: GetWorkingDirectory Return type: const char * Description: Get current working directory (uses static string) No input parameters -Function 127: GetApplicationDirectory() (0 input parameters) +Function 130: GetApplicationDirectory() (0 input parameters) Name: GetApplicationDirectory Return type: const char * - Description: Get the directory if the running application (uses static string) + Description: Get the directory of the running application (uses static string) No input parameters -Function 128: ChangeDirectory() (1 input parameters) +Function 131: ChangeDirectory() (1 input parameters) Name: ChangeDirectory Return type: bool Description: Change working directory, return true on success Param[1]: dir (type: const char *) -Function 129: IsPathFile() (1 input parameters) +Function 132: IsPathFile() (1 input parameters) Name: IsPathFile Return type: bool Description: Check if a given path is a file or a directory Param[1]: path (type: const char *) -Function 130: LoadDirectoryFiles() (1 input parameters) +Function 133: LoadDirectoryFiles() (1 input parameters) Name: LoadDirectoryFiles Return type: FilePathList Description: Load directory filepaths Param[1]: dirPath (type: const char *) -Function 131: LoadDirectoryFilesEx() (3 input parameters) +Function 134: LoadDirectoryFilesEx() (3 input parameters) Name: LoadDirectoryFilesEx Return type: FilePathList Description: Load directory filepaths with extension filtering and recursive directory scan Param[1]: basePath (type: const char *) Param[2]: filter (type: const char *) Param[3]: scanSubdirs (type: bool) -Function 132: UnloadDirectoryFiles() (1 input parameters) +Function 135: UnloadDirectoryFiles() (1 input parameters) Name: UnloadDirectoryFiles Return type: void Description: Unload filepaths Param[1]: files (type: FilePathList) -Function 133: IsFileDropped() (0 input parameters) +Function 136: IsFileDropped() (0 input parameters) Name: IsFileDropped Return type: bool Description: Check if a file has been dropped into window No input parameters -Function 134: LoadDroppedFiles() (0 input parameters) +Function 137: LoadDroppedFiles() (0 input parameters) Name: LoadDroppedFiles Return type: FilePathList Description: Load dropped filepaths No input parameters -Function 135: UnloadDroppedFiles() (1 input parameters) +Function 138: UnloadDroppedFiles() (1 input parameters) Name: UnloadDroppedFiles Return type: void Description: Unload dropped filepaths Param[1]: files (type: FilePathList) -Function 136: GetFileModTime() (1 input parameters) +Function 139: GetFileModTime() (1 input parameters) Name: GetFileModTime Return type: long Description: Get file modification time (last write time) Param[1]: fileName (type: const char *) -Function 137: CompressData() (3 input parameters) +Function 140: CompressData() (3 input parameters) Name: CompressData Return type: unsigned char * Description: Compress data (DEFLATE algorithm), memory must be MemFree() Param[1]: data (type: const unsigned char *) Param[2]: dataSize (type: int) Param[3]: compDataSize (type: int *) -Function 138: DecompressData() (3 input parameters) +Function 141: DecompressData() (3 input parameters) Name: DecompressData Return type: unsigned char * Description: Decompress data (DEFLATE algorithm), memory must be MemFree() Param[1]: compData (type: const unsigned char *) Param[2]: compDataSize (type: int) Param[3]: dataSize (type: int *) -Function 139: EncodeDataBase64() (3 input parameters) +Function 142: EncodeDataBase64() (3 input parameters) Name: EncodeDataBase64 Return type: char * Description: Encode data to Base64 string, memory must be MemFree() Param[1]: data (type: const unsigned char *) Param[2]: dataSize (type: int) Param[3]: outputSize (type: int *) -Function 140: DecodeDataBase64() (2 input parameters) +Function 143: DecodeDataBase64() (2 input parameters) Name: DecodeDataBase64 Return type: unsigned char * Description: Decode Base64 string data, memory must be MemFree() Param[1]: data (type: const unsigned char *) Param[2]: outputSize (type: int *) -Function 141: IsKeyPressed() (1 input parameters) +Function 144: IsKeyPressed() (1 input parameters) Name: IsKeyPressed Return type: bool Description: Check if a key has been pressed once Param[1]: key (type: int) -Function 142: IsKeyDown() (1 input parameters) +Function 145: IsKeyPressedRepeat() (1 input parameters) + Name: IsKeyPressedRepeat + Return type: bool + Description: Check if a key has been pressed again (Only PLATFORM_DESKTOP) + Param[1]: key (type: int) +Function 146: IsKeyDown() (1 input parameters) Name: IsKeyDown Return type: bool Description: Check if a key is being pressed Param[1]: key (type: int) -Function 143: IsKeyReleased() (1 input parameters) +Function 147: IsKeyReleased() (1 input parameters) Name: IsKeyReleased Return type: bool Description: Check if a key has been released once Param[1]: key (type: int) -Function 144: IsKeyUp() (1 input parameters) +Function 148: IsKeyUp() (1 input parameters) Name: IsKeyUp Return type: bool Description: Check if a key is NOT being pressed Param[1]: key (type: int) -Function 145: SetExitKey() (1 input parameters) +Function 149: SetExitKey() (1 input parameters) Name: SetExitKey Return type: void Description: Set a custom key to exit program (default is ESC) Param[1]: key (type: int) -Function 146: GetKeyPressed() (0 input parameters) +Function 150: GetKeyPressed() (0 input parameters) Name: GetKeyPressed Return type: int Description: Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty No input parameters -Function 147: GetCharPressed() (0 input parameters) +Function 151: GetCharPressed() (0 input parameters) Name: GetCharPressed Return type: int Description: Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty No input parameters -Function 148: IsGamepadAvailable() (1 input parameters) +Function 152: IsGamepadAvailable() (1 input parameters) Name: IsGamepadAvailable Return type: bool Description: Check if a gamepad is available Param[1]: gamepad (type: int) -Function 149: GetGamepadName() (1 input parameters) +Function 153: GetGamepadName() (1 input parameters) Name: GetGamepadName Return type: const char * Description: Get gamepad internal name id Param[1]: gamepad (type: int) -Function 150: IsGamepadButtonPressed() (2 input parameters) +Function 154: IsGamepadButtonPressed() (2 input parameters) Name: IsGamepadButtonPressed Return type: bool Description: Check if a gamepad button has been pressed once Param[1]: gamepad (type: int) Param[2]: button (type: int) -Function 151: IsGamepadButtonDown() (2 input parameters) +Function 155: IsGamepadButtonDown() (2 input parameters) Name: IsGamepadButtonDown Return type: bool Description: Check if a gamepad button is being pressed Param[1]: gamepad (type: int) Param[2]: button (type: int) -Function 152: IsGamepadButtonReleased() (2 input parameters) +Function 156: IsGamepadButtonReleased() (2 input parameters) Name: IsGamepadButtonReleased Return type: bool Description: Check if a gamepad button has been released once Param[1]: gamepad (type: int) Param[2]: button (type: int) -Function 153: IsGamepadButtonUp() (2 input parameters) +Function 157: IsGamepadButtonUp() (2 input parameters) Name: IsGamepadButtonUp Return type: bool Description: Check if a gamepad button is NOT being pressed Param[1]: gamepad (type: int) Param[2]: button (type: int) -Function 154: GetGamepadButtonPressed() (0 input parameters) +Function 158: GetGamepadButtonPressed() (0 input parameters) Name: GetGamepadButtonPressed Return type: int Description: Get the last gamepad button pressed No input parameters -Function 155: GetGamepadAxisCount() (1 input parameters) +Function 159: GetGamepadAxisCount() (1 input parameters) Name: GetGamepadAxisCount Return type: int Description: Get gamepad axis count for a gamepad Param[1]: gamepad (type: int) -Function 156: GetGamepadAxisMovement() (2 input parameters) +Function 160: GetGamepadAxisMovement() (2 input parameters) Name: GetGamepadAxisMovement Return type: float Description: Get axis movement value for a gamepad axis Param[1]: gamepad (type: int) Param[2]: axis (type: int) -Function 157: SetGamepadMappings() (1 input parameters) +Function 161: SetGamepadMappings() (1 input parameters) Name: SetGamepadMappings Return type: int Description: Set internal gamepad mappings (SDL_GameControllerDB) Param[1]: mappings (type: const char *) -Function 158: IsMouseButtonPressed() (1 input parameters) +Function 162: IsMouseButtonPressed() (1 input parameters) Name: IsMouseButtonPressed Return type: bool Description: Check if a mouse button has been pressed once Param[1]: button (type: int) -Function 159: IsMouseButtonDown() (1 input parameters) +Function 163: IsMouseButtonDown() (1 input parameters) Name: IsMouseButtonDown Return type: bool Description: Check if a mouse button is being pressed Param[1]: button (type: int) -Function 160: IsMouseButtonReleased() (1 input parameters) +Function 164: IsMouseButtonReleased() (1 input parameters) Name: IsMouseButtonReleased Return type: bool Description: Check if a mouse button has been released once Param[1]: button (type: int) -Function 161: IsMouseButtonUp() (1 input parameters) +Function 165: IsMouseButtonUp() (1 input parameters) Name: IsMouseButtonUp Return type: bool Description: Check if a mouse button is NOT being pressed Param[1]: button (type: int) -Function 162: GetMouseX() (0 input parameters) +Function 166: GetMouseX() (0 input parameters) Name: GetMouseX Return type: int Description: Get mouse position X No input parameters -Function 163: GetMouseY() (0 input parameters) +Function 167: GetMouseY() (0 input parameters) Name: GetMouseY Return type: int Description: Get mouse position Y No input parameters -Function 164: GetMousePosition() (0 input parameters) +Function 168: GetMousePosition() (0 input parameters) Name: GetMousePosition Return type: Vector2 Description: Get mouse position XY No input parameters -Function 165: GetMouseDelta() (0 input parameters) +Function 169: GetMouseDelta() (0 input parameters) Name: GetMouseDelta Return type: Vector2 Description: Get mouse delta between frames No input parameters -Function 166: SetMousePosition() (2 input parameters) +Function 170: SetMousePosition() (2 input parameters) Name: SetMousePosition Return type: void Description: Set mouse position XY Param[1]: x (type: int) Param[2]: y (type: int) -Function 167: SetMouseOffset() (2 input parameters) +Function 171: SetMouseOffset() (2 input parameters) Name: SetMouseOffset Return type: void Description: Set mouse offset Param[1]: offsetX (type: int) Param[2]: offsetY (type: int) -Function 168: SetMouseScale() (2 input parameters) +Function 172: SetMouseScale() (2 input parameters) Name: SetMouseScale Return type: void Description: Set mouse scaling Param[1]: scaleX (type: float) Param[2]: scaleY (type: float) -Function 169: GetMouseWheelMove() (0 input parameters) +Function 173: GetMouseWheelMove() (0 input parameters) Name: GetMouseWheelMove Return type: float Description: Get mouse wheel movement for X or Y, whichever is larger No input parameters -Function 170: GetMouseWheelMoveV() (0 input parameters) +Function 174: GetMouseWheelMoveV() (0 input parameters) Name: GetMouseWheelMoveV Return type: Vector2 Description: Get mouse wheel movement for both X and Y No input parameters -Function 171: SetMouseCursor() (1 input parameters) +Function 175: SetMouseCursor() (1 input parameters) Name: SetMouseCursor Return type: void Description: Set mouse cursor Param[1]: cursor (type: int) -Function 172: GetTouchX() (0 input parameters) +Function 176: GetTouchX() (0 input parameters) Name: GetTouchX Return type: int Description: Get touch position X for touch point 0 (relative to screen size) No input parameters -Function 173: GetTouchY() (0 input parameters) +Function 177: GetTouchY() (0 input parameters) Name: GetTouchY Return type: int Description: Get touch position Y for touch point 0 (relative to screen size) No input parameters -Function 174: GetTouchPosition() (1 input parameters) +Function 178: GetTouchPosition() (1 input parameters) Name: GetTouchPosition Return type: Vector2 Description: Get touch position XY for a touch point index (relative to screen size) Param[1]: index (type: int) -Function 175: GetTouchPointId() (1 input parameters) +Function 179: GetTouchPointId() (1 input parameters) Name: GetTouchPointId Return type: int Description: Get touch point identifier for given index Param[1]: index (type: int) -Function 176: GetTouchPointCount() (0 input parameters) +Function 180: GetTouchPointCount() (0 input parameters) Name: GetTouchPointCount Return type: int Description: Get number of touch points No input parameters -Function 177: SetGesturesEnabled() (1 input parameters) +Function 181: SetGesturesEnabled() (1 input parameters) Name: SetGesturesEnabled Return type: void Description: Enable a set of gestures using flags Param[1]: flags (type: unsigned int) -Function 178: IsGestureDetected() (1 input parameters) +Function 182: IsGestureDetected() (1 input parameters) Name: IsGestureDetected Return type: bool Description: Check if a gesture have been detected - Param[1]: gesture (type: int) -Function 179: GetGestureDetected() (0 input parameters) + Param[1]: gesture (type: unsigned int) +Function 183: GetGestureDetected() (0 input parameters) Name: GetGestureDetected Return type: int Description: Get latest detected gesture No input parameters -Function 180: GetGestureHoldDuration() (0 input parameters) +Function 184: GetGestureHoldDuration() (0 input parameters) Name: GetGestureHoldDuration Return type: float Description: Get gesture hold time in milliseconds No input parameters -Function 181: GetGestureDragVector() (0 input parameters) +Function 185: GetGestureDragVector() (0 input parameters) Name: GetGestureDragVector Return type: Vector2 Description: Get gesture drag vector No input parameters -Function 182: GetGestureDragAngle() (0 input parameters) +Function 186: GetGestureDragAngle() (0 input parameters) Name: GetGestureDragAngle Return type: float Description: Get gesture drag angle No input parameters -Function 183: GetGesturePinchVector() (0 input parameters) +Function 187: GetGesturePinchVector() (0 input parameters) Name: GetGesturePinchVector Return type: Vector2 Description: Get gesture pinch delta No input parameters -Function 184: GetGesturePinchAngle() (0 input parameters) +Function 188: GetGesturePinchAngle() (0 input parameters) Name: GetGesturePinchAngle Return type: float Description: Get gesture pinch angle No input parameters -Function 185: UpdateCamera() (2 input parameters) +Function 189: UpdateCamera() (2 input parameters) Name: UpdateCamera Return type: void Description: Update camera position for selected mode Param[1]: camera (type: Camera *) Param[2]: mode (type: int) -Function 186: UpdateCameraPro() (4 input parameters) +Function 190: UpdateCameraPro() (4 input parameters) Name: UpdateCameraPro Return type: void Description: Update camera movement/rotation @@ -1959,26 +1984,26 @@ Function 186: UpdateCameraPro() (4 input parameters) Param[2]: movement (type: Vector3) Param[3]: rotation (type: Vector3) Param[4]: zoom (type: float) -Function 187: SetShapesTexture() (2 input parameters) +Function 191: SetShapesTexture() (2 input parameters) Name: SetShapesTexture Return type: void Description: Set texture and rectangle to be used on shapes drawing Param[1]: texture (type: Texture2D) Param[2]: source (type: Rectangle) -Function 188: DrawPixel() (3 input parameters) +Function 192: DrawPixel() (3 input parameters) Name: DrawPixel Return type: void Description: Draw a pixel Param[1]: posX (type: int) Param[2]: posY (type: int) Param[3]: color (type: Color) -Function 189: DrawPixelV() (2 input parameters) +Function 193: DrawPixelV() (2 input parameters) Name: DrawPixelV Return type: void Description: Draw a pixel (Vector version) Param[1]: position (type: Vector2) Param[2]: color (type: Color) -Function 190: DrawLine() (5 input parameters) +Function 194: DrawLine() (5 input parameters) Name: DrawLine Return type: void Description: Draw a line @@ -1987,14 +2012,14 @@ Function 190: DrawLine() (5 input parameters) Param[3]: endPosX (type: int) Param[4]: endPosY (type: int) Param[5]: color (type: Color) -Function 191: DrawLineV() (3 input parameters) +Function 195: DrawLineV() (3 input parameters) Name: DrawLineV Return type: void Description: Draw a line (Vector version) Param[1]: startPos (type: Vector2) Param[2]: endPos (type: Vector2) Param[3]: color (type: Color) -Function 192: DrawLineEx() (4 input parameters) +Function 196: DrawLineEx() (4 input parameters) Name: DrawLineEx Return type: void Description: Draw a line defining thickness @@ -2002,7 +2027,7 @@ Function 192: DrawLineEx() (4 input parameters) Param[2]: endPos (type: Vector2) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 193: DrawLineBezier() (4 input parameters) +Function 197: DrawLineBezier() (4 input parameters) Name: DrawLineBezier Return type: void Description: Draw a line using cubic-bezier curves in-out @@ -2010,7 +2035,7 @@ Function 193: DrawLineBezier() (4 input parameters) Param[2]: endPos (type: Vector2) Param[3]: thick (type: float) Param[4]: color (type: Color) -Function 194: DrawLineBezierQuad() (5 input parameters) +Function 198: DrawLineBezierQuad() (5 input parameters) Name: DrawLineBezierQuad Return type: void Description: Draw line using quadratic bezier curves with a control point @@ -2019,7 +2044,7 @@ Function 194: DrawLineBezierQuad() (5 input parameters) Param[3]: controlPos (type: Vector2) Param[4]: thick (type: float) Param[5]: color (type: Color) -Function 195: DrawLineBezierCubic() (6 input parameters) +Function 199: DrawLineBezierCubic() (6 input parameters) Name: DrawLineBezierCubic Return type: void Description: Draw line using cubic bezier curves with 2 control points @@ -2029,14 +2054,30 @@ Function 195: DrawLineBezierCubic() (6 input parameters) Param[4]: endControlPos (type: Vector2) Param[5]: thick (type: float) Param[6]: color (type: Color) -Function 196: DrawLineStrip() (3 input parameters) +Function 200: DrawLineBSpline() (4 input parameters) + Name: DrawLineBSpline + Return type: void + Description: Draw a B-Spline line, minimum 4 points + Param[1]: points (type: Vector2 *) + Param[2]: pointCount (type: int) + Param[3]: thick (type: float) + Param[4]: color (type: Color) +Function 201: DrawLineCatmullRom() (4 input parameters) + Name: DrawLineCatmullRom + Return type: void + Description: Draw a Catmull Rom spline line, minimum 4 points + Param[1]: points (type: Vector2 *) + Param[2]: pointCount (type: int) + Param[3]: thick (type: float) + Param[4]: color (type: Color) +Function 202: DrawLineStrip() (3 input parameters) Name: DrawLineStrip Return type: void Description: Draw lines sequence Param[1]: points (type: Vector2 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 197: DrawCircle() (4 input parameters) +Function 203: DrawCircle() (4 input parameters) Name: DrawCircle Return type: void Description: Draw a color-filled circle @@ -2044,7 +2085,7 @@ Function 197: DrawCircle() (4 input parameters) Param[2]: centerY (type: int) Param[3]: radius (type: float) Param[4]: color (type: Color) -Function 198: DrawCircleSector() (6 input parameters) +Function 204: DrawCircleSector() (6 input parameters) Name: DrawCircleSector Return type: void Description: Draw a piece of a circle @@ -2054,7 +2095,7 @@ Function 198: DrawCircleSector() (6 input parameters) Param[4]: endAngle (type: float) Param[5]: segments (type: int) Param[6]: color (type: Color) -Function 199: DrawCircleSectorLines() (6 input parameters) +Function 205: DrawCircleSectorLines() (6 input parameters) Name: DrawCircleSectorLines Return type: void Description: Draw circle sector outline @@ -2064,7 +2105,7 @@ Function 199: DrawCircleSectorLines() (6 input parameters) Param[4]: endAngle (type: float) Param[5]: segments (type: int) Param[6]: color (type: Color) -Function 200: DrawCircleGradient() (5 input parameters) +Function 206: DrawCircleGradient() (5 input parameters) Name: DrawCircleGradient Return type: void Description: Draw a gradient-filled circle @@ -2073,14 +2114,14 @@ Function 200: DrawCircleGradient() (5 input parameters) Param[3]: radius (type: float) Param[4]: color1 (type: Color) Param[5]: color2 (type: Color) -Function 201: DrawCircleV() (3 input parameters) +Function 207: DrawCircleV() (3 input parameters) Name: DrawCircleV Return type: void Description: Draw a color-filled circle (Vector version) Param[1]: center (type: Vector2) Param[2]: radius (type: float) Param[3]: color (type: Color) -Function 202: DrawCircleLines() (4 input parameters) +Function 208: DrawCircleLines() (4 input parameters) Name: DrawCircleLines Return type: void Description: Draw circle outline @@ -2088,7 +2129,7 @@ Function 202: DrawCircleLines() (4 input parameters) Param[2]: centerY (type: int) Param[3]: radius (type: float) Param[4]: color (type: Color) -Function 203: DrawEllipse() (5 input parameters) +Function 209: DrawEllipse() (5 input parameters) Name: DrawEllipse Return type: void Description: Draw ellipse @@ -2097,7 +2138,7 @@ Function 203: DrawEllipse() (5 input parameters) Param[3]: radiusH (type: float) Param[4]: radiusV (type: float) Param[5]: color (type: Color) -Function 204: DrawEllipseLines() (5 input parameters) +Function 210: DrawEllipseLines() (5 input parameters) Name: DrawEllipseLines Return type: void Description: Draw ellipse outline @@ -2106,7 +2147,7 @@ Function 204: DrawEllipseLines() (5 input parameters) Param[3]: radiusH (type: float) Param[4]: radiusV (type: float) Param[5]: color (type: Color) -Function 205: DrawRing() (7 input parameters) +Function 211: DrawRing() (7 input parameters) Name: DrawRing Return type: void Description: Draw ring @@ -2117,7 +2158,7 @@ Function 205: DrawRing() (7 input parameters) Param[5]: endAngle (type: float) Param[6]: segments (type: int) Param[7]: color (type: Color) -Function 206: DrawRingLines() (7 input parameters) +Function 212: DrawRingLines() (7 input parameters) Name: DrawRingLines Return type: void Description: Draw ring outline @@ -2128,7 +2169,7 @@ Function 206: DrawRingLines() (7 input parameters) Param[5]: endAngle (type: float) Param[6]: segments (type: int) Param[7]: color (type: Color) -Function 207: DrawRectangle() (5 input parameters) +Function 213: DrawRectangle() (5 input parameters) Name: DrawRectangle Return type: void Description: Draw a color-filled rectangle @@ -2137,20 +2178,20 @@ Function 207: DrawRectangle() (5 input parameters) Param[3]: width (type: int) Param[4]: height (type: int) Param[5]: color (type: Color) -Function 208: DrawRectangleV() (3 input parameters) +Function 214: DrawRectangleV() (3 input parameters) Name: DrawRectangleV Return type: void Description: Draw a color-filled rectangle (Vector version) Param[1]: position (type: Vector2) Param[2]: size (type: Vector2) Param[3]: color (type: Color) -Function 209: DrawRectangleRec() (2 input parameters) +Function 215: DrawRectangleRec() (2 input parameters) Name: DrawRectangleRec Return type: void Description: Draw a color-filled rectangle Param[1]: rec (type: Rectangle) Param[2]: color (type: Color) -Function 210: DrawRectanglePro() (4 input parameters) +Function 216: DrawRectanglePro() (4 input parameters) Name: DrawRectanglePro Return type: void Description: Draw a color-filled rectangle with pro parameters @@ -2158,7 +2199,7 @@ Function 210: DrawRectanglePro() (4 input parameters) Param[2]: origin (type: Vector2) Param[3]: rotation (type: float) Param[4]: color (type: Color) -Function 211: DrawRectangleGradientV() (6 input parameters) +Function 217: DrawRectangleGradientV() (6 input parameters) Name: DrawRectangleGradientV Return type: void Description: Draw a vertical-gradient-filled rectangle @@ -2168,7 +2209,7 @@ Function 211: DrawRectangleGradientV() (6 input parameters) Param[4]: height (type: int) Param[5]: color1 (type: Color) Param[6]: color2 (type: Color) -Function 212: DrawRectangleGradientH() (6 input parameters) +Function 218: DrawRectangleGradientH() (6 input parameters) Name: DrawRectangleGradientH Return type: void Description: Draw a horizontal-gradient-filled rectangle @@ -2178,7 +2219,7 @@ Function 212: DrawRectangleGradientH() (6 input parameters) Param[4]: height (type: int) Param[5]: color1 (type: Color) Param[6]: color2 (type: Color) -Function 213: DrawRectangleGradientEx() (5 input parameters) +Function 219: DrawRectangleGradientEx() (5 input parameters) Name: DrawRectangleGradientEx Return type: void Description: Draw a gradient-filled rectangle with custom vertex colors @@ -2187,7 +2228,7 @@ Function 213: DrawRectangleGradientEx() (5 input parameters) Param[3]: col2 (type: Color) Param[4]: col3 (type: Color) Param[5]: col4 (type: Color) -Function 214: DrawRectangleLines() (5 input parameters) +Function 220: DrawRectangleLines() (5 input parameters) Name: DrawRectangleLines Return type: void Description: Draw rectangle outline @@ -2196,14 +2237,14 @@ Function 214: DrawRectangleLines() (5 input parameters) Param[3]: width (type: int) Param[4]: height (type: int) Param[5]: color (type: Color) -Function 215: DrawRectangleLinesEx() (3 input parameters) +Function 221: DrawRectangleLinesEx() (3 input parameters) Name: DrawRectangleLinesEx Return type: void Description: Draw rectangle outline with extended parameters Param[1]: rec (type: Rectangle) Param[2]: lineThick (type: float) Param[3]: color (type: Color) -Function 216: DrawRectangleRounded() (4 input parameters) +Function 222: DrawRectangleRounded() (4 input parameters) Name: DrawRectangleRounded Return type: void Description: Draw rectangle with rounded edges @@ -2211,7 +2252,7 @@ Function 216: DrawRectangleRounded() (4 input parameters) Param[2]: roundness (type: float) Param[3]: segments (type: int) Param[4]: color (type: Color) -Function 217: DrawRectangleRoundedLines() (5 input parameters) +Function 223: DrawRectangleRoundedLines() (5 input parameters) Name: DrawRectangleRoundedLines Return type: void Description: Draw rectangle with rounded edges outline @@ -2220,7 +2261,7 @@ Function 217: DrawRectangleRoundedLines() (5 input parameters) Param[3]: segments (type: int) Param[4]: lineThick (type: float) Param[5]: color (type: Color) -Function 218: DrawTriangle() (4 input parameters) +Function 224: DrawTriangle() (4 input parameters) Name: DrawTriangle Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -2228,7 +2269,7 @@ Function 218: DrawTriangle() (4 input parameters) Param[2]: v2 (type: Vector2) Param[3]: v3 (type: Vector2) Param[4]: color (type: Color) -Function 219: DrawTriangleLines() (4 input parameters) +Function 225: DrawTriangleLines() (4 input parameters) Name: DrawTriangleLines Return type: void Description: Draw triangle outline (vertex in counter-clockwise order!) @@ -2236,21 +2277,21 @@ Function 219: DrawTriangleLines() (4 input parameters) Param[2]: v2 (type: Vector2) Param[3]: v3 (type: Vector2) Param[4]: color (type: Color) -Function 220: DrawTriangleFan() (3 input parameters) +Function 226: DrawTriangleFan() (3 input parameters) Name: DrawTriangleFan Return type: void Description: Draw a triangle fan defined by points (first vertex is the center) Param[1]: points (type: Vector2 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 221: DrawTriangleStrip() (3 input parameters) +Function 227: DrawTriangleStrip() (3 input parameters) Name: DrawTriangleStrip Return type: void Description: Draw a triangle strip defined by points Param[1]: points (type: Vector2 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 222: DrawPoly() (5 input parameters) +Function 228: DrawPoly() (5 input parameters) Name: DrawPoly Return type: void Description: Draw a regular polygon (Vector version) @@ -2259,7 +2300,7 @@ Function 222: DrawPoly() (5 input parameters) Param[3]: radius (type: float) Param[4]: rotation (type: float) Param[5]: color (type: Color) -Function 223: DrawPolyLines() (5 input parameters) +Function 229: DrawPolyLines() (5 input parameters) Name: DrawPolyLines Return type: void Description: Draw a polygon outline of n sides @@ -2268,7 +2309,7 @@ Function 223: DrawPolyLines() (5 input parameters) Param[3]: radius (type: float) Param[4]: rotation (type: float) Param[5]: color (type: Color) -Function 224: DrawPolyLinesEx() (6 input parameters) +Function 230: DrawPolyLinesEx() (6 input parameters) Name: DrawPolyLinesEx Return type: void Description: Draw a polygon outline of n sides with extended parameters @@ -2278,13 +2319,13 @@ Function 224: DrawPolyLinesEx() (6 input parameters) Param[4]: rotation (type: float) Param[5]: lineThick (type: float) Param[6]: color (type: Color) -Function 225: CheckCollisionRecs() (2 input parameters) +Function 231: CheckCollisionRecs() (2 input parameters) Name: CheckCollisionRecs Return type: bool Description: Check collision between two rectangles Param[1]: rec1 (type: Rectangle) Param[2]: rec2 (type: Rectangle) -Function 226: CheckCollisionCircles() (4 input parameters) +Function 232: CheckCollisionCircles() (4 input parameters) Name: CheckCollisionCircles Return type: bool Description: Check collision between two circles @@ -2292,27 +2333,27 @@ Function 226: CheckCollisionCircles() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector2) Param[4]: radius2 (type: float) -Function 227: CheckCollisionCircleRec() (3 input parameters) +Function 233: CheckCollisionCircleRec() (3 input parameters) Name: CheckCollisionCircleRec Return type: bool Description: Check collision between circle and rectangle Param[1]: center (type: Vector2) Param[2]: radius (type: float) Param[3]: rec (type: Rectangle) -Function 228: CheckCollisionPointRec() (2 input parameters) +Function 234: CheckCollisionPointRec() (2 input parameters) Name: CheckCollisionPointRec Return type: bool Description: Check if point is inside rectangle Param[1]: point (type: Vector2) Param[2]: rec (type: Rectangle) -Function 229: CheckCollisionPointCircle() (3 input parameters) +Function 235: CheckCollisionPointCircle() (3 input parameters) Name: CheckCollisionPointCircle Return type: bool Description: Check if point is inside circle Param[1]: point (type: Vector2) Param[2]: center (type: Vector2) Param[3]: radius (type: float) -Function 230: CheckCollisionPointTriangle() (4 input parameters) +Function 236: CheckCollisionPointTriangle() (4 input parameters) Name: CheckCollisionPointTriangle Return type: bool Description: Check if point is inside a triangle @@ -2320,14 +2361,14 @@ Function 230: CheckCollisionPointTriangle() (4 input parameters) Param[2]: p1 (type: Vector2) Param[3]: p2 (type: Vector2) Param[4]: p3 (type: Vector2) -Function 231: CheckCollisionPointPoly() (3 input parameters) +Function 237: CheckCollisionPointPoly() (3 input parameters) Name: CheckCollisionPointPoly Return type: bool Description: Check if point is within a polygon described by array of vertices Param[1]: point (type: Vector2) Param[2]: points (type: Vector2 *) Param[3]: pointCount (type: int) -Function 232: CheckCollisionLines() (5 input parameters) +Function 238: CheckCollisionLines() (5 input parameters) Name: CheckCollisionLines Return type: bool Description: Check the collision between two lines defined by two points each, returns collision point by reference @@ -2336,7 +2377,7 @@ Function 232: CheckCollisionLines() (5 input parameters) Param[3]: startPos2 (type: Vector2) Param[4]: endPos2 (type: Vector2) Param[5]: collisionPoint (type: Vector2 *) -Function 233: CheckCollisionPointLine() (4 input parameters) +Function 239: CheckCollisionPointLine() (4 input parameters) Name: CheckCollisionPointLine Return type: bool Description: Check if point belongs to line created between two points [p1] and [p2] with defined margin in pixels [threshold] @@ -2344,18 +2385,18 @@ Function 233: CheckCollisionPointLine() (4 input parameters) Param[2]: p1 (type: Vector2) Param[3]: p2 (type: Vector2) Param[4]: threshold (type: int) -Function 234: GetCollisionRec() (2 input parameters) +Function 240: GetCollisionRec() (2 input parameters) Name: GetCollisionRec Return type: Rectangle Description: Get collision rectangle for two rectangles collision Param[1]: rec1 (type: Rectangle) Param[2]: rec2 (type: Rectangle) -Function 235: LoadImage() (1 input parameters) +Function 241: LoadImage() (1 input parameters) Name: LoadImage Return type: Image Description: Load image from file into CPU memory (RAM) Param[1]: fileName (type: const char *) -Function 236: LoadImageRaw() (5 input parameters) +Function 242: LoadImageRaw() (5 input parameters) Name: LoadImageRaw Return type: Image Description: Load image from RAW file data @@ -2364,59 +2405,73 @@ Function 236: LoadImageRaw() (5 input parameters) Param[3]: height (type: int) Param[4]: format (type: int) Param[5]: headerSize (type: int) -Function 237: LoadImageAnim() (2 input parameters) +Function 243: LoadImageSvg() (3 input parameters) + Name: LoadImageSvg + Return type: Image + Description: Load image from SVG file data or string with specified size + Param[1]: fileNameOrString (type: const char *) + Param[2]: width (type: int) + Param[3]: height (type: int) +Function 244: LoadImageAnim() (2 input parameters) Name: LoadImageAnim Return type: Image Description: Load image sequence from file (frames appended to image.data) Param[1]: fileName (type: const char *) Param[2]: frames (type: int *) -Function 238: LoadImageFromMemory() (3 input parameters) +Function 245: LoadImageFromMemory() (3 input parameters) Name: LoadImageFromMemory Return type: Image Description: Load image from memory buffer, fileType refers to extension: i.e. '.png' Param[1]: fileType (type: const char *) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 239: LoadImageFromTexture() (1 input parameters) +Function 246: LoadImageFromTexture() (1 input parameters) Name: LoadImageFromTexture Return type: Image Description: Load image from GPU texture data Param[1]: texture (type: Texture2D) -Function 240: LoadImageFromScreen() (0 input parameters) +Function 247: LoadImageFromScreen() (0 input parameters) Name: LoadImageFromScreen Return type: Image Description: Load image from screen buffer and (screenshot) No input parameters -Function 241: IsImageReady() (1 input parameters) +Function 248: IsImageReady() (1 input parameters) Name: IsImageReady Return type: bool Description: Check if an image is ready Param[1]: image (type: Image) -Function 242: UnloadImage() (1 input parameters) +Function 249: UnloadImage() (1 input parameters) Name: UnloadImage Return type: void Description: Unload image from CPU memory (RAM) Param[1]: image (type: Image) -Function 243: ExportImage() (2 input parameters) +Function 250: ExportImage() (2 input parameters) Name: ExportImage Return type: bool Description: Export image data to file, returns true on success Param[1]: image (type: Image) Param[2]: fileName (type: const char *) -Function 244: ExportImageAsCode() (2 input parameters) +Function 251: ExportImageToMemory() (3 input parameters) + Name: ExportImageToMemory + Return type: unsigned char * + Description: Export image to memory buffer + Param[1]: image (type: Image) + Param[2]: fileType (type: const char *) + Param[3]: fileSize (type: int *) +Function 252: ExportImageAsCode() (2 input parameters) Name: ExportImageAsCode Return type: bool Description: Export image as code file defining an array of bytes, returns true on success Param[1]: image (type: Image) Param[2]: fileName (type: const char *) -Function 245: GenImageColor() (3 input parameters) +Function 253: GenImageColor() (3 input parameters) Name: GenImageColor Return type: Image Description: Generate image: plain color Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: color (type: Color) -Function 246: GenImageGradientLinear() (5 input parameters) +Function 254: GenImageGradientLinear() (5 input parameters) Name: GenImageGradientLinear Return type: Image Description: Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient @@ -2425,7 +2480,7 @@ Function 246: GenImageGradientLinear() (5 input parameters) Param[3]: direction (type: int) Param[4]: start (type: Color) Param[5]: end (type: Color) -Function 247: GenImageGradientRadial() (5 input parameters) +Function 255: GenImageGradientRadial() (5 input parameters) Name: GenImageGradientRadial Return type: Image Description: Generate image: radial gradient @@ -2434,7 +2489,7 @@ Function 247: GenImageGradientRadial() (5 input parameters) Param[3]: density (type: float) Param[4]: inner (type: Color) Param[5]: outer (type: Color) -Function 248: GenImageGradientSquare() (5 input parameters) +Function 256: GenImageGradientSquare() (5 input parameters) Name: GenImageGradientSquare Return type: Image Description: Generate image: square gradient @@ -2443,7 +2498,7 @@ Function 248: GenImageGradientSquare() (5 input parameters) Param[3]: density (type: float) Param[4]: inner (type: Color) Param[5]: outer (type: Color) -Function 249: GenImageChecked() (6 input parameters) +Function 257: GenImageChecked() (6 input parameters) Name: GenImageChecked Return type: Image Description: Generate image: checked @@ -2453,14 +2508,14 @@ Function 249: GenImageChecked() (6 input parameters) Param[4]: checksY (type: int) Param[5]: col1 (type: Color) Param[6]: col2 (type: Color) -Function 250: GenImageWhiteNoise() (3 input parameters) +Function 258: GenImageWhiteNoise() (3 input parameters) Name: GenImageWhiteNoise Return type: Image Description: Generate image: white noise Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: factor (type: float) -Function 251: GenImagePerlinNoise() (5 input parameters) +Function 259: GenImagePerlinNoise() (5 input parameters) Name: GenImagePerlinNoise Return type: Image Description: Generate image: perlin noise @@ -2469,39 +2524,39 @@ Function 251: GenImagePerlinNoise() (5 input parameters) Param[3]: offsetX (type: int) Param[4]: offsetY (type: int) Param[5]: scale (type: float) -Function 252: GenImageCellular() (3 input parameters) +Function 260: GenImageCellular() (3 input parameters) Name: GenImageCellular Return type: Image Description: Generate image: cellular algorithm, bigger tileSize means bigger cells Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: tileSize (type: int) -Function 253: GenImageText() (3 input parameters) +Function 261: GenImageText() (3 input parameters) Name: GenImageText Return type: Image Description: Generate image: grayscale image from text data Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: text (type: const char *) -Function 254: ImageCopy() (1 input parameters) +Function 262: ImageCopy() (1 input parameters) Name: ImageCopy Return type: Image Description: Create an image duplicate (useful for transformations) Param[1]: image (type: Image) -Function 255: ImageFromImage() (2 input parameters) +Function 263: ImageFromImage() (2 input parameters) Name: ImageFromImage Return type: Image Description: Create an image from another image piece Param[1]: image (type: Image) Param[2]: rec (type: Rectangle) -Function 256: ImageText() (3 input parameters) +Function 264: ImageText() (3 input parameters) Name: ImageText Return type: Image Description: Create an image from text (default font) Param[1]: text (type: const char *) Param[2]: fontSize (type: int) Param[3]: color (type: Color) -Function 257: ImageTextEx() (5 input parameters) +Function 265: ImageTextEx() (5 input parameters) Name: ImageTextEx Return type: Image Description: Create an image from text (custom sprite font) @@ -2510,69 +2565,69 @@ Function 257: ImageTextEx() (5 input parameters) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) Param[5]: tint (type: Color) -Function 258: ImageFormat() (2 input parameters) +Function 266: ImageFormat() (2 input parameters) Name: ImageFormat Return type: void Description: Convert image data to desired format Param[1]: image (type: Image *) Param[2]: newFormat (type: int) -Function 259: ImageToPOT() (2 input parameters) +Function 267: ImageToPOT() (2 input parameters) Name: ImageToPOT Return type: void Description: Convert image to POT (power-of-two) Param[1]: image (type: Image *) Param[2]: fill (type: Color) -Function 260: ImageCrop() (2 input parameters) +Function 268: ImageCrop() (2 input parameters) Name: ImageCrop Return type: void Description: Crop an image to a defined rectangle Param[1]: image (type: Image *) Param[2]: crop (type: Rectangle) -Function 261: ImageAlphaCrop() (2 input parameters) +Function 269: ImageAlphaCrop() (2 input parameters) Name: ImageAlphaCrop Return type: void Description: Crop image depending on alpha value Param[1]: image (type: Image *) Param[2]: threshold (type: float) -Function 262: ImageAlphaClear() (3 input parameters) +Function 270: ImageAlphaClear() (3 input parameters) Name: ImageAlphaClear Return type: void Description: Clear alpha channel to desired color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: threshold (type: float) -Function 263: ImageAlphaMask() (2 input parameters) +Function 271: ImageAlphaMask() (2 input parameters) Name: ImageAlphaMask Return type: void Description: Apply alpha mask to image Param[1]: image (type: Image *) Param[2]: alphaMask (type: Image) -Function 264: ImageAlphaPremultiply() (1 input parameters) +Function 272: ImageAlphaPremultiply() (1 input parameters) Name: ImageAlphaPremultiply Return type: void Description: Premultiply alpha channel Param[1]: image (type: Image *) -Function 265: ImageBlurGaussian() (2 input parameters) +Function 273: ImageBlurGaussian() (2 input parameters) Name: ImageBlurGaussian Return type: void Description: Apply Gaussian blur using a box blur approximation Param[1]: image (type: Image *) Param[2]: blurSize (type: int) -Function 266: ImageResize() (3 input parameters) +Function 274: ImageResize() (3 input parameters) Name: ImageResize Return type: void Description: Resize image (Bicubic scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 267: ImageResizeNN() (3 input parameters) +Function 275: ImageResizeNN() (3 input parameters) Name: ImageResizeNN Return type: void Description: Resize image (Nearest-Neighbor scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 268: ImageResizeCanvas() (6 input parameters) +Function 276: ImageResizeCanvas() (6 input parameters) Name: ImageResizeCanvas Return type: void Description: Resize canvas and fill with color @@ -2582,12 +2637,12 @@ Function 268: ImageResizeCanvas() (6 input parameters) Param[4]: offsetX (type: int) Param[5]: offsetY (type: int) Param[6]: fill (type: Color) -Function 269: ImageMipmaps() (1 input parameters) +Function 277: ImageMipmaps() (1 input parameters) Name: ImageMipmaps Return type: void Description: Compute all mipmap levels for a provided image Param[1]: image (type: Image *) -Function 270: ImageDither() (5 input parameters) +Function 278: ImageDither() (5 input parameters) Name: ImageDither Return type: void Description: Dither image data to 16bpp or lower (Floyd-Steinberg dithering) @@ -2596,109 +2651,109 @@ Function 270: ImageDither() (5 input parameters) Param[3]: gBpp (type: int) Param[4]: bBpp (type: int) Param[5]: aBpp (type: int) -Function 271: ImageFlipVertical() (1 input parameters) +Function 279: ImageFlipVertical() (1 input parameters) Name: ImageFlipVertical Return type: void Description: Flip image vertically Param[1]: image (type: Image *) -Function 272: ImageFlipHorizontal() (1 input parameters) +Function 280: ImageFlipHorizontal() (1 input parameters) Name: ImageFlipHorizontal Return type: void Description: Flip image horizontally Param[1]: image (type: Image *) -Function 273: ImageRotate() (2 input parameters) +Function 281: ImageRotate() (2 input parameters) Name: ImageRotate Return type: void - Description: Rotate image by input angle in degrees (-359 to 359) + Description: Rotate image by input angle in degrees (-359 to 359) Param[1]: image (type: Image *) Param[2]: degrees (type: int) -Function 274: ImageRotateCW() (1 input parameters) +Function 282: ImageRotateCW() (1 input parameters) Name: ImageRotateCW Return type: void Description: Rotate image clockwise 90deg Param[1]: image (type: Image *) -Function 275: ImageRotateCCW() (1 input parameters) +Function 283: ImageRotateCCW() (1 input parameters) Name: ImageRotateCCW Return type: void Description: Rotate image counter-clockwise 90deg Param[1]: image (type: Image *) -Function 276: ImageColorTint() (2 input parameters) +Function 284: ImageColorTint() (2 input parameters) Name: ImageColorTint Return type: void Description: Modify image color: tint Param[1]: image (type: Image *) Param[2]: color (type: Color) -Function 277: ImageColorInvert() (1 input parameters) +Function 285: ImageColorInvert() (1 input parameters) Name: ImageColorInvert Return type: void Description: Modify image color: invert Param[1]: image (type: Image *) -Function 278: ImageColorGrayscale() (1 input parameters) +Function 286: ImageColorGrayscale() (1 input parameters) Name: ImageColorGrayscale Return type: void Description: Modify image color: grayscale Param[1]: image (type: Image *) -Function 279: ImageColorContrast() (2 input parameters) +Function 287: ImageColorContrast() (2 input parameters) Name: ImageColorContrast Return type: void Description: Modify image color: contrast (-100 to 100) Param[1]: image (type: Image *) Param[2]: contrast (type: float) -Function 280: ImageColorBrightness() (2 input parameters) +Function 288: ImageColorBrightness() (2 input parameters) Name: ImageColorBrightness Return type: void Description: Modify image color: brightness (-255 to 255) Param[1]: image (type: Image *) Param[2]: brightness (type: int) -Function 281: ImageColorReplace() (3 input parameters) +Function 289: ImageColorReplace() (3 input parameters) Name: ImageColorReplace Return type: void Description: Modify image color: replace color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: replace (type: Color) -Function 282: LoadImageColors() (1 input parameters) +Function 290: LoadImageColors() (1 input parameters) Name: LoadImageColors Return type: Color * Description: Load color data from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) -Function 283: LoadImagePalette() (3 input parameters) +Function 291: LoadImagePalette() (3 input parameters) Name: LoadImagePalette Return type: Color * Description: Load colors palette from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) Param[2]: maxPaletteSize (type: int) Param[3]: colorCount (type: int *) -Function 284: UnloadImageColors() (1 input parameters) +Function 292: UnloadImageColors() (1 input parameters) Name: UnloadImageColors Return type: void Description: Unload color data loaded with LoadImageColors() Param[1]: colors (type: Color *) -Function 285: UnloadImagePalette() (1 input parameters) +Function 293: UnloadImagePalette() (1 input parameters) Name: UnloadImagePalette Return type: void Description: Unload colors palette loaded with LoadImagePalette() Param[1]: colors (type: Color *) -Function 286: GetImageAlphaBorder() (2 input parameters) +Function 294: GetImageAlphaBorder() (2 input parameters) Name: GetImageAlphaBorder Return type: Rectangle Description: Get image alpha border rectangle Param[1]: image (type: Image) Param[2]: threshold (type: float) -Function 287: GetImageColor() (3 input parameters) +Function 295: GetImageColor() (3 input parameters) Name: GetImageColor Return type: Color Description: Get image pixel color at (x, y) position Param[1]: image (type: Image) Param[2]: x (type: int) Param[3]: y (type: int) -Function 288: ImageClearBackground() (2 input parameters) +Function 296: ImageClearBackground() (2 input parameters) Name: ImageClearBackground Return type: void Description: Clear image background with given color Param[1]: dst (type: Image *) Param[2]: color (type: Color) -Function 289: ImageDrawPixel() (4 input parameters) +Function 297: ImageDrawPixel() (4 input parameters) Name: ImageDrawPixel Return type: void Description: Draw pixel within an image @@ -2706,14 +2761,14 @@ Function 289: ImageDrawPixel() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: color (type: Color) -Function 290: ImageDrawPixelV() (3 input parameters) +Function 298: ImageDrawPixelV() (3 input parameters) Name: ImageDrawPixelV Return type: void Description: Draw pixel within an image (Vector version) Param[1]: dst (type: Image *) Param[2]: position (type: Vector2) Param[3]: color (type: Color) -Function 291: ImageDrawLine() (6 input parameters) +Function 299: ImageDrawLine() (6 input parameters) Name: ImageDrawLine Return type: void Description: Draw line within an image @@ -2723,7 +2778,7 @@ Function 291: ImageDrawLine() (6 input parameters) Param[4]: endPosX (type: int) Param[5]: endPosY (type: int) Param[6]: color (type: Color) -Function 292: ImageDrawLineV() (4 input parameters) +Function 300: ImageDrawLineV() (4 input parameters) Name: ImageDrawLineV Return type: void Description: Draw line within an image (Vector version) @@ -2731,7 +2786,7 @@ Function 292: ImageDrawLineV() (4 input parameters) Param[2]: start (type: Vector2) Param[3]: end (type: Vector2) Param[4]: color (type: Color) -Function 293: ImageDrawCircle() (5 input parameters) +Function 301: ImageDrawCircle() (5 input parameters) Name: ImageDrawCircle Return type: void Description: Draw a filled circle within an image @@ -2740,7 +2795,7 @@ Function 293: ImageDrawCircle() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 294: ImageDrawCircleV() (4 input parameters) +Function 302: ImageDrawCircleV() (4 input parameters) Name: ImageDrawCircleV Return type: void Description: Draw a filled circle within an image (Vector version) @@ -2748,7 +2803,7 @@ Function 294: ImageDrawCircleV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 295: ImageDrawCircleLines() (5 input parameters) +Function 303: ImageDrawCircleLines() (5 input parameters) Name: ImageDrawCircleLines Return type: void Description: Draw circle outline within an image @@ -2757,7 +2812,7 @@ Function 295: ImageDrawCircleLines() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 296: ImageDrawCircleLinesV() (4 input parameters) +Function 304: ImageDrawCircleLinesV() (4 input parameters) Name: ImageDrawCircleLinesV Return type: void Description: Draw circle outline within an image (Vector version) @@ -2765,7 +2820,7 @@ Function 296: ImageDrawCircleLinesV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 297: ImageDrawRectangle() (6 input parameters) +Function 305: ImageDrawRectangle() (6 input parameters) Name: ImageDrawRectangle Return type: void Description: Draw rectangle within an image @@ -2775,7 +2830,7 @@ Function 297: ImageDrawRectangle() (6 input parameters) Param[4]: width (type: int) Param[5]: height (type: int) Param[6]: color (type: Color) -Function 298: ImageDrawRectangleV() (4 input parameters) +Function 306: ImageDrawRectangleV() (4 input parameters) Name: ImageDrawRectangleV Return type: void Description: Draw rectangle within an image (Vector version) @@ -2783,14 +2838,14 @@ Function 298: ImageDrawRectangleV() (4 input parameters) Param[2]: position (type: Vector2) Param[3]: size (type: Vector2) Param[4]: color (type: Color) -Function 299: ImageDrawRectangleRec() (3 input parameters) +Function 307: ImageDrawRectangleRec() (3 input parameters) Name: ImageDrawRectangleRec Return type: void Description: Draw rectangle within an image Param[1]: dst (type: Image *) Param[2]: rec (type: Rectangle) Param[3]: color (type: Color) -Function 300: ImageDrawRectangleLines() (4 input parameters) +Function 308: ImageDrawRectangleLines() (4 input parameters) Name: ImageDrawRectangleLines Return type: void Description: Draw rectangle lines within an image @@ -2798,7 +2853,7 @@ Function 300: ImageDrawRectangleLines() (4 input parameters) Param[2]: rec (type: Rectangle) Param[3]: thick (type: int) Param[4]: color (type: Color) -Function 301: ImageDraw() (5 input parameters) +Function 309: ImageDraw() (5 input parameters) Name: ImageDraw Return type: void Description: Draw a source image within a destination image (tint applied to source) @@ -2807,7 +2862,7 @@ Function 301: ImageDraw() (5 input parameters) Param[3]: srcRec (type: Rectangle) Param[4]: dstRec (type: Rectangle) Param[5]: tint (type: Color) -Function 302: ImageDrawText() (6 input parameters) +Function 310: ImageDrawText() (6 input parameters) Name: ImageDrawText Return type: void Description: Draw text (using default font) within an image (destination) @@ -2817,7 +2872,7 @@ Function 302: ImageDrawText() (6 input parameters) Param[4]: posY (type: int) Param[5]: fontSize (type: int) Param[6]: color (type: Color) -Function 303: ImageDrawTextEx() (7 input parameters) +Function 311: ImageDrawTextEx() (7 input parameters) Name: ImageDrawTextEx Return type: void Description: Draw text (custom sprite font) within an image (destination) @@ -2828,79 +2883,79 @@ Function 303: ImageDrawTextEx() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 304: LoadTexture() (1 input parameters) +Function 312: LoadTexture() (1 input parameters) Name: LoadTexture Return type: Texture2D Description: Load texture from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 305: LoadTextureFromImage() (1 input parameters) +Function 313: LoadTextureFromImage() (1 input parameters) Name: LoadTextureFromImage Return type: Texture2D Description: Load texture from image data Param[1]: image (type: Image) -Function 306: LoadTextureCubemap() (2 input parameters) +Function 314: LoadTextureCubemap() (2 input parameters) Name: LoadTextureCubemap Return type: TextureCubemap Description: Load cubemap from image, multiple image cubemap layouts supported Param[1]: image (type: Image) Param[2]: layout (type: int) -Function 307: LoadRenderTexture() (2 input parameters) +Function 315: LoadRenderTexture() (2 input parameters) Name: LoadRenderTexture Return type: RenderTexture2D Description: Load texture for rendering (framebuffer) Param[1]: width (type: int) Param[2]: height (type: int) -Function 308: IsTextureReady() (1 input parameters) +Function 316: IsTextureReady() (1 input parameters) Name: IsTextureReady Return type: bool Description: Check if a texture is ready Param[1]: texture (type: Texture2D) -Function 309: UnloadTexture() (1 input parameters) +Function 317: UnloadTexture() (1 input parameters) Name: UnloadTexture Return type: void Description: Unload texture from GPU memory (VRAM) Param[1]: texture (type: Texture2D) -Function 310: IsRenderTextureReady() (1 input parameters) +Function 318: IsRenderTextureReady() (1 input parameters) Name: IsRenderTextureReady Return type: bool Description: Check if a render texture is ready Param[1]: target (type: RenderTexture2D) -Function 311: UnloadRenderTexture() (1 input parameters) +Function 319: UnloadRenderTexture() (1 input parameters) Name: UnloadRenderTexture Return type: void Description: Unload render texture from GPU memory (VRAM) Param[1]: target (type: RenderTexture2D) -Function 312: UpdateTexture() (2 input parameters) +Function 320: UpdateTexture() (2 input parameters) Name: UpdateTexture Return type: void Description: Update GPU texture with new data Param[1]: texture (type: Texture2D) Param[2]: pixels (type: const void *) -Function 313: UpdateTextureRec() (3 input parameters) +Function 321: UpdateTextureRec() (3 input parameters) Name: UpdateTextureRec Return type: void Description: Update GPU texture rectangle with new data Param[1]: texture (type: Texture2D) Param[2]: rec (type: Rectangle) Param[3]: pixels (type: const void *) -Function 314: GenTextureMipmaps() (1 input parameters) +Function 322: GenTextureMipmaps() (1 input parameters) Name: GenTextureMipmaps Return type: void Description: Generate GPU mipmaps for a texture Param[1]: texture (type: Texture2D *) -Function 315: SetTextureFilter() (2 input parameters) +Function 323: SetTextureFilter() (2 input parameters) Name: SetTextureFilter Return type: void Description: Set texture scaling filter mode Param[1]: texture (type: Texture2D) Param[2]: filter (type: int) -Function 316: SetTextureWrap() (2 input parameters) +Function 324: SetTextureWrap() (2 input parameters) Name: SetTextureWrap Return type: void Description: Set texture wrapping mode Param[1]: texture (type: Texture2D) Param[2]: wrap (type: int) -Function 317: DrawTexture() (4 input parameters) +Function 325: DrawTexture() (4 input parameters) Name: DrawTexture Return type: void Description: Draw a Texture2D @@ -2908,14 +2963,14 @@ Function 317: DrawTexture() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: tint (type: Color) -Function 318: DrawTextureV() (3 input parameters) +Function 326: DrawTextureV() (3 input parameters) Name: DrawTextureV Return type: void Description: Draw a Texture2D with position defined as Vector2 Param[1]: texture (type: Texture2D) Param[2]: position (type: Vector2) Param[3]: tint (type: Color) -Function 319: DrawTextureEx() (5 input parameters) +Function 327: DrawTextureEx() (5 input parameters) Name: DrawTextureEx Return type: void Description: Draw a Texture2D with extended parameters @@ -2924,7 +2979,7 @@ Function 319: DrawTextureEx() (5 input parameters) Param[3]: rotation (type: float) Param[4]: scale (type: float) Param[5]: tint (type: Color) -Function 320: DrawTextureRec() (4 input parameters) +Function 328: DrawTextureRec() (4 input parameters) Name: DrawTextureRec Return type: void Description: Draw a part of a texture defined by a rectangle @@ -2932,7 +2987,7 @@ Function 320: DrawTextureRec() (4 input parameters) Param[2]: source (type: Rectangle) Param[3]: position (type: Vector2) Param[4]: tint (type: Color) -Function 321: DrawTexturePro() (6 input parameters) +Function 329: DrawTexturePro() (6 input parameters) Name: DrawTexturePro Return type: void Description: Draw a part of a texture defined by a rectangle with 'pro' parameters @@ -2942,7 +2997,7 @@ Function 321: DrawTexturePro() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 322: DrawTextureNPatch() (6 input parameters) +Function 330: DrawTextureNPatch() (6 input parameters) Name: DrawTextureNPatch Return type: void Description: Draws a texture (or part of it) that stretches or shrinks nicely @@ -2952,121 +3007,121 @@ Function 322: DrawTextureNPatch() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 323: Fade() (2 input parameters) +Function 331: Fade() (2 input parameters) Name: Fade Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 324: ColorToInt() (1 input parameters) +Function 332: ColorToInt() (1 input parameters) Name: ColorToInt Return type: int Description: Get hexadecimal value for a Color Param[1]: color (type: Color) -Function 325: ColorNormalize() (1 input parameters) +Function 333: ColorNormalize() (1 input parameters) Name: ColorNormalize Return type: Vector4 Description: Get Color normalized as float [0..1] Param[1]: color (type: Color) -Function 326: ColorFromNormalized() (1 input parameters) +Function 334: ColorFromNormalized() (1 input parameters) Name: ColorFromNormalized Return type: Color Description: Get Color from normalized values [0..1] Param[1]: normalized (type: Vector4) -Function 327: ColorToHSV() (1 input parameters) +Function 335: ColorToHSV() (1 input parameters) Name: ColorToHSV Return type: Vector3 Description: Get HSV values for a Color, hue [0..360], saturation/value [0..1] Param[1]: color (type: Color) -Function 328: ColorFromHSV() (3 input parameters) +Function 336: ColorFromHSV() (3 input parameters) Name: ColorFromHSV Return type: Color Description: Get a Color from HSV values, hue [0..360], saturation/value [0..1] Param[1]: hue (type: float) Param[2]: saturation (type: float) Param[3]: value (type: float) -Function 329: ColorTint() (2 input parameters) +Function 337: ColorTint() (2 input parameters) Name: ColorTint Return type: Color Description: Get color multiplied with another color Param[1]: color (type: Color) Param[2]: tint (type: Color) -Function 330: ColorBrightness() (2 input parameters) +Function 338: ColorBrightness() (2 input parameters) Name: ColorBrightness Return type: Color Description: Get color with brightness correction, brightness factor goes from -1.0f to 1.0f Param[1]: color (type: Color) Param[2]: factor (type: float) -Function 331: ColorContrast() (2 input parameters) +Function 339: ColorContrast() (2 input parameters) Name: ColorContrast Return type: Color Description: Get color with contrast correction, contrast values between -1.0f and 1.0f Param[1]: color (type: Color) Param[2]: contrast (type: float) -Function 332: ColorAlpha() (2 input parameters) +Function 340: ColorAlpha() (2 input parameters) Name: ColorAlpha Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 333: ColorAlphaBlend() (3 input parameters) +Function 341: ColorAlphaBlend() (3 input parameters) Name: ColorAlphaBlend Return type: Color Description: Get src alpha-blended into dst color with tint Param[1]: dst (type: Color) Param[2]: src (type: Color) Param[3]: tint (type: Color) -Function 334: GetColor() (1 input parameters) +Function 342: GetColor() (1 input parameters) Name: GetColor Return type: Color Description: Get Color structure from hexadecimal value Param[1]: hexValue (type: unsigned int) -Function 335: GetPixelColor() (2 input parameters) +Function 343: GetPixelColor() (2 input parameters) Name: GetPixelColor Return type: Color Description: Get Color from a source pixel pointer of certain format Param[1]: srcPtr (type: void *) Param[2]: format (type: int) -Function 336: SetPixelColor() (3 input parameters) +Function 344: SetPixelColor() (3 input parameters) Name: SetPixelColor Return type: void Description: Set color formatted into destination pixel pointer Param[1]: dstPtr (type: void *) Param[2]: color (type: Color) Param[3]: format (type: int) -Function 337: GetPixelDataSize() (3 input parameters) +Function 345: GetPixelDataSize() (3 input parameters) Name: GetPixelDataSize Return type: int Description: Get pixel data size in bytes for certain format Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: format (type: int) -Function 338: GetFontDefault() (0 input parameters) +Function 346: GetFontDefault() (0 input parameters) Name: GetFontDefault Return type: Font Description: Get the default Font No input parameters -Function 339: LoadFont() (1 input parameters) +Function 347: LoadFont() (1 input parameters) Name: LoadFont Return type: Font Description: Load font from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 340: LoadFontEx() (4 input parameters) +Function 348: LoadFontEx() (4 input parameters) Name: LoadFontEx Return type: Font - Description: Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set + Description: Load font from file with extended parameters, use NULL for codepoints and 0 for codepointCount to load the default character setFont Param[1]: fileName (type: const char *) Param[2]: fontSize (type: int) - Param[3]: fontChars (type: int *) - Param[4]: glyphCount (type: int) -Function 341: LoadFontFromImage() (3 input parameters) + Param[3]: codepoints (type: int *) + Param[4]: codepointCount (type: int) +Function 349: LoadFontFromImage() (3 input parameters) Name: LoadFontFromImage Return type: Font Description: Load font from Image (XNA style) Param[1]: image (type: Image) Param[2]: key (type: Color) Param[3]: firstChar (type: int) -Function 342: LoadFontFromMemory() (6 input parameters) +Function 350: LoadFontFromMemory() (6 input parameters) Name: LoadFontFromMemory Return type: Font Description: Load font from memory buffer, fileType refers to extension: i.e. '.ttf' @@ -3074,57 +3129,57 @@ Function 342: LoadFontFromMemory() (6 input parameters) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) Param[4]: fontSize (type: int) - Param[5]: fontChars (type: int *) - Param[6]: glyphCount (type: int) -Function 343: IsFontReady() (1 input parameters) + Param[5]: codepoints (type: int *) + Param[6]: codepointCount (type: int) +Function 351: IsFontReady() (1 input parameters) Name: IsFontReady Return type: bool Description: Check if a font is ready Param[1]: font (type: Font) -Function 344: LoadFontData() (6 input parameters) +Function 352: LoadFontData() (6 input parameters) Name: LoadFontData Return type: GlyphInfo * Description: Load font data for further use Param[1]: fileData (type: const unsigned char *) Param[2]: dataSize (type: int) Param[3]: fontSize (type: int) - Param[4]: fontChars (type: int *) - Param[5]: glyphCount (type: int) + Param[4]: codepoints (type: int *) + Param[5]: codepointCount (type: int) Param[6]: type (type: int) -Function 345: GenImageFontAtlas() (6 input parameters) +Function 353: GenImageFontAtlas() (6 input parameters) Name: GenImageFontAtlas Return type: Image Description: Generate image font atlas using chars info - Param[1]: chars (type: const GlyphInfo *) - Param[2]: recs (type: Rectangle **) + Param[1]: glyphs (type: const GlyphInfo *) + Param[2]: glyphRecs (type: Rectangle **) Param[3]: glyphCount (type: int) Param[4]: fontSize (type: int) Param[5]: padding (type: int) Param[6]: packMethod (type: int) -Function 346: UnloadFontData() (2 input parameters) +Function 354: UnloadFontData() (2 input parameters) Name: UnloadFontData Return type: void Description: Unload font chars info data (RAM) - Param[1]: chars (type: GlyphInfo *) + Param[1]: glyphs (type: GlyphInfo *) Param[2]: glyphCount (type: int) -Function 347: UnloadFont() (1 input parameters) +Function 355: UnloadFont() (1 input parameters) Name: UnloadFont Return type: void Description: Unload font from GPU memory (VRAM) Param[1]: font (type: Font) -Function 348: ExportFontAsCode() (2 input parameters) +Function 356: ExportFontAsCode() (2 input parameters) Name: ExportFontAsCode Return type: bool Description: Export font as code file, returns true on success Param[1]: font (type: Font) Param[2]: fileName (type: const char *) -Function 349: DrawFPS() (2 input parameters) +Function 357: DrawFPS() (2 input parameters) Name: DrawFPS Return type: void Description: Draw current FPS Param[1]: posX (type: int) Param[2]: posY (type: int) -Function 350: DrawText() (5 input parameters) +Function 358: DrawText() (5 input parameters) Name: DrawText Return type: void Description: Draw text (using default font) @@ -3133,7 +3188,7 @@ Function 350: DrawText() (5 input parameters) Param[3]: posY (type: int) Param[4]: fontSize (type: int) Param[5]: color (type: Color) -Function 351: DrawTextEx() (6 input parameters) +Function 359: DrawTextEx() (6 input parameters) Name: DrawTextEx Return type: void Description: Draw text using font and additional parameters @@ -3143,7 +3198,7 @@ Function 351: DrawTextEx() (6 input parameters) Param[4]: fontSize (type: float) Param[5]: spacing (type: float) Param[6]: tint (type: Color) -Function 352: DrawTextPro() (8 input parameters) +Function 360: DrawTextPro() (8 input parameters) Name: DrawTextPro Return type: void Description: Draw text using Font and pro parameters (rotation) @@ -3155,7 +3210,7 @@ Function 352: DrawTextPro() (8 input parameters) Param[6]: fontSize (type: float) Param[7]: spacing (type: float) Param[8]: tint (type: Color) -Function 353: DrawTextCodepoint() (5 input parameters) +Function 361: DrawTextCodepoint() (5 input parameters) Name: DrawTextCodepoint Return type: void Description: Draw one character (codepoint) @@ -3164,24 +3219,29 @@ Function 353: DrawTextCodepoint() (5 input parameters) Param[3]: position (type: Vector2) Param[4]: fontSize (type: float) Param[5]: tint (type: Color) -Function 354: DrawTextCodepoints() (7 input parameters) +Function 362: DrawTextCodepoints() (7 input parameters) Name: DrawTextCodepoints Return type: void Description: Draw multiple character (codepoint) Param[1]: font (type: Font) Param[2]: codepoints (type: const int *) - Param[3]: count (type: int) + Param[3]: codepointCount (type: int) Param[4]: position (type: Vector2) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 355: MeasureText() (2 input parameters) +Function 363: SetTextLineSpacing() (1 input parameters) + Name: SetTextLineSpacing + Return type: void + Description: Set vertical line spacing when drawing with line-breaks + Param[1]: spacing (type: int) +Function 364: MeasureText() (2 input parameters) Name: MeasureText Return type: int Description: Measure string width for default font Param[1]: text (type: const char *) Param[2]: fontSize (type: int) -Function 356: MeasureTextEx() (4 input parameters) +Function 365: MeasureTextEx() (4 input parameters) Name: MeasureTextEx Return type: Vector2 Description: Measure string size for Font @@ -3189,180 +3249,180 @@ Function 356: MeasureTextEx() (4 input parameters) Param[2]: text (type: const char *) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) -Function 357: GetGlyphIndex() (2 input parameters) +Function 366: GetGlyphIndex() (2 input parameters) Name: GetGlyphIndex Return type: int Description: Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 358: GetGlyphInfo() (2 input parameters) +Function 367: GetGlyphInfo() (2 input parameters) Name: GetGlyphInfo Return type: GlyphInfo Description: Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 359: GetGlyphAtlasRec() (2 input parameters) +Function 368: GetGlyphAtlasRec() (2 input parameters) Name: GetGlyphAtlasRec Return type: Rectangle Description: Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 360: LoadUTF8() (2 input parameters) +Function 369: LoadUTF8() (2 input parameters) Name: LoadUTF8 Return type: char * Description: Load UTF-8 text encoded from codepoints array Param[1]: codepoints (type: const int *) Param[2]: length (type: int) -Function 361: UnloadUTF8() (1 input parameters) +Function 370: UnloadUTF8() (1 input parameters) Name: UnloadUTF8 Return type: void Description: Unload UTF-8 text encoded from codepoints array Param[1]: text (type: char *) -Function 362: LoadCodepoints() (2 input parameters) +Function 371: LoadCodepoints() (2 input parameters) Name: LoadCodepoints Return type: int * Description: Load all codepoints from a UTF-8 text string, codepoints count returned by parameter Param[1]: text (type: const char *) Param[2]: count (type: int *) -Function 363: UnloadCodepoints() (1 input parameters) +Function 372: UnloadCodepoints() (1 input parameters) Name: UnloadCodepoints Return type: void Description: Unload codepoints data from memory Param[1]: codepoints (type: int *) -Function 364: GetCodepointCount() (1 input parameters) +Function 373: GetCodepointCount() (1 input parameters) Name: GetCodepointCount Return type: int Description: Get total number of codepoints in a UTF-8 encoded string Param[1]: text (type: const char *) -Function 365: GetCodepoint() (2 input parameters) +Function 374: GetCodepoint() (2 input parameters) Name: GetCodepoint Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 366: GetCodepointNext() (2 input parameters) +Function 375: GetCodepointNext() (2 input parameters) Name: GetCodepointNext Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 367: GetCodepointPrevious() (2 input parameters) +Function 376: GetCodepointPrevious() (2 input parameters) Name: GetCodepointPrevious Return type: int Description: Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 368: CodepointToUTF8() (2 input parameters) +Function 377: CodepointToUTF8() (2 input parameters) Name: CodepointToUTF8 Return type: const char * Description: Encode one codepoint into UTF-8 byte array (array length returned as parameter) Param[1]: codepoint (type: int) Param[2]: utf8Size (type: int *) -Function 369: TextCopy() (2 input parameters) +Function 378: TextCopy() (2 input parameters) Name: TextCopy Return type: int Description: Copy one string to another, returns bytes copied Param[1]: dst (type: char *) Param[2]: src (type: const char *) -Function 370: TextIsEqual() (2 input parameters) +Function 379: TextIsEqual() (2 input parameters) Name: TextIsEqual Return type: bool Description: Check if two text string are equal Param[1]: text1 (type: const char *) Param[2]: text2 (type: const char *) -Function 371: TextLength() (1 input parameters) +Function 380: TextLength() (1 input parameters) Name: TextLength Return type: unsigned int Description: Get text length, checks for '\0' ending Param[1]: text (type: const char *) -Function 372: TextFormat() (2 input parameters) +Function 381: TextFormat() (2 input parameters) Name: TextFormat Return type: const char * Description: Text formatting with variables (sprintf() style) Param[1]: text (type: const char *) Param[2]: args (type: ...) -Function 373: TextSubtext() (3 input parameters) +Function 382: TextSubtext() (3 input parameters) Name: TextSubtext Return type: const char * Description: Get a piece of a text string Param[1]: text (type: const char *) Param[2]: position (type: int) Param[3]: length (type: int) -Function 374: TextReplace() (3 input parameters) +Function 383: TextReplace() (3 input parameters) Name: TextReplace Return type: char * Description: Replace text string (WARNING: memory must be freed!) Param[1]: text (type: char *) Param[2]: replace (type: const char *) Param[3]: by (type: const char *) -Function 375: TextInsert() (3 input parameters) +Function 384: TextInsert() (3 input parameters) Name: TextInsert Return type: char * Description: Insert text in a position (WARNING: memory must be freed!) Param[1]: text (type: const char *) Param[2]: insert (type: const char *) Param[3]: position (type: int) -Function 376: TextJoin() (3 input parameters) +Function 385: TextJoin() (3 input parameters) Name: TextJoin Return type: const char * Description: Join text strings with delimiter Param[1]: textList (type: const char **) Param[2]: count (type: int) Param[3]: delimiter (type: const char *) -Function 377: TextSplit() (3 input parameters) +Function 386: TextSplit() (3 input parameters) Name: TextSplit Return type: const char ** Description: Split text into multiple strings Param[1]: text (type: const char *) Param[2]: delimiter (type: char) Param[3]: count (type: int *) -Function 378: TextAppend() (3 input parameters) +Function 387: TextAppend() (3 input parameters) Name: TextAppend Return type: void Description: Append text at specific position and move cursor! Param[1]: text (type: char *) Param[2]: append (type: const char *) Param[3]: position (type: int *) -Function 379: TextFindIndex() (2 input parameters) +Function 388: TextFindIndex() (2 input parameters) Name: TextFindIndex Return type: int Description: Find first text occurrence within a string Param[1]: text (type: const char *) Param[2]: find (type: const char *) -Function 380: TextToUpper() (1 input parameters) +Function 389: TextToUpper() (1 input parameters) Name: TextToUpper Return type: const char * Description: Get upper case version of provided string Param[1]: text (type: const char *) -Function 381: TextToLower() (1 input parameters) +Function 390: TextToLower() (1 input parameters) Name: TextToLower Return type: const char * Description: Get lower case version of provided string Param[1]: text (type: const char *) -Function 382: TextToPascal() (1 input parameters) +Function 391: TextToPascal() (1 input parameters) Name: TextToPascal Return type: const char * Description: Get Pascal case notation version of provided string Param[1]: text (type: const char *) -Function 383: TextToInteger() (1 input parameters) +Function 392: TextToInteger() (1 input parameters) Name: TextToInteger Return type: int Description: Get integer value from text (negative values not supported) Param[1]: text (type: const char *) -Function 384: DrawLine3D() (3 input parameters) +Function 393: DrawLine3D() (3 input parameters) Name: DrawLine3D Return type: void Description: Draw a line in 3D world space Param[1]: startPos (type: Vector3) Param[2]: endPos (type: Vector3) Param[3]: color (type: Color) -Function 385: DrawPoint3D() (2 input parameters) +Function 394: DrawPoint3D() (2 input parameters) Name: DrawPoint3D Return type: void Description: Draw a point in 3D space, actually a small line Param[1]: position (type: Vector3) Param[2]: color (type: Color) -Function 386: DrawCircle3D() (5 input parameters) +Function 395: DrawCircle3D() (5 input parameters) Name: DrawCircle3D Return type: void Description: Draw a circle in 3D world space @@ -3371,7 +3431,7 @@ Function 386: DrawCircle3D() (5 input parameters) Param[3]: rotationAxis (type: Vector3) Param[4]: rotationAngle (type: float) Param[5]: color (type: Color) -Function 387: DrawTriangle3D() (4 input parameters) +Function 396: DrawTriangle3D() (4 input parameters) Name: DrawTriangle3D Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -3379,14 +3439,14 @@ Function 387: DrawTriangle3D() (4 input parameters) Param[2]: v2 (type: Vector3) Param[3]: v3 (type: Vector3) Param[4]: color (type: Color) -Function 388: DrawTriangleStrip3D() (3 input parameters) +Function 397: DrawTriangleStrip3D() (3 input parameters) Name: DrawTriangleStrip3D Return type: void Description: Draw a triangle strip defined by points Param[1]: points (type: Vector3 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 389: DrawCube() (5 input parameters) +Function 398: DrawCube() (5 input parameters) Name: DrawCube Return type: void Description: Draw cube @@ -3395,14 +3455,14 @@ Function 389: DrawCube() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 390: DrawCubeV() (3 input parameters) +Function 399: DrawCubeV() (3 input parameters) Name: DrawCubeV Return type: void Description: Draw cube (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 391: DrawCubeWires() (5 input parameters) +Function 400: DrawCubeWires() (5 input parameters) Name: DrawCubeWires Return type: void Description: Draw cube wires @@ -3411,21 +3471,21 @@ Function 391: DrawCubeWires() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 392: DrawCubeWiresV() (3 input parameters) +Function 401: DrawCubeWiresV() (3 input parameters) Name: DrawCubeWiresV Return type: void Description: Draw cube wires (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 393: DrawSphere() (3 input parameters) +Function 402: DrawSphere() (3 input parameters) Name: DrawSphere Return type: void Description: Draw sphere Param[1]: centerPos (type: Vector3) Param[2]: radius (type: float) Param[3]: color (type: Color) -Function 394: DrawSphereEx() (5 input parameters) +Function 403: DrawSphereEx() (5 input parameters) Name: DrawSphereEx Return type: void Description: Draw sphere with extended parameters @@ -3434,7 +3494,7 @@ Function 394: DrawSphereEx() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 395: DrawSphereWires() (5 input parameters) +Function 404: DrawSphereWires() (5 input parameters) Name: DrawSphereWires Return type: void Description: Draw sphere wires @@ -3443,7 +3503,7 @@ Function 395: DrawSphereWires() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 396: DrawCylinder() (6 input parameters) +Function 405: DrawCylinder() (6 input parameters) Name: DrawCylinder Return type: void Description: Draw a cylinder/cone @@ -3453,7 +3513,7 @@ Function 396: DrawCylinder() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 397: DrawCylinderEx() (6 input parameters) +Function 406: DrawCylinderEx() (6 input parameters) Name: DrawCylinderEx Return type: void Description: Draw a cylinder with base at startPos and top at endPos @@ -3463,7 +3523,7 @@ Function 397: DrawCylinderEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 398: DrawCylinderWires() (6 input parameters) +Function 407: DrawCylinderWires() (6 input parameters) Name: DrawCylinderWires Return type: void Description: Draw a cylinder/cone wires @@ -3473,7 +3533,7 @@ Function 398: DrawCylinderWires() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 399: DrawCylinderWiresEx() (6 input parameters) +Function 408: DrawCylinderWiresEx() (6 input parameters) Name: DrawCylinderWiresEx Return type: void Description: Draw a cylinder wires with base at startPos and top at endPos @@ -3483,7 +3543,7 @@ Function 399: DrawCylinderWiresEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 400: DrawCapsule() (6 input parameters) +Function 409: DrawCapsule() (6 input parameters) Name: DrawCapsule Return type: void Description: Draw a capsule with the center of its sphere caps at startPos and endPos @@ -3493,7 +3553,7 @@ Function 400: DrawCapsule() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 401: DrawCapsuleWires() (6 input parameters) +Function 410: DrawCapsuleWires() (6 input parameters) Name: DrawCapsuleWires Return type: void Description: Draw capsule wireframe with the center of its sphere caps at startPos and endPos @@ -3503,51 +3563,51 @@ Function 401: DrawCapsuleWires() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 402: DrawPlane() (3 input parameters) +Function 411: DrawPlane() (3 input parameters) Name: DrawPlane Return type: void Description: Draw a plane XZ Param[1]: centerPos (type: Vector3) Param[2]: size (type: Vector2) Param[3]: color (type: Color) -Function 403: DrawRay() (2 input parameters) +Function 412: DrawRay() (2 input parameters) Name: DrawRay Return type: void Description: Draw a ray line Param[1]: ray (type: Ray) Param[2]: color (type: Color) -Function 404: DrawGrid() (2 input parameters) +Function 413: DrawGrid() (2 input parameters) Name: DrawGrid Return type: void Description: Draw a grid (centered at (0, 0, 0)) Param[1]: slices (type: int) Param[2]: spacing (type: float) -Function 405: LoadModel() (1 input parameters) +Function 414: LoadModel() (1 input parameters) Name: LoadModel Return type: Model Description: Load model from files (meshes and materials) Param[1]: fileName (type: const char *) -Function 406: LoadModelFromMesh() (1 input parameters) +Function 415: LoadModelFromMesh() (1 input parameters) Name: LoadModelFromMesh Return type: Model Description: Load model from generated mesh (default material) Param[1]: mesh (type: Mesh) -Function 407: IsModelReady() (1 input parameters) +Function 416: IsModelReady() (1 input parameters) Name: IsModelReady Return type: bool Description: Check if a model is ready Param[1]: model (type: Model) -Function 408: UnloadModel() (1 input parameters) +Function 417: UnloadModel() (1 input parameters) Name: UnloadModel Return type: void Description: Unload model (including meshes) from memory (RAM and/or VRAM) Param[1]: model (type: Model) -Function 409: GetModelBoundingBox() (1 input parameters) +Function 418: GetModelBoundingBox() (1 input parameters) Name: GetModelBoundingBox Return type: BoundingBox Description: Compute model bounding box limits (considers all meshes) Param[1]: model (type: Model) -Function 410: DrawModel() (4 input parameters) +Function 419: DrawModel() (4 input parameters) Name: DrawModel Return type: void Description: Draw a model (with texture if set) @@ -3555,7 +3615,7 @@ Function 410: DrawModel() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 411: DrawModelEx() (6 input parameters) +Function 420: DrawModelEx() (6 input parameters) Name: DrawModelEx Return type: void Description: Draw a model with extended parameters @@ -3565,7 +3625,7 @@ Function 411: DrawModelEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 412: DrawModelWires() (4 input parameters) +Function 421: DrawModelWires() (4 input parameters) Name: DrawModelWires Return type: void Description: Draw a model wires (with texture if set) @@ -3573,7 +3633,7 @@ Function 412: DrawModelWires() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 413: DrawModelWiresEx() (6 input parameters) +Function 422: DrawModelWiresEx() (6 input parameters) Name: DrawModelWiresEx Return type: void Description: Draw a model wires (with texture if set) with extended parameters @@ -3583,13 +3643,13 @@ Function 413: DrawModelWiresEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 414: DrawBoundingBox() (2 input parameters) +Function 423: DrawBoundingBox() (2 input parameters) Name: DrawBoundingBox Return type: void Description: Draw bounding box (wires) Param[1]: box (type: BoundingBox) Param[2]: color (type: Color) -Function 415: DrawBillboard() (5 input parameters) +Function 424: DrawBillboard() (5 input parameters) Name: DrawBillboard Return type: void Description: Draw a billboard texture @@ -3598,7 +3658,7 @@ Function 415: DrawBillboard() (5 input parameters) Param[3]: position (type: Vector3) Param[4]: size (type: float) Param[5]: tint (type: Color) -Function 416: DrawBillboardRec() (6 input parameters) +Function 425: DrawBillboardRec() (6 input parameters) Name: DrawBillboardRec Return type: void Description: Draw a billboard texture defined by source @@ -3608,7 +3668,7 @@ Function 416: DrawBillboardRec() (6 input parameters) Param[4]: position (type: Vector3) Param[5]: size (type: Vector2) Param[6]: tint (type: Color) -Function 417: DrawBillboardPro() (9 input parameters) +Function 426: DrawBillboardPro() (9 input parameters) Name: DrawBillboardPro Return type: void Description: Draw a billboard texture defined by source and rotation @@ -3621,13 +3681,13 @@ Function 417: DrawBillboardPro() (9 input parameters) Param[7]: origin (type: Vector2) Param[8]: rotation (type: float) Param[9]: tint (type: Color) -Function 418: UploadMesh() (2 input parameters) +Function 427: UploadMesh() (2 input parameters) Name: UploadMesh Return type: void Description: Upload mesh vertex data in GPU and provide VAO/VBO ids Param[1]: mesh (type: Mesh *) Param[2]: dynamic (type: bool) -Function 419: UpdateMeshBuffer() (5 input parameters) +Function 428: UpdateMeshBuffer() (5 input parameters) Name: UpdateMeshBuffer Return type: void Description: Update mesh vertex data in GPU for a specific buffer index @@ -3636,19 +3696,19 @@ Function 419: UpdateMeshBuffer() (5 input parameters) Param[3]: data (type: const void *) Param[4]: dataSize (type: int) Param[5]: offset (type: int) -Function 420: UnloadMesh() (1 input parameters) +Function 429: UnloadMesh() (1 input parameters) Name: UnloadMesh Return type: void Description: Unload mesh data from CPU and GPU Param[1]: mesh (type: Mesh) -Function 421: DrawMesh() (3 input parameters) +Function 430: DrawMesh() (3 input parameters) Name: DrawMesh Return type: void Description: Draw a 3d mesh with material and transform Param[1]: mesh (type: Mesh) Param[2]: material (type: Material) Param[3]: transform (type: Matrix) -Function 422: DrawMeshInstanced() (4 input parameters) +Function 431: DrawMeshInstanced() (4 input parameters) Name: DrawMeshInstanced Return type: void Description: Draw multiple mesh instances with material and different transforms @@ -3656,29 +3716,29 @@ Function 422: DrawMeshInstanced() (4 input parameters) Param[2]: material (type: Material) Param[3]: transforms (type: const Matrix *) Param[4]: instances (type: int) -Function 423: ExportMesh() (2 input parameters) +Function 432: ExportMesh() (2 input parameters) Name: ExportMesh Return type: bool Description: Export mesh data to file, returns true on success Param[1]: mesh (type: Mesh) Param[2]: fileName (type: const char *) -Function 424: GetMeshBoundingBox() (1 input parameters) +Function 433: GetMeshBoundingBox() (1 input parameters) Name: GetMeshBoundingBox Return type: BoundingBox Description: Compute mesh bounding box limits Param[1]: mesh (type: Mesh) -Function 425: GenMeshTangents() (1 input parameters) +Function 434: GenMeshTangents() (1 input parameters) Name: GenMeshTangents Return type: void Description: Compute mesh tangents Param[1]: mesh (type: Mesh *) -Function 426: GenMeshPoly() (2 input parameters) +Function 435: GenMeshPoly() (2 input parameters) Name: GenMeshPoly Return type: Mesh Description: Generate polygonal mesh Param[1]: sides (type: int) Param[2]: radius (type: float) -Function 427: GenMeshPlane() (4 input parameters) +Function 436: GenMeshPlane() (4 input parameters) Name: GenMeshPlane Return type: Mesh Description: Generate plane mesh (with subdivisions) @@ -3686,42 +3746,42 @@ Function 427: GenMeshPlane() (4 input parameters) Param[2]: length (type: float) Param[3]: resX (type: int) Param[4]: resZ (type: int) -Function 428: GenMeshCube() (3 input parameters) +Function 437: GenMeshCube() (3 input parameters) Name: GenMeshCube Return type: Mesh Description: Generate cuboid mesh Param[1]: width (type: float) Param[2]: height (type: float) Param[3]: length (type: float) -Function 429: GenMeshSphere() (3 input parameters) +Function 438: GenMeshSphere() (3 input parameters) Name: GenMeshSphere Return type: Mesh Description: Generate sphere mesh (standard sphere) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 430: GenMeshHemiSphere() (3 input parameters) +Function 439: GenMeshHemiSphere() (3 input parameters) Name: GenMeshHemiSphere Return type: Mesh Description: Generate half-sphere mesh (no bottom cap) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 431: GenMeshCylinder() (3 input parameters) +Function 440: GenMeshCylinder() (3 input parameters) Name: GenMeshCylinder Return type: Mesh Description: Generate cylinder mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 432: GenMeshCone() (3 input parameters) +Function 441: GenMeshCone() (3 input parameters) Name: GenMeshCone Return type: Mesh Description: Generate cone/pyramid mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 433: GenMeshTorus() (4 input parameters) +Function 442: GenMeshTorus() (4 input parameters) Name: GenMeshTorus Return type: Mesh Description: Generate torus mesh @@ -3729,7 +3789,7 @@ Function 433: GenMeshTorus() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 434: GenMeshKnot() (4 input parameters) +Function 443: GenMeshKnot() (4 input parameters) Name: GenMeshKnot Return type: Mesh Description: Generate trefoil knot mesh @@ -3737,84 +3797,84 @@ Function 434: GenMeshKnot() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 435: GenMeshHeightmap() (2 input parameters) +Function 444: GenMeshHeightmap() (2 input parameters) Name: GenMeshHeightmap Return type: Mesh Description: Generate heightmap mesh from image data Param[1]: heightmap (type: Image) Param[2]: size (type: Vector3) -Function 436: GenMeshCubicmap() (2 input parameters) +Function 445: GenMeshCubicmap() (2 input parameters) Name: GenMeshCubicmap Return type: Mesh Description: Generate cubes-based map mesh from image data Param[1]: cubicmap (type: Image) Param[2]: cubeSize (type: Vector3) -Function 437: LoadMaterials() (2 input parameters) +Function 446: LoadMaterials() (2 input parameters) Name: LoadMaterials Return type: Material * Description: Load materials from model file Param[1]: fileName (type: const char *) Param[2]: materialCount (type: int *) -Function 438: LoadMaterialDefault() (0 input parameters) +Function 447: LoadMaterialDefault() (0 input parameters) Name: LoadMaterialDefault Return type: Material Description: Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) No input parameters -Function 439: IsMaterialReady() (1 input parameters) +Function 448: IsMaterialReady() (1 input parameters) Name: IsMaterialReady Return type: bool Description: Check if a material is ready Param[1]: material (type: Material) -Function 440: UnloadMaterial() (1 input parameters) +Function 449: UnloadMaterial() (1 input parameters) Name: UnloadMaterial Return type: void Description: Unload material from GPU memory (VRAM) Param[1]: material (type: Material) -Function 441: SetMaterialTexture() (3 input parameters) +Function 450: SetMaterialTexture() (3 input parameters) Name: SetMaterialTexture Return type: void Description: Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) Param[1]: material (type: Material *) Param[2]: mapType (type: int) Param[3]: texture (type: Texture2D) -Function 442: SetModelMeshMaterial() (3 input parameters) +Function 451: SetModelMeshMaterial() (3 input parameters) Name: SetModelMeshMaterial Return type: void Description: Set material for a mesh Param[1]: model (type: Model *) Param[2]: meshId (type: int) Param[3]: materialId (type: int) -Function 443: LoadModelAnimations() (2 input parameters) +Function 452: LoadModelAnimations() (2 input parameters) Name: LoadModelAnimations Return type: ModelAnimation * Description: Load model animations from file Param[1]: fileName (type: const char *) - Param[2]: animCount (type: unsigned int *) -Function 444: UpdateModelAnimation() (3 input parameters) + Param[2]: animCount (type: int *) +Function 453: UpdateModelAnimation() (3 input parameters) Name: UpdateModelAnimation Return type: void Description: Update model animation pose Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) Param[3]: frame (type: int) -Function 445: UnloadModelAnimation() (1 input parameters) +Function 454: UnloadModelAnimation() (1 input parameters) Name: UnloadModelAnimation Return type: void Description: Unload animation data Param[1]: anim (type: ModelAnimation) -Function 446: UnloadModelAnimations() (2 input parameters) +Function 455: UnloadModelAnimations() (2 input parameters) Name: UnloadModelAnimations Return type: void Description: Unload animation array data Param[1]: animations (type: ModelAnimation *) - Param[2]: count (type: unsigned int) -Function 447: IsModelAnimationValid() (2 input parameters) + Param[2]: animCount (type: int) +Function 456: IsModelAnimationValid() (2 input parameters) Name: IsModelAnimationValid Return type: bool Description: Check model animation skeleton match Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) -Function 448: CheckCollisionSpheres() (4 input parameters) +Function 457: CheckCollisionSpheres() (4 input parameters) Name: CheckCollisionSpheres Return type: bool Description: Check collision between two spheres @@ -3822,40 +3882,40 @@ Function 448: CheckCollisionSpheres() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector3) Param[4]: radius2 (type: float) -Function 449: CheckCollisionBoxes() (2 input parameters) +Function 458: CheckCollisionBoxes() (2 input parameters) Name: CheckCollisionBoxes Return type: bool Description: Check collision between two bounding boxes Param[1]: box1 (type: BoundingBox) Param[2]: box2 (type: BoundingBox) -Function 450: CheckCollisionBoxSphere() (3 input parameters) +Function 459: CheckCollisionBoxSphere() (3 input parameters) Name: CheckCollisionBoxSphere Return type: bool Description: Check collision between box and sphere Param[1]: box (type: BoundingBox) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 451: GetRayCollisionSphere() (3 input parameters) +Function 460: GetRayCollisionSphere() (3 input parameters) Name: GetRayCollisionSphere Return type: RayCollision Description: Get collision info between ray and sphere Param[1]: ray (type: Ray) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 452: GetRayCollisionBox() (2 input parameters) +Function 461: GetRayCollisionBox() (2 input parameters) Name: GetRayCollisionBox Return type: RayCollision Description: Get collision info between ray and box Param[1]: ray (type: Ray) Param[2]: box (type: BoundingBox) -Function 453: GetRayCollisionMesh() (3 input parameters) +Function 462: GetRayCollisionMesh() (3 input parameters) Name: GetRayCollisionMesh Return type: RayCollision Description: Get collision info between ray and mesh Param[1]: ray (type: Ray) Param[2]: mesh (type: Mesh) Param[3]: transform (type: Matrix) -Function 454: GetRayCollisionTriangle() (4 input parameters) +Function 463: GetRayCollisionTriangle() (4 input parameters) Name: GetRayCollisionTriangle Return type: RayCollision Description: Get collision info between ray and triangle @@ -3863,7 +3923,7 @@ Function 454: GetRayCollisionTriangle() (4 input parameters) Param[2]: p1 (type: Vector3) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) -Function 455: GetRayCollisionQuad() (5 input parameters) +Function 464: GetRayCollisionQuad() (5 input parameters) Name: GetRayCollisionQuad Return type: RayCollision Description: Get collision info between ray and quad @@ -3872,143 +3932,153 @@ Function 455: GetRayCollisionQuad() (5 input parameters) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) Param[5]: p4 (type: Vector3) -Function 456: InitAudioDevice() (0 input parameters) +Function 465: InitAudioDevice() (0 input parameters) Name: InitAudioDevice Return type: void Description: Initialize audio device and context No input parameters -Function 457: CloseAudioDevice() (0 input parameters) +Function 466: CloseAudioDevice() (0 input parameters) Name: CloseAudioDevice Return type: void Description: Close the audio device and context No input parameters -Function 458: IsAudioDeviceReady() (0 input parameters) +Function 467: IsAudioDeviceReady() (0 input parameters) Name: IsAudioDeviceReady Return type: bool Description: Check if audio device has been initialized successfully No input parameters -Function 459: SetMasterVolume() (1 input parameters) +Function 468: SetMasterVolume() (1 input parameters) Name: SetMasterVolume Return type: void Description: Set master volume (listener) Param[1]: volume (type: float) -Function 460: LoadWave() (1 input parameters) +Function 469: LoadWave() (1 input parameters) Name: LoadWave Return type: Wave Description: Load wave data from file Param[1]: fileName (type: const char *) -Function 461: LoadWaveFromMemory() (3 input parameters) +Function 470: LoadWaveFromMemory() (3 input parameters) Name: LoadWaveFromMemory Return type: Wave Description: Load wave from memory buffer, fileType refers to extension: i.e. '.wav' Param[1]: fileType (type: const char *) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 462: IsWaveReady() (1 input parameters) +Function 471: IsWaveReady() (1 input parameters) Name: IsWaveReady Return type: bool Description: Checks if wave data is ready Param[1]: wave (type: Wave) -Function 463: LoadSound() (1 input parameters) +Function 472: LoadSound() (1 input parameters) Name: LoadSound Return type: Sound Description: Load sound from file Param[1]: fileName (type: const char *) -Function 464: LoadSoundFromWave() (1 input parameters) +Function 473: LoadSoundFromWave() (1 input parameters) Name: LoadSoundFromWave Return type: Sound Description: Load sound from wave data Param[1]: wave (type: Wave) -Function 465: IsSoundReady() (1 input parameters) +Function 474: LoadSoundAlias() (1 input parameters) + Name: LoadSoundAlias + Return type: Sound + Description: Create a new sound that shares the same sample data as the source sound, does not own the sound data + Param[1]: source (type: Sound) +Function 475: IsSoundReady() (1 input parameters) Name: IsSoundReady Return type: bool Description: Checks if a sound is ready Param[1]: sound (type: Sound) -Function 466: UpdateSound() (3 input parameters) +Function 476: UpdateSound() (3 input parameters) Name: UpdateSound Return type: void Description: Update sound buffer with new data Param[1]: sound (type: Sound) Param[2]: data (type: const void *) Param[3]: sampleCount (type: int) -Function 467: UnloadWave() (1 input parameters) +Function 477: UnloadWave() (1 input parameters) Name: UnloadWave Return type: void Description: Unload wave data Param[1]: wave (type: Wave) -Function 468: UnloadSound() (1 input parameters) +Function 478: UnloadSound() (1 input parameters) Name: UnloadSound Return type: void Description: Unload sound Param[1]: sound (type: Sound) -Function 469: ExportWave() (2 input parameters) +Function 479: UnloadSoundAlias() (1 input parameters) + Name: UnloadSoundAlias + Return type: void + Description: Unload a sound alias (does not deallocate sample data) + Param[1]: alias (type: Sound) +Function 480: ExportWave() (2 input parameters) Name: ExportWave Return type: bool Description: Export wave data to file, returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 470: ExportWaveAsCode() (2 input parameters) +Function 481: ExportWaveAsCode() (2 input parameters) Name: ExportWaveAsCode Return type: bool Description: Export wave sample data to code (.h), returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 471: PlaySound() (1 input parameters) +Function 482: PlaySound() (1 input parameters) Name: PlaySound Return type: void Description: Play a sound Param[1]: sound (type: Sound) -Function 472: StopSound() (1 input parameters) +Function 483: StopSound() (1 input parameters) Name: StopSound Return type: void Description: Stop playing a sound Param[1]: sound (type: Sound) -Function 473: PauseSound() (1 input parameters) +Function 484: PauseSound() (1 input parameters) Name: PauseSound Return type: void Description: Pause a sound Param[1]: sound (type: Sound) -Function 474: ResumeSound() (1 input parameters) +Function 485: ResumeSound() (1 input parameters) Name: ResumeSound Return type: void Description: Resume a paused sound Param[1]: sound (type: Sound) -Function 475: IsSoundPlaying() (1 input parameters) +Function 486: IsSoundPlaying() (1 input parameters) Name: IsSoundPlaying Return type: bool Description: Check if a sound is currently playing Param[1]: sound (type: Sound) -Function 476: SetSoundVolume() (2 input parameters) +Function 487: SetSoundVolume() (2 input parameters) Name: SetSoundVolume Return type: void Description: Set volume for a sound (1.0 is max level) Param[1]: sound (type: Sound) Param[2]: volume (type: float) -Function 477: SetSoundPitch() (2 input parameters) +Function 488: SetSoundPitch() (2 input parameters) Name: SetSoundPitch Return type: void Description: Set pitch for a sound (1.0 is base level) Param[1]: sound (type: Sound) Param[2]: pitch (type: float) -Function 478: SetSoundPan() (2 input parameters) +Function 489: SetSoundPan() (2 input parameters) Name: SetSoundPan Return type: void Description: Set pan for a sound (0.5 is center) Param[1]: sound (type: Sound) Param[2]: pan (type: float) -Function 479: WaveCopy() (1 input parameters) +Function 490: WaveCopy() (1 input parameters) Name: WaveCopy Return type: Wave Description: Copy a wave to a new wave Param[1]: wave (type: Wave) -Function 480: WaveCrop() (3 input parameters) +Function 491: WaveCrop() (3 input parameters) Name: WaveCrop Return type: void Description: Crop a wave to defined samples range Param[1]: wave (type: Wave *) Param[2]: initSample (type: int) Param[3]: finalSample (type: int) -Function 481: WaveFormat() (4 input parameters) +Function 492: WaveFormat() (4 input parameters) Name: WaveFormat Return type: void Description: Convert wave data to desired format @@ -4016,203 +4086,203 @@ Function 481: WaveFormat() (4 input parameters) Param[2]: sampleRate (type: int) Param[3]: sampleSize (type: int) Param[4]: channels (type: int) -Function 482: LoadWaveSamples() (1 input parameters) +Function 493: LoadWaveSamples() (1 input parameters) Name: LoadWaveSamples Return type: float * Description: Load samples data from wave as a 32bit float data array Param[1]: wave (type: Wave) -Function 483: UnloadWaveSamples() (1 input parameters) +Function 494: UnloadWaveSamples() (1 input parameters) Name: UnloadWaveSamples Return type: void Description: Unload samples data loaded with LoadWaveSamples() Param[1]: samples (type: float *) -Function 484: LoadMusicStream() (1 input parameters) +Function 495: LoadMusicStream() (1 input parameters) Name: LoadMusicStream Return type: Music Description: Load music stream from file Param[1]: fileName (type: const char *) -Function 485: LoadMusicStreamFromMemory() (3 input parameters) +Function 496: LoadMusicStreamFromMemory() (3 input parameters) Name: LoadMusicStreamFromMemory Return type: Music Description: Load music stream from data Param[1]: fileType (type: const char *) Param[2]: data (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 486: IsMusicReady() (1 input parameters) +Function 497: IsMusicReady() (1 input parameters) Name: IsMusicReady Return type: bool Description: Checks if a music stream is ready Param[1]: music (type: Music) -Function 487: UnloadMusicStream() (1 input parameters) +Function 498: UnloadMusicStream() (1 input parameters) Name: UnloadMusicStream Return type: void Description: Unload music stream Param[1]: music (type: Music) -Function 488: PlayMusicStream() (1 input parameters) +Function 499: PlayMusicStream() (1 input parameters) Name: PlayMusicStream Return type: void Description: Start music playing Param[1]: music (type: Music) -Function 489: IsMusicStreamPlaying() (1 input parameters) +Function 500: IsMusicStreamPlaying() (1 input parameters) Name: IsMusicStreamPlaying Return type: bool Description: Check if music is playing Param[1]: music (type: Music) -Function 490: UpdateMusicStream() (1 input parameters) +Function 501: UpdateMusicStream() (1 input parameters) Name: UpdateMusicStream Return type: void Description: Updates buffers for music streaming Param[1]: music (type: Music) -Function 491: StopMusicStream() (1 input parameters) +Function 502: StopMusicStream() (1 input parameters) Name: StopMusicStream Return type: void Description: Stop music playing Param[1]: music (type: Music) -Function 492: PauseMusicStream() (1 input parameters) +Function 503: PauseMusicStream() (1 input parameters) Name: PauseMusicStream Return type: void Description: Pause music playing Param[1]: music (type: Music) -Function 493: ResumeMusicStream() (1 input parameters) +Function 504: ResumeMusicStream() (1 input parameters) Name: ResumeMusicStream Return type: void Description: Resume playing paused music Param[1]: music (type: Music) -Function 494: SeekMusicStream() (2 input parameters) +Function 505: SeekMusicStream() (2 input parameters) Name: SeekMusicStream Return type: void Description: Seek music to a position (in seconds) Param[1]: music (type: Music) Param[2]: position (type: float) -Function 495: SetMusicVolume() (2 input parameters) +Function 506: SetMusicVolume() (2 input parameters) Name: SetMusicVolume Return type: void Description: Set volume for music (1.0 is max level) Param[1]: music (type: Music) Param[2]: volume (type: float) -Function 496: SetMusicPitch() (2 input parameters) +Function 507: SetMusicPitch() (2 input parameters) Name: SetMusicPitch Return type: void Description: Set pitch for a music (1.0 is base level) Param[1]: music (type: Music) Param[2]: pitch (type: float) -Function 497: SetMusicPan() (2 input parameters) +Function 508: SetMusicPan() (2 input parameters) Name: SetMusicPan Return type: void Description: Set pan for a music (0.5 is center) Param[1]: music (type: Music) Param[2]: pan (type: float) -Function 498: GetMusicTimeLength() (1 input parameters) +Function 509: GetMusicTimeLength() (1 input parameters) Name: GetMusicTimeLength Return type: float Description: Get music time length (in seconds) Param[1]: music (type: Music) -Function 499: GetMusicTimePlayed() (1 input parameters) +Function 510: GetMusicTimePlayed() (1 input parameters) Name: GetMusicTimePlayed Return type: float Description: Get current music time played (in seconds) Param[1]: music (type: Music) -Function 500: LoadAudioStream() (3 input parameters) +Function 511: LoadAudioStream() (3 input parameters) Name: LoadAudioStream Return type: AudioStream Description: Load audio stream (to stream raw audio pcm data) Param[1]: sampleRate (type: unsigned int) Param[2]: sampleSize (type: unsigned int) Param[3]: channels (type: unsigned int) -Function 501: IsAudioStreamReady() (1 input parameters) +Function 512: IsAudioStreamReady() (1 input parameters) Name: IsAudioStreamReady Return type: bool Description: Checks if an audio stream is ready Param[1]: stream (type: AudioStream) -Function 502: UnloadAudioStream() (1 input parameters) +Function 513: UnloadAudioStream() (1 input parameters) Name: UnloadAudioStream Return type: void Description: Unload audio stream and free memory Param[1]: stream (type: AudioStream) -Function 503: UpdateAudioStream() (3 input parameters) +Function 514: UpdateAudioStream() (3 input parameters) Name: UpdateAudioStream Return type: void Description: Update audio stream buffers with data Param[1]: stream (type: AudioStream) Param[2]: data (type: const void *) Param[3]: frameCount (type: int) -Function 504: IsAudioStreamProcessed() (1 input parameters) +Function 515: IsAudioStreamProcessed() (1 input parameters) Name: IsAudioStreamProcessed Return type: bool Description: Check if any audio stream buffers requires refill Param[1]: stream (type: AudioStream) -Function 505: PlayAudioStream() (1 input parameters) +Function 516: PlayAudioStream() (1 input parameters) Name: PlayAudioStream Return type: void Description: Play audio stream Param[1]: stream (type: AudioStream) -Function 506: PauseAudioStream() (1 input parameters) +Function 517: PauseAudioStream() (1 input parameters) Name: PauseAudioStream Return type: void Description: Pause audio stream Param[1]: stream (type: AudioStream) -Function 507: ResumeAudioStream() (1 input parameters) +Function 518: ResumeAudioStream() (1 input parameters) Name: ResumeAudioStream Return type: void Description: Resume audio stream Param[1]: stream (type: AudioStream) -Function 508: IsAudioStreamPlaying() (1 input parameters) +Function 519: IsAudioStreamPlaying() (1 input parameters) Name: IsAudioStreamPlaying Return type: bool Description: Check if audio stream is playing Param[1]: stream (type: AudioStream) -Function 509: StopAudioStream() (1 input parameters) +Function 520: StopAudioStream() (1 input parameters) Name: StopAudioStream Return type: void Description: Stop audio stream Param[1]: stream (type: AudioStream) -Function 510: SetAudioStreamVolume() (2 input parameters) +Function 521: SetAudioStreamVolume() (2 input parameters) Name: SetAudioStreamVolume Return type: void Description: Set volume for audio stream (1.0 is max level) Param[1]: stream (type: AudioStream) Param[2]: volume (type: float) -Function 511: SetAudioStreamPitch() (2 input parameters) +Function 522: SetAudioStreamPitch() (2 input parameters) Name: SetAudioStreamPitch Return type: void Description: Set pitch for audio stream (1.0 is base level) Param[1]: stream (type: AudioStream) Param[2]: pitch (type: float) -Function 512: SetAudioStreamPan() (2 input parameters) +Function 523: SetAudioStreamPan() (2 input parameters) Name: SetAudioStreamPan Return type: void Description: Set pan for audio stream (0.5 is centered) Param[1]: stream (type: AudioStream) Param[2]: pan (type: float) -Function 513: SetAudioStreamBufferSizeDefault() (1 input parameters) +Function 524: SetAudioStreamBufferSizeDefault() (1 input parameters) Name: SetAudioStreamBufferSizeDefault Return type: void Description: Default size for new audio streams Param[1]: size (type: int) -Function 514: SetAudioStreamCallback() (2 input parameters) +Function 525: SetAudioStreamCallback() (2 input parameters) Name: SetAudioStreamCallback Return type: void Description: Audio thread callback to request new data Param[1]: stream (type: AudioStream) Param[2]: callback (type: AudioCallback) -Function 515: AttachAudioStreamProcessor() (2 input parameters) +Function 526: AttachAudioStreamProcessor() (2 input parameters) Name: AttachAudioStreamProcessor Return type: void - Description: Attach audio stream processor to stream + Description: Attach audio stream processor to stream, receives the samples as s Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 516: DetachAudioStreamProcessor() (2 input parameters) +Function 527: DetachAudioStreamProcessor() (2 input parameters) Name: DetachAudioStreamProcessor Return type: void Description: Detach audio stream processor from stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 517: AttachAudioMixedProcessor() (1 input parameters) +Function 528: AttachAudioMixedProcessor() (1 input parameters) Name: AttachAudioMixedProcessor Return type: void - Description: Attach audio stream processor to the entire audio pipeline + Description: Attach audio stream processor to the entire audio pipeline, receives the samples as s Param[1]: processor (type: AudioCallback) -Function 518: DetachAudioMixedProcessor() (1 input parameters) +Function 529: DetachAudioMixedProcessor() (1 input parameters) Name: DetachAudioMixedProcessor Return type: void Description: Detach audio stream processor from the entire audio pipeline diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index f450069cc..aa7605b8b 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -292,7 +292,7 @@ - + @@ -306,6 +306,7 @@ + @@ -539,7 +540,7 @@ - + @@ -550,17 +551,20 @@ - - - - - - - - - - - + + + + + + + + + + + + + + @@ -637,12 +641,12 @@ - + - + @@ -656,7 +660,7 @@ - + @@ -691,6 +695,8 @@ + + @@ -704,20 +710,24 @@ - + - + + + + + @@ -725,6 +735,8 @@ + + @@ -761,7 +773,7 @@ - + @@ -976,7 +988,7 @@ - + @@ -984,11 +996,11 @@ - + - + @@ -1031,7 +1043,7 @@ - + @@ -1082,6 +1094,9 @@ + + + @@ -1187,7 +1202,7 @@ - + @@ -1263,6 +1278,18 @@ + + + + + + + + + + + + @@ -1516,6 +1543,11 @@ + + + + + @@ -1540,6 +1572,11 @@ + + + + + @@ -1685,7 +1722,7 @@ - + @@ -1995,11 +2032,11 @@ - + - - + + @@ -2011,8 +2048,8 @@ - - + + @@ -2021,20 +2058,20 @@ - - + + - - + + - + @@ -2083,12 +2120,15 @@ - + + + + @@ -2523,7 +2563,7 @@ - + @@ -2535,7 +2575,7 @@ - + @@ -2609,6 +2649,9 @@ + + + @@ -2623,6 +2666,9 @@ + + + @@ -2785,7 +2831,7 @@ - + @@ -2793,7 +2839,7 @@ - + From be8eea9edadea82395b0ebec1c8afddbed129641 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 8 Oct 2023 18:10:05 +0200 Subject: [PATCH 0312/1350] Format tweaks --- parser/raylib_parser.c | 4 ++-- src/config.h | 4 +--- src/raylib.h | 2 +- src/rlgl.h | 2 +- src/rmodels.c | 8 ++++---- 5 files changed, 9 insertions(+), 11 deletions(-) diff --git a/parser/raylib_parser.c b/parser/raylib_parser.c index e1ff37918..678b64d65 100644 --- a/parser/raylib_parser.c +++ b/parser/raylib_parser.c @@ -447,11 +447,11 @@ int main(int argc, char* argv[]) { if (isFloat) { - defines[defineIndex].type = linePtr[j-1] == 'f' ? FLOAT : DOUBLE; + defines[defineIndex].type = (linePtr[j-1] == 'f')? FLOAT : DOUBLE; } else { - defines[defineIndex].type = linePtr[j-1] == 'L' ? LONG : INT; + defines[defineIndex].type = (linePtr[j-1] == 'L')? LONG : INT; defines[defineIndex].isHex = isHex; } } diff --git a/src/config.h b/src/config.h index 370837610..7886aeeba 100644 --- a/src/config.h +++ b/src/config.h @@ -55,9 +55,7 @@ // Use busy wait loop for timing sync, if not defined, a high-resolution timer is set up and used //#define SUPPORT_BUSY_WAIT_LOOP 1 // Use a partial-busy wait loop, in this case frame sleeps for most of the time, but then runs a busy loop at the end for accuracy -#define SUPPORT_PARTIALBUSY_WAIT_LOOP -// Wait for events passively (sleeping while no events) instead of polling them actively every frame -//#define SUPPORT_EVENTS_WAITING 1 +#define SUPPORT_PARTIALBUSY_WAIT_LOOP 1 // Allow automatic screen capture of current screen pressing F12, defined in KeyCallback() #define SUPPORT_SCREEN_CAPTURE 1 // Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback() diff --git a/src/raylib.h b/src/raylib.h index f52f3460d..5fa66bd03 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -938,8 +938,8 @@ extern "C" { // Prevents name mangling of functions // Window-related functions RLAPI void InitWindow(int width, int height, const char *title); // Initialize window and OpenGL context -RLAPI bool WindowShouldClose(void); // Check if KEY_ESCAPE pressed or Close icon pressed RLAPI void CloseWindow(void); // Close window and unload OpenGL context +RLAPI bool WindowShouldClose(void); // Check if application should close (KEY_ESCAPE pressed or windows close icon clicked) RLAPI bool IsWindowReady(void); // Check if window has been initialized successfully RLAPI bool IsWindowFullscreen(void); // Check if window is currently fullscreen RLAPI bool IsWindowHidden(void); // Check if window is currently hidden (only PLATFORM_DESKTOP) diff --git a/src/rlgl.h b/src/rlgl.h index 7a0d7f862..40e7b4785 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -4248,7 +4248,7 @@ void rlBindImageTexture(unsigned int id, unsigned int index, int format, bool re unsigned int glInternalFormat = 0, glFormat = 0, glType = 0; rlGetGlTextureFormats(format, &glInternalFormat, &glFormat, &glType); - glBindImageTexture(index, id, 0, 0, 0, readonly ? GL_READ_ONLY : GL_READ_WRITE, glInternalFormat); + glBindImageTexture(index, id, 0, 0, 0, readonly? GL_READ_ONLY : GL_READ_WRITE, glInternalFormat); #endif } diff --git a/src/rmodels.c b/src/rmodels.c index 27fe4d85f..490eac555 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1202,7 +1202,7 @@ void UploadMesh(Mesh *mesh, bool dynamic) // NOTE: Vertex attributes must be uploaded considering default locations points and available vertex data // Enable vertex attributes: position (shader-location = 0) - void *vertices = mesh->animVertices != NULL ? mesh->animVertices : mesh->vertices; + void *vertices = (mesh->animVertices != NULL)? mesh->animVertices : mesh->vertices; mesh->vboId[0] = rlLoadVertexBuffer(vertices, mesh->vertexCount*3*sizeof(float), dynamic); rlSetVertexAttribute(0, 3, RL_FLOAT, 0, 0, 0); rlEnableVertexAttribute(0); @@ -1218,7 +1218,7 @@ void UploadMesh(Mesh *mesh, bool dynamic) if (mesh->normals != NULL) { // Enable vertex attributes: normals (shader-location = 2) - void *normals = mesh->animNormals != NULL ? mesh->animNormals : mesh->normals; + void *normals = (mesh->animNormals != NULL)? mesh->animNormals : mesh->normals; mesh->vboId[2] = rlLoadVertexBuffer(normals, mesh->vertexCount*3*sizeof(float), dynamic); rlSetVertexAttribute(2, 3, RL_FLOAT, 0, 0, 0); rlEnableVertexAttribute(2); @@ -5585,7 +5585,7 @@ static Model LoadM3D(const char *fileName) if (!m3d || M3D_ERR_ISFATAL(m3d->errcode)) { - TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load M3D data, error code %d", fileName, m3d ? m3d->errcode : -2); + TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load M3D data, error code %d", fileName, m3d? m3d->errcode : -2); if (m3d) m3d_free(m3d); UnloadFileData(fileData); return model; @@ -5910,7 +5910,7 @@ static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCou if (!m3d || M3D_ERR_ISFATAL(m3d->errcode)) { - TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load M3D data, error code %d", fileName, m3d ? m3d->errcode : -2); + TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load M3D data, error code %d", fileName, m3d? m3d->errcode : -2); UnloadFileData(fileData); return NULL; } From 1327b570e3aac6111ebdb3b233b9c60cdc12ee9f Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 8 Oct 2023 18:11:55 +0200 Subject: [PATCH 0313/1350] Update raylib_parser.c --- parser/raylib_parser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parser/raylib_parser.c b/parser/raylib_parser.c index 678b64d65..bdec4915a 100644 --- a/parser/raylib_parser.c +++ b/parser/raylib_parser.c @@ -534,8 +534,8 @@ int main(int argc, char* argv[]) { // Found a valid number -> update largestType int numberType; - if (isFloat) numberType = valuePtr[c - 1] == 'f' ? FLOAT_MATH : DOUBLE_MATH; - else numberType = valuePtr[c - 1] == 'L' ? LONG_MATH : INT_MATH; + if (isFloat) numberType = (valuePtr[c - 1] == 'f')? FLOAT_MATH : DOUBLE_MATH; + else numberType = (valuePtr[c - 1] == 'L')? LONG_MATH : INT_MATH; if (numberType > largestType) largestType = numberType; } From fecf56e15aaba160aefe14b241de19262a20ab3e Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 8 Oct 2023 18:36:07 +0200 Subject: [PATCH 0314/1350] WARNING: `rcore` module split per-platform **BIG CHANGE** (#3388) * Submodules (#3311) * Check in current state * Add submodules to Makefile and clean up some imports * Start moving InitGraphicsDeivce * Move android_main and CloseWindow() out of rcore * Move WindowShouldClose out of rcore * Move IsWindowHidden out of rcore * Move IsWindowMinimized out of rcore * Move IsWindowMaximized, IsWindowFocused and IsWindowResized out of rcore * Move ToggleFullscreen out of rcore * Move MaximizeWindow, MinimizeWindow and RestoreWindow out of rcore * Move 13 functions out of rcore: ToggleBorderlessWindowed SetWindowState ClearWindowState SetWindowIcon SetWindowIcons SetWindowTitle SetWindowPosition SetWindowMonitor SetWindowMinSize SetWindowMaxSize SetWindowSize SetWindowOpacity SetWindowFocused * Minor clean up, revert makefile change, include submodules directly in rcore * Fix makefile comment * Remove rcore.h from Makefile * Remove debug include * Move 18 functions from rcore to submodules GetWindowHandle GetMonitorCount GetCurrentMonitor GetMonitorPosition GetMonitorWidth GetMonitorHeight GetMonitorPhysicalHeight GetMonitorRefreshRate GetWindowPosition GetWindowScaleDPI GetMonitorName SetClipboardText GetClipboardText ShowCursor HideCursor EnableCursor DisableCursor GetTime * Move TakeScreenshot, OpenURL, GetGamepadName out of rcore into submodules * remove debugging #defines * Move GetMonitorPhysicalWidth from rcore to submodule * Move GetGamepadAxisCount from rcore * Move SetGamepadMappings out of rcore * Move GetMouseX, GetMouseY, GetMousePosition out of rcore * Move SetMousePosition out of rcore * Move GetMouseWheelMove out of rcore * Move the last functions out of rcore * Move shared function defs and some global var to rcore.h * Clean up rcore.c and rcore.h a little more * Remove unnecessary #define --------- Co-authored-by: MichaelFiber * REVIEWED: `PLATFORM_DESKTOP` Windows building * Revert "REVIEWED: `PLATFORM_DESKTOP` Windows building" This reverts commit 71a12171f768eb25053ef908732b4ce8fdf802f7. * Reviewed Windows building * [split] Fix compilation for web (and desktop) (#3329) * Fix compilation for web * Remove EM_ASM_INT from core_input_gestures_web example * Fix raymath undefined symbols for desktop and web * Remove raylib_opengl_interop from examples Makefile * Revert previous commit (8651c78) * Fix TraceLog for web and desktop * [split] `rcore`, `rcore_web` and `rcore_desktop` changes (batch 2) (#3334) * Fix formatting * Reapply commit 9d230d7 (#3305) that was missing * Reapplies commits 719365f (#3309) and 8a1779b (#3312) that were missing * Reapply commit 5c9cc3f (#3323) that was missing * Reapply commit a2b3b1e that was missing * Revert commit cef25c6 to fix macro redefined warning * Move rcore.h #include to after config.h to fix macro redefinitions warnings * [split] `rcore`, `web`, `desktop`, `android` changes (batch 3) (#3338) * First pass to remove unneeded platform macros for web * Second pass to remove unneeded platform macros for web * Move GetTouchX, GetTouchY, GetTouchPosition from rcore to web, desktop, android * Move SetMouseCursor from rcore to android, desktop, web * [split] `rcore`, `web`, `desktop`, `android` changes (batch 4) (#3343) * Fix ToggleBorderlessWindowed duplicated glfwSetWindowSize calls * First pass to remove unneeded platform macros for android * Second pass to remove unneeded platform macros for android * Remove unneeded platform macros for desktop * Relocate GetGamepadName and update SetGamepadMappings on android, desktop, web * Add missing comment to web * [split] `rcore`, `web`, `desktop`, `android` changes (batch 5) (#3345) * Move SetExitKey from core to android, desktop, web * Move some callbacks from core to desktop and web * Relocate emscripten callbacks on web * Relocate android callbacks on android * Revert "Relocate android callbacks on android" This reverts commit bbdbecc01ea7f871dae56019724386e73611c69c. * Updates UnloadVrStereoConfig on rcore * Update SetClipboardText on android * Fix screenMin/Max default values for android * [split] `rcore`, `drm` changes (#3347) * Tweak makefiles for PLATFORM_DRM and move rcore_drm's dependencies to rcore.h * Move drm functions to rcore_drm.c * Fix a typo in rcore.c * Add SetExitKey to rcore_drm.c --------- Co-authored-by: MichaelFiber * Fix compilation for android (#3360) * Fix android include (#3364) * Reviewed platform split #3313 - Added file headers info - Added TRACELOG message for unimplemented functions - Reviewed code formatting and organization - Several code tweaks * REVIEWED: `GetDirectoryPath()` --------- Co-authored-by: MichaelFiber <42419558+michaelfiber@users.noreply.github.com> Co-authored-by: MichaelFiber Co-authored-by: ubkp <118854183+ubkp@users.noreply.github.com> --- .gitignore | 3 + examples/Makefile | 1 + examples/core/core_input_gestures_web.c | 26 +- src/Makefile | 1 + src/rcore.c | 5041 +---------------------- src/rcore.h | 318 ++ src/rcore_android.c | 1267 ++++++ src/rcore_desktop.c | 2074 ++++++++++ src/rcore_drm.c | 2059 +++++++++ src/rcore_web.c | 1604 ++++++++ 10 files changed, 7485 insertions(+), 4909 deletions(-) create mode 100644 src/rcore.h create mode 100644 src/rcore_android.c create mode 100644 src/rcore_desktop.c create mode 100644 src/rcore_drm.c create mode 100644 src/rcore_web.c diff --git a/.gitignore b/.gitignore index ec9325081..fb4865ba3 100644 --- a/.gitignore +++ b/.gitignore @@ -75,6 +75,9 @@ xcschememanagement.plist xcuserdata/ DerivedData/ +# VSCode project +.vscode + # Jetbrains project .idea/ cmake-build-*/ diff --git a/examples/Makefile b/examples/Makefile index cd079c312..9404d39cc 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -225,6 +225,7 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) endif endif ifeq ($(PLATFORM),PLATFORM_DRM) + INCLUDE_PATHS += -I$(RAYLIB_INCLUDE_PATH) INCLUDE_PATHS += -I/usr/include/libdrm endif diff --git a/examples/core/core_input_gestures_web.c b/examples/core/core_input_gestures_web.c index 0129a983b..e1492244c 100644 --- a/examples/core/core_input_gestures_web.c +++ b/examples/core/core_input_gestures_web.c @@ -135,7 +135,7 @@ void Update(void) } } } - + int fillLog = 0; // Gate variable to be used to allow or not the gesture log to be filled if (currentGesture !=0) { @@ -156,16 +156,16 @@ void Update(void) fillLog = 1; } } - + if (fillLog) // If one of the conditions from logMode was met, fill the gesture log { previousGesture = currentGesture; gestureColor = GetGestureColor(currentGesture); if (gestureLogIndex <= 0) gestureLogIndex = GESTURE_LOG_SIZE; gestureLogIndex--; - + // Copy the gesture respective name to the gesture log array - TextCopy(gestureLog[gestureLogIndex], GetGestureName(currentGesture)); + TextCopy(gestureLog[gestureLogIndex], GetGestureName(currentGesture)); } // Handle protractor @@ -182,14 +182,14 @@ void Update(void) { currentAngleDegrees = 0.0f; } - + float currentAngleRadians = ((currentAngleDegrees +90.0f)*PI/180); // Convert the current angle to Radians finalVector = (Vector2){ (angleLength*sinf(currentAngleRadians)) + protractorPosition.x, (angleLength*cosf(currentAngleRadians)) + protractorPosition.y }; // Calculate the final vector for display // Handle touch and mouse pointer points //-------------------------------------------------------------------------------------- #define MAX_TOUCH_COUNT 32 - + Vector2 touchPosition[MAX_TOUCH_COUNT] = { 0 }; Vector2 mousePosition = {0, 0}; if (currentGesture != GESTURE_NONE) @@ -204,7 +204,7 @@ void Update(void) // Draw //-------------------------------------------------------------------------------------- BeginDrawing(); - + ClearBackground(RAYWHITE); // Draw common @@ -235,7 +235,7 @@ void Update(void) // Draw gesture log //-------------------------------------------------------------------------------------- DrawText("Log", gestureLogPosition.x, gestureLogPosition.y, 20, BLACK); - + // Loop in both directions to print the gesture log array in the inverted order (and looping around if the index started somewhere in the middle) for (i = 0, ii = gestureLogIndex; i < GESTURE_LOG_SIZE; i++, ii = (ii + 1) % GESTURE_LOG_SIZE) DrawText(gestureLog[ii], gestureLogPosition.x, gestureLogPosition.y + 410 - i*20, 20, (i == 0 ? gestureColor : LIGHTGRAY)); Color logButton1Color, logButton2Color; @@ -286,7 +286,7 @@ void Update(void) DrawCircleV(touchPosition[i], 50.0f, Fade(gestureColor, 0.5f)); DrawCircleV(touchPosition[i], 5.0f, gestureColor); } - + if (touchCount == 2) DrawLineEx(touchPosition[0], touchPosition[1], ((currentGesture == 512)? 8 : 12), gestureColor); } else @@ -308,14 +308,6 @@ int main(void) { // Initialization //-------------------------------------------------------------------------------------- - #if defined( PLATFORM_WEB ) - // Using Emscripten EM_ASM_INT macro, get the page canvas width - const int canvasWidth = EM_ASM_INT( return document.getElementById('canvas').getBoundingClientRect().width; ); - - if (canvasWidth > 400) screenWidth = canvasWidth; - else screenWidth = 400; // Set a minimum width for the screen - #endif - InitWindow(screenWidth, screenHeight, "raylib [core] example - input gestures web"); //-------------------------------------------------------------------------------------- diff --git a/src/Makefile b/src/Makefile index 63cbe2a41..e75ae5368 100644 --- a/src/Makefile +++ b/src/Makefile @@ -384,6 +384,7 @@ ifeq ($(PLATFORM),PLATFORM_DRM) # without EGL_NO_X11 eglplatform.h tears Xlib.h in which tears X.h in # which contains a conflicting type Font CFLAGS += -DEGL_NO_X11 + CFLAGS += -Werror=implicit-function-declaration endif # Use Wayland display on Linux desktop ifeq ($(PLATFORM),PLATFORM_DESKTOP) diff --git a/src/rcore.c b/src/rcore.c index 3ba0404d7..44b7ee787 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -53,9 +53,6 @@ * #define SUPPORT_PARTIALBUSY_WAIT_LOOP * Use a partial-busy wait loop, in this case frame sleeps for most of the time and runs a busy-wait-loop at the end * -* #define SUPPORT_EVENTS_WAITING -* Wait for events passively (sleeping while no events) instead of polling them actively every frame -* * #define SUPPORT_SCREEN_CAPTURE * Allow automatic screen capture of current screen pressing F12, defined in KeyCallback() * @@ -105,12 +102,11 @@ #include "config.h" // Defines module configuration flags #endif -#include "utils.h" // Required for: TRACELOG() macros +#include "rcore.h" #define RLGL_IMPLEMENTATION #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 -#define RAYMATH_IMPLEMENTATION // Define external out-of-line implementation #include "raymath.h" // Vector3, Quaternion and Matrix functionality #if defined(SUPPORT_GESTURES_SYSTEM) @@ -141,10 +137,6 @@ #include "external/sdefl.h" // Deflate (RFC 1951) compressor #endif -#if (defined(__linux__) || defined(PLATFORM_WEB)) && (_POSIX_C_SOURCE < 199309L) - #undef _POSIX_C_SOURCE - #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext. -#endif #if defined(__linux__) && !defined(_GNU_SOURCE) #define _GNU_SOURCE #endif @@ -166,12 +158,6 @@ #endif // OSs #endif // PLATFORM_DESKTOP -#include // Required for: srand(), rand(), atexit() -#include // Required for: sprintf() [Used in OpenURL()] -#include // Required for: strrchr(), strcmp(), strlen(), memset() -#include // Required for: time() [Used in InitTimer()] -#include // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()] - #define _CRT_INTERNAL_NONSTDC_NAMES 1 #include // Required for: stat(), S_ISREG [Used in GetFileModTime(), IsFilePath()] @@ -199,318 +185,21 @@ #define CHDIR chdir #endif -#if defined(PLATFORM_DESKTOP) - #define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 - // NOTE: Already provided by rlgl implementation (on glad.h) - #include "GLFW/glfw3.h" // GLFW3 library: Windows, OpenGL context and Input management - // NOTE: GLFW3 already includes gl.h (OpenGL) headers - - // Support retrieving native window handlers - #if defined(_WIN32) - typedef void *PVOID; - typedef PVOID HANDLE; - typedef HANDLE HWND; - #define GLFW_EXPOSE_NATIVE_WIN32 - #define GLFW_NATIVE_INCLUDE_NONE // To avoid some symbols re-definition in windows.h - #include "GLFW/glfw3native.h" - - #if defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - // NOTE: Those functions require linking with winmm library - unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod); - unsigned int __stdcall timeEndPeriod(unsigned int uPeriod); - #endif - #endif - #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) - #include // Required for: timespec, nanosleep(), select() - POSIX - - //#define GLFW_EXPOSE_NATIVE_X11 // WARNING: Exposing Xlib.h > X.h results in dup symbols for Font type - //#define GLFW_EXPOSE_NATIVE_WAYLAND - //#define GLFW_EXPOSE_NATIVE_MIR - #include "GLFW/glfw3native.h" // Required for: glfwGetX11Window() - #endif - #if defined(__APPLE__) - #include // Required for: usleep() - - //#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition - void *glfwGetCocoaWindow(GLFWwindow* handle); - #include "GLFW/glfw3native.h" // Required for: glfwGetCocoaWindow() - #endif - - // TODO: HACK: Added flag if not provided by GLFW when using external library - // Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev - #if !defined(GLFW_MOUSE_PASSTHROUGH) - #define GLFW_MOUSE_PASSTHROUGH 0x0002000D - #endif -#endif - -#if defined(PLATFORM_ANDROID) - //#include // Required for: Android sensors functions (accelerometer, gyroscope, light...) - #include // Required for: AWINDOW_FLAG_FULLSCREEN definition and others - #include // Required for: android_app struct and activity management - #include // Required for: JNIEnv and JavaVM [Used in OpenURL()] - - #include // Native platform windowing system interface - //#include // OpenGL ES 2.0 library (not required in this module, only in rlgl) -#endif - -#if defined(PLATFORM_DRM) - #include // POSIX file control definitions - open(), creat(), fcntl() - #include // POSIX standard function definitions - read(), close(), STDIN_FILENO - #include // POSIX terminal control definitions - tcgetattr(), tcsetattr() - #include // POSIX threads management (inputs reading) - #include // POSIX directory browsing - - #include // Required for: ioctl() - UNIX System call for device-specific input/output operations - #include // Linux: KDSKBMODE, K_MEDIUMRAM constants definition - #include // Linux: Keycodes constants definition (KEY_A, ...) - #include // Linux: Joystick support library - - #include // Generic Buffer Management (native platform for EGL on DRM) - #include // Direct Rendering Manager user-level library interface - #include // Direct Rendering Manager mode setting (KMS) interface - - #include "EGL/egl.h" // Native platform windowing system interface - #include "EGL/eglext.h" // EGL extensions - //#include "GLES2/gl2.h" // OpenGL ES 2.0 library (not required in this module, only in rlgl) -#endif - -#if defined(PLATFORM_WEB) - #define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) - //#define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?) - #include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management - #include // Required for: timespec, nanosleep(), select() - POSIX - - #include // Emscripten functionality for C - #include // Emscripten HTML5 library -#endif - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#if defined(PLATFORM_DRM) - #define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event number - - #define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...) - #define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events -#endif - -#ifndef MAX_FILEPATH_CAPACITY - #define MAX_FILEPATH_CAPACITY 8192 // Maximum capacity for filepath -#endif -#ifndef MAX_FILEPATH_LENGTH - #define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value) -#endif - -#ifndef MAX_KEYBOARD_KEYS - #define MAX_KEYBOARD_KEYS 512 // Maximum number of keyboard keys supported -#endif -#ifndef MAX_MOUSE_BUTTONS - #define MAX_MOUSE_BUTTONS 8 // Maximum number of mouse buttons supported -#endif -#ifndef MAX_GAMEPADS - #define MAX_GAMEPADS 4 // Maximum number of gamepads supported -#endif -#ifndef MAX_GAMEPAD_AXIS - #define MAX_GAMEPAD_AXIS 8 // Maximum number of axis supported (per gamepad) -#endif -#ifndef MAX_GAMEPAD_BUTTONS - #define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad) -#endif -#ifndef MAX_TOUCH_POINTS - #define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported -#endif -#ifndef MAX_KEY_PRESSED_QUEUE - #define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue -#endif -#ifndef MAX_CHAR_PRESSED_QUEUE - #define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue -#endif - -#ifndef MAX_DECOMPRESSION_SIZE - #define MAX_DECOMPRESSION_SIZE 64 // Maximum size allocated for decompression in MB -#endif - -// Flags operation macros -#define FLAG_SET(n, f) ((n) |= (f)) -#define FLAG_CLEAR(n, f) ((n) &= ~(f)) -#define FLAG_TOGGLE(n, f) ((n) ^= (f)) -#define FLAG_CHECK(n, f) ((n) & (f)) - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -#if defined(PLATFORM_DRM) -typedef struct { - pthread_t threadId; // Event reading thread id - int fd; // File descriptor to the device it is assigned to - int eventNum; // Number of 'event' device - Rectangle absRange; // Range of values for absolute pointing devices (touchscreens) - int touchSlot; // Hold the touch slot number of the currently being sent multitouch block - bool isMouse; // True if device supports relative X Y movements - bool isTouch; // True if device supports absolute X Y movements and has BTN_TOUCH - bool isMultitouch; // True if device supports multiple absolute movevents and has BTN_TOUCH - bool isKeyboard; // True if device has letter keycodes - bool isGamepad; // True if device has gamepad buttons -} InputEventWorker; -#endif - -typedef struct { int x; int y; } Point; -typedef struct { unsigned int width; unsigned int height; } Size; - -// Core global state context data -typedef struct CoreData { - struct { -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - GLFWwindow *handle; // GLFW window handle (graphic device) -#endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) -#if defined(PLATFORM_DRM) - int fd; // File descriptor for /dev/dri/... - drmModeConnector *connector; // Direct Rendering Manager (DRM) mode connector - drmModeCrtc *crtc; // CRT Controller - int modeIndex; // Index of the used mode of connector->modes - struct gbm_device *gbmDevice; // GBM device - struct gbm_surface *gbmSurface; // GBM surface - struct gbm_bo *prevBO; // Previous GBM buffer object (during frame swapping) - uint32_t prevFB; // Previous GBM framebufer (during frame swapping) -#endif // PLATFORM_DRM - EGLDisplay device; // Native display device (physical screen connection) - EGLSurface surface; // Surface to draw on, framebuffers (connected to context) - EGLContext context; // Graphic context, mode in which drawing can be done - EGLConfig config; // Graphic config -#endif - const char *title; // Window text title const pointer - unsigned int flags; // Configuration flags (bit based), keeps window state - bool ready; // Check if window has been initialized successfully - bool fullscreen; // Check if fullscreen mode is enabled - bool shouldClose; // Check if window set for closing - bool resizedLastFrame; // Check if window has been resized last frame - bool eventWaiting; // Wait for events before ending frame - - Point position; // Window position (required on fullscreen toggle) - Point previousPosition; // Window previous position (required on borderless windowed toggle) - Size display; // Display width and height (monitor, device-screen, LCD, ...) - Size screen; // Screen width and height (used render area) - Size previousScreen; // Screen previous width and height (required on borderless windowed toggle) - Size currentFbo; // Current render width and height (depends on active fbo) - Size render; // Framebuffer width and height (render area, including black bars if required) - Point renderOffset; // Offset from render area (must be divided by 2) - Size screenMin; // Screen minimum width and height (for resizable window) - Size screenMax; // Screen maximum width and height (for resizable window) - Matrix screenScale; // Matrix to scale screen (framebuffer rendering) - - char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) - unsigned int dropFileCount; // Count dropped files strings - - } Window; -#if defined(PLATFORM_ANDROID) - struct { - bool appEnabled; // Flag to detect if app is active ** = true - struct android_app *app; // Android activity - struct android_poll_source *source; // Android events polling source - bool contextRebindRequired; // Used to know context rebind required - } Android; -#endif - struct { - const char *basePath; // Base path for data storage - } Storage; - struct { -#if defined(PLATFORM_DRM) - InputEventWorker eventWorker[10]; // List of worker threads for every monitored "/dev/input/event" -#endif - struct { - int exitKey; // Default exit key - char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state - char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state - // NOTE: Since key press logic involves comparing prev vs cur key state, we need to handle key repeats specially - char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame. - - int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue - int keyPressedQueueCount; // Input keys queue count - - int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode) - int charPressedQueueCount; // Input characters queue count - -#if defined(PLATFORM_DRM) - int defaultMode; // Default keyboard mode -#if defined(SUPPORT_SSH_KEYBOARD_RPI) - bool evtMode; // Keyboard in event mode -#endif - int defaultFileFlags; // Default IO file flags - struct termios defaultSettings; // Default keyboard settings - int fd; // File descriptor for the evdev keyboard -#endif - } Keyboard; - struct { - Vector2 offset; // Mouse offset - Vector2 scale; // Mouse scaling - Vector2 currentPosition; // Mouse position on screen - Vector2 previousPosition; // Previous mouse position - - int cursor; // Tracks current mouse cursor - bool cursorHidden; // Track if cursor is hidden - bool cursorOnScreen; // Tracks if cursor is inside client area - - char currentButtonState[MAX_MOUSE_BUTTONS]; // Registers current mouse button state - char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state - Vector2 currentWheelMove; // Registers current mouse wheel variation - Vector2 previousWheelMove; // Registers previous mouse wheel variation -#if defined(PLATFORM_DRM) - Vector2 eventWheelMove; // Registers the event mouse wheel variation - // NOTE: currentButtonState[] can't be written directly due to multithreading, app could miss the update - char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab -#endif - } Mouse; - struct { - int pointCount; // Number of touch points active - int pointId[MAX_TOUCH_POINTS]; // Point identifiers - Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen - char currentTouchState[MAX_TOUCH_POINTS]; // Registers current touch state - char previousTouchState[MAX_TOUCH_POINTS]; // Registers previous touch state - } Touch; - struct { - int lastButtonPressed; // Register last gamepad button pressed - int axisCount; // Register number of available gamepad axis - bool ready[MAX_GAMEPADS]; // Flag to know if gamepad is ready - char name[MAX_GAMEPADS][64]; // Gamepad name holder - char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state - char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state - float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state -#if defined(PLATFORM_DRM) - pthread_t threadId; // Gamepad reading thread id - int streamId[MAX_GAMEPADS]; // Gamepad device file descriptor -#endif - } Gamepad; - } Input; - struct { - double current; // Current time measure - double previous; // Previous time measure - double update; // Time measure for frame update - double draw; // Time measure for frame draw - double frame; // Time measure for one frame - double target; // Desired time for one frame, if 0 not applied -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) - unsigned long long int base; // Base time measure for hi-res timer -#endif - unsigned int frameCounter; // Frame counter - } Time; -} CoreData; - //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- RLAPI const char *raylib_version = RAYLIB_VERSION; // raylib version exported symbol, required for some bindings -static CoreData CORE = { 0 }; // Global CORE state context +CoreData CORE = { 0 }; // Global CORE state context #if defined(SUPPORT_SCREEN_CAPTURE) -static int screenshotCounter = 0; // Screenshots counter +static int screenshotCounter = 0; // Screenshots counter #endif #if defined(SUPPORT_GIF_RECORDING) -static int gifFrameCounter = 0; // GIF frames counter -static bool gifRecording = false; // GIF recording state -static MsfGifState gifState = { 0 }; // MSGIF context state +int gifFrameCounter = 0; // GIF frames counter +bool gifRecording = false; // GIF recording state +MsfGifState gifState = { 0 }; // MSGIF context state #endif #if defined(SUPPORT_EVENTS_AUTOMATION) @@ -602,79 +291,22 @@ static bool eventsRecording = false; // Record events //----------------------------------------------------------------------------------- //---------------------------------------------------------------------------------- -// Other Modules Functions Declaration (required by core) +// Module Functions Declaration +// NOTE: Those functions are common for all platforms! //---------------------------------------------------------------------------------- + #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) -extern void LoadFontDefault(void); // [Module: text] Loads default font on InitWindow() -extern void UnloadFontDefault(void); // [Module: text] Unloads default font from GPU memory +extern void LoadFontDefault(void); // [Module: text] Loads default font on InitWindow() +extern void UnloadFontDefault(void); // [Module: text] Unloads default font from GPU memory #endif -//---------------------------------------------------------------------------------- -// Module specific Functions Declaration -//---------------------------------------------------------------------------------- -static void InitTimer(void); // Initialize timer (hi-resolution if available) -static bool InitGraphicsDevice(int width, int height); // Initialize graphics device -static void SetupFramebuffer(int width, int height); // Setup main framebuffer -static void SetupViewport(int width, int height); // Set viewport for a provided width and height +static void InitTimer(void); // Initialize timer (hi-resolution if available) +static void SetupFramebuffer(int width, int height); // Setup main framebuffer +static void SetupViewport(int width, int height); // Set viewport for a provided width and height static void ScanDirectoryFiles(const char *basePath, FilePathList *list, const char *filter); // Scan all files and directories in a base path static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *list, const char *filter); // Scan all files and directories recursively from a base path -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) -static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error -// Window callbacks events -static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized -#if !defined(PLATFORM_WEB) -static void WindowMaximizeCallback(GLFWwindow* window, int maximized); // GLFW3 Window Maximize Callback, runs when window is maximized -#endif -static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored -static void WindowFocusCallback(GLFWwindow *window, int focused); // GLFW3 WindowFocus Callback, runs when window get/lose focus -static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window -// Input callbacks events -static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed -static void CharCallback(GLFWwindow *window, unsigned int key); // GLFW3 Char Key Callback, runs on key pressed (get char value) -static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods); // GLFW3 Mouse Button Callback, runs on mouse button pressed -static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); // GLFW3 Cursor Position Callback, runs on mouse move -static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel -static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area -#endif - -#if defined(PLATFORM_ANDROID) -static void AndroidCommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands -static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event); // Process Android inputs -#endif - -#if defined(PLATFORM_WEB) -static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData); -static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData); -static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData); - -static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); -static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData); -static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData); -#endif - -#if defined(PLATFORM_DRM) -static void InitKeyboard(void); // Initialize raw keyboard system -static void RestoreKeyboard(void); // Restore keyboard system -#if defined(SUPPORT_SSH_KEYBOARD_RPI) -static void ProcessKeyboard(void); // Process keyboard events -#endif - -static void InitEvdevInput(void); // Initialize evdev inputs -static void ConfigureEvdevDevice(char *device); // Identifies a input device and configures it for use if appropriate -static void PollKeyboardEvents(void); // Process evdev keyboard events. -static void *EventThread(void *arg); // Input device events reading thread - -static void InitGamepad(void); // Initialize raw gamepad input -static void *GamepadThread(void *arg); // Mouse reading thread - -static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode); // Search matching DRM mode in connector's mode list -static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced); // Search exactly matching DRM connector mode in connector's list -static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced); // Search the nearest matching DRM connector mode in connector's list - -#endif // PLATFORM_DRM - #if defined(SUPPORT_EVENTS_AUTOMATION) static void LoadAutomationEvents(const char *fileName); // Load automation events from file static void ExportAutomationEvents(const char *fileName); // Export recorded automation events into a file @@ -691,443 +323,73 @@ void __stdcall Sleep(unsigned long msTimeout); // Required for: Wai const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' #endif // !SUPPORT_MODULE_RTEXT +// Include platform-specific submodules +#if defined(PLATFORM_DESKTOP) + #include "rcore_desktop.c" +#elif defined(PLATFORM_WEB) + #include "rcore_web.c" +#elif defined(PLATFORM_DRM) + #include "rcore_drm.c" +#elif defined(PLATFORM_ANDROID) + #include "rcore_android.c" +#else + // Software rendering backend, user needs to provide buffer ;) +#endif + //---------------------------------------------------------------------------------- // Module Functions Definition - Window and OpenGL Context Functions //---------------------------------------------------------------------------------- -#if defined(PLATFORM_ANDROID) -// To allow easier porting to android, we allow the user to define a -// main function which we call from android_main, defined by ourselves -extern int main(int argc, char *argv[]); -void android_main(struct android_app *app) -{ - char arg0[] = "raylib"; // NOTE: argv[] are mutable - CORE.Android.app = app; +// NOTE: Multiple window/display/monitor management functions have been moved to platform-specific modules + +// Platform-specific functions: +//void InitWindow(int width, int height, const char *title); +//void CloseWindow(void); +//bool WindowShouldClose(void) +//bool IsWindowHidden(void) +//bool IsWindowMinimized(void) +//bool IsWindowMaximized(void) +//bool IsWindowFocused(void) +//bool IsWindowResized(void) +//void ToggleFullscreen(void) +//void MaximizeWindow(void) +//void MinimizeWindow(void) +//void RestoreWindow(void) +//void ToggleBorderlessWindowed(void) +//void SetWindowState(unsigned int flags) +//void ClearWindowState(unsigned int flags) +//void SetWindowIcon(Image image) +//void SetWindowIcons(Image *images, int count) +//void SetWindowTitle(const char *title) +//void SetWindowPosition(int x, int y) +//void SetWindowMonitor(int monitor) +//void SetWindowMinSize(int width, int height) +//void SetWindowMaxSize(int width, int height) +//void SetWindowSize(int width, int height) +//void SetWindowOpacity(float opacity) +//void SetWindowFocused(void) +//void *GetWindowHandle(void) +//int GetMonitorCount(void) +//int GetCurrentMonitor(void) +//Vector2 GetMonitorPosition(int monitor) +//int GetMonitorWidth(int monitor) +//int GetMonitorHeight(int monitor) +//int GetMonitorPhysicalWidth(int monitor) +//int GetMonitorPhysicalHeight(int monitor) +//int GetMonitorRefreshRate(int monitor) +//const char *GetMonitorName(int monitor) +//Vector2 GetWindowPosition(void) +//Vector2 GetWindowScaleDPI(void) +//void SetClipboardText(const char *text) +//const char *GetClipboardText(void) +//void ShowCursor(void) +//void HideCursor(void) +//void EnableCursor(void) +//void DisableCursor(void) +//double GetTime(void) +//void TakeScreenshot(const char *fileName) +//void OpenURL(const char *url) - // NOTE: Return from main is ignored - (void)main(1, (char *[]) { arg0, NULL }); - - // Request to end the native activity - ANativeActivity_finish(app->activity); - - // Android ALooper_pollAll() variables - int pollResult = 0; - int pollEvents = 0; - - // Waiting for application events before complete finishing - while (!app->destroyRequested) - { - while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void **)&CORE.Android.source)) >= 0) - { - if (CORE.Android.source != NULL) CORE.Android.source->process(app, CORE.Android.source); - } - } -} - -// NOTE: Add this to header (if apps really need it) -struct android_app *GetAndroidApp(void) -{ - return CORE.Android.app; -} -#endif - -// Initialize window and OpenGL context -// NOTE: data parameter could be used to pass any kind of required data to the initialization -void InitWindow(int width, int height, const char *title) -{ - TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); - - TRACELOG(LOG_INFO, "Supported raylib modules:"); - TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); - TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); -#if defined(SUPPORT_MODULE_RSHAPES) - TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXTURES) - TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXT) - TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RMODELS) - TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RAUDIO) - TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); -#endif - - if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; - - // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); - CORE.Input.Keyboard.exitKey = KEY_ESCAPE; - CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; - CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN -#if defined(SUPPORT_EVENTS_WAITING) - CORE.Window.eventWaiting = true; -#endif - -#if defined(PLATFORM_ANDROID) - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - CORE.Window.currentFbo.width = width; - CORE.Window.currentFbo.height = height; - - // Set desired windows flags before initializing anything - ANativeActivity_setWindowFlags(CORE.Android.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER - - int orientation = AConfiguration_getOrientation(CORE.Android.app->config); - - if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait"); - else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape"); - - // TODO: Automatic orientation doesn't seem to work - if (width <= height) - { - AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_PORT); - TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait"); - } - else - { - AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_LAND); - TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape"); - } - - //AConfiguration_getDensity(CORE.Android.app->config); - //AConfiguration_getKeyboard(CORE.Android.app->config); - //AConfiguration_getScreenSize(CORE.Android.app->config); - //AConfiguration_getScreenLong(CORE.Android.app->config); - - // Initialize App command system - // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()... - CORE.Android.app->onAppCmd = AndroidCommandCallback; - - // Initialize input events system - CORE.Android.app->onInputEvent = AndroidInputCallback; - - // Initialize assets manager - InitAssetManager(CORE.Android.app->activity->assetManager, CORE.Android.app->activity->internalDataPath); - - // Initialize base path for storage - CORE.Storage.basePath = CORE.Android.app->activity->internalDataPath; - - TRACELOG(LOG_INFO, "ANDROID: App initialized successfully"); - - // Android ALooper_pollAll() variables - int pollResult = 0; - int pollEvents = 0; - - // Wait for window to be initialized (display and context) - while (!CORE.Window.ready) - { - // Process events loop - while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) - { - // Process this event - if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); - - // NOTE: Never close window, native activity is controlled by the system! - //if (CORE.Android.app->destroyRequested != 0) CORE.Window.shouldClose = true; - } - } -#endif -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) || defined(PLATFORM_DRM) - // Initialize graphics device (display device and OpenGL context) - // NOTE: returns true if window and graphic device has been initialized successfully - CORE.Window.ready = InitGraphicsDevice(width, height); - - // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) - { - TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); - return; - } - else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); - - // Initialize hi-res timer - InitTimer(); - - // Initialize random seed - srand((unsigned int)time(NULL)); - - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - // Load default font - // WARNING: External function: Module required: rtext - LoadFontDefault(); - #if defined(SUPPORT_MODULE_RSHAPES) - // Set font white rectangle for shapes drawing, so shapes and text can be batched together - // WARNING: rshapes module is required, if not available, default internal white rectangle is used - Rectangle rec = GetFontDefault().recs[95]; - if (CORE.Window.flags & FLAG_MSAA_4X_HINT) - { - // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); - } - else - { - // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); - } - #endif -#else - #if defined(SUPPORT_MODULE_RSHAPES) - // Set default texture and rectangle to be used for shapes drawing - // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 - Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes - #endif -#endif -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // Set default font texture filter for HighDPI (blurry) - // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); - } -#endif - -#if defined(PLATFORM_DRM) - // Initialize raw input system - InitEvdevInput(); // Evdev inputs initialization - InitGamepad(); // Gamepad init - InitKeyboard(); // Keyboard init (stdin) -#endif - -#if defined(PLATFORM_WEB) - // Setup callback functions for the DOM events - emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); - - // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review - // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas) - //emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - // Check Resize event (note this is done on the window since most browsers don't support this on #canvas) - emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - // Trigger this once to get initial window sizing - EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); - - // Support keyboard events -> Not used, GLFW.JS takes care of that - //emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); - //emscripten_set_keydown_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); - - // Support mouse events - emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback); - - // Support touch events - emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - - // Support gamepad events (not provided by GLFW3 on emscripten) - emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); - emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); -#endif - -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; -#endif - -#endif // PLATFORM_DESKTOP || PLATFORM_WEB || PLATFORM_DRM -} - -// Close window and unload OpenGL context -void CloseWindow(void) -{ -#if defined(SUPPORT_GIF_RECORDING) - if (gifRecording) - { - MsfGifResult result = msf_gif_end(&gifState); - msf_gif_free(result); - gifRecording = false; - } -#endif - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - UnloadFontDefault(); // WARNING: Module required: rtext -#endif - - rlglClose(); // De-init rlgl - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - glfwDestroyWindow(CORE.Window.handle); - glfwTerminate(); -#endif - -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - timeEndPeriod(1); // Restore time period -#endif - -#if defined(PLATFORM_ANDROID) - // Close surface, context and display - if (CORE.Window.device != EGL_NO_DISPLAY) - { - eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - - if (CORE.Window.surface != EGL_NO_SURFACE) - { - eglDestroySurface(CORE.Window.device, CORE.Window.surface); - CORE.Window.surface = EGL_NO_SURFACE; - } - - if (CORE.Window.context != EGL_NO_CONTEXT) - { - eglDestroyContext(CORE.Window.device, CORE.Window.context); - CORE.Window.context = EGL_NO_CONTEXT; - } - - eglTerminate(CORE.Window.device); - CORE.Window.device = EGL_NO_DISPLAY; - } -#endif - -#if defined(PLATFORM_DRM) - if (CORE.Window.prevFB) - { - drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB); - CORE.Window.prevFB = 0; - } - - if (CORE.Window.prevBO) - { - gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO); - CORE.Window.prevBO = NULL; - } - - if (CORE.Window.gbmSurface) - { - gbm_surface_destroy(CORE.Window.gbmSurface); - CORE.Window.gbmSurface = NULL; - } - - if (CORE.Window.gbmDevice) - { - gbm_device_destroy(CORE.Window.gbmDevice); - CORE.Window.gbmDevice = NULL; - } - - if (CORE.Window.crtc) - { - if (CORE.Window.connector) - { - drmModeSetCrtc(CORE.Window.fd, CORE.Window.crtc->crtc_id, CORE.Window.crtc->buffer_id, - CORE.Window.crtc->x, CORE.Window.crtc->y, &CORE.Window.connector->connector_id, 1, &CORE.Window.crtc->mode); - drmModeFreeConnector(CORE.Window.connector); - CORE.Window.connector = NULL; - } - - drmModeFreeCrtc(CORE.Window.crtc); - CORE.Window.crtc = NULL; - } - - if (CORE.Window.fd != -1) - { - close(CORE.Window.fd); - CORE.Window.fd = -1; - } - - // Close surface, context and display - if (CORE.Window.device != EGL_NO_DISPLAY) - { - if (CORE.Window.surface != EGL_NO_SURFACE) - { - eglDestroySurface(CORE.Window.device, CORE.Window.surface); - CORE.Window.surface = EGL_NO_SURFACE; - } - - if (CORE.Window.context != EGL_NO_CONTEXT) - { - eglDestroyContext(CORE.Window.device, CORE.Window.context); - CORE.Window.context = EGL_NO_CONTEXT; - } - - eglTerminate(CORE.Window.device); - CORE.Window.device = EGL_NO_DISPLAY; - } -#endif - -#if defined(PLATFORM_DRM) - // Wait for mouse and gamepad threads to finish before closing - // NOTE: Those threads should already have finished at this point - // because they are controlled by CORE.Window.shouldClose variable - - CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called - - // Close the evdev keyboard - if (CORE.Input.Keyboard.fd != -1) - { - close(CORE.Input.Keyboard.fd); - CORE.Input.Keyboard.fd = -1; - } - - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) - { - if (CORE.Input.eventWorker[i].threadId) - { - pthread_join(CORE.Input.eventWorker[i].threadId, NULL); - } - } - - if (CORE.Input.Gamepad.threadId) pthread_join(CORE.Input.Gamepad.threadId, NULL); -#endif - -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - - CORE.Window.ready = false; - TRACELOG(LOG_INFO, "Window closed successfully"); -} - -// Check if KEY_ESCAPE pressed or Close icon pressed -bool WindowShouldClose(void) -{ -#if defined(PLATFORM_WEB) - // Emterpreter-Async required to run sync code - // https://github.com/emscripten-core/emscripten/wiki/Emterpreter#emterpreter-async-run-synchronous-code - // By default, this function is never called on a web-ready raylib example because we encapsulate - // frame code in a UpdateDrawFrame() function, to allow browser manage execution asynchronously - // but now emscripten allows sync code to be executed in an interpreted way, using emterpreter! - emscripten_sleep(16); - return false; -#endif - -#if defined(PLATFORM_DESKTOP) - if (CORE.Window.ready) - { - // While window minimized, stop loop execution - while (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)) glfwWaitEvents(); - - CORE.Window.shouldClose = glfwWindowShouldClose(CORE.Window.handle); - - // Reset close status for next frame - glfwSetWindowShouldClose(CORE.Window.handle, GLFW_FALSE); - - return CORE.Window.shouldClose; - } - else return true; -#endif - -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) - if (CORE.Window.ready) return CORE.Window.shouldClose; - else return true; -#endif -} // Check if window has been initialized successfully bool IsWindowReady(void) @@ -1141,713 +403,12 @@ bool IsWindowFullscreen(void) return CORE.Window.fullscreen; } -// Check if window is currently hidden -bool IsWindowHidden(void) -{ -#if defined(PLATFORM_DESKTOP) - return ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0); -#endif - return false; -} - -// Check if window has been minimized -bool IsWindowMinimized(void) -{ -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - return ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0); -#endif - return false; -} - -// Check if window has been maximized (only PLATFORM_DESKTOP) -bool IsWindowMaximized(void) -{ -#if defined(PLATFORM_DESKTOP) - return ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0); -#endif - return false; -} - -// Check if window has the focus -bool IsWindowFocused(void) -{ -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0); -#endif -#if defined(PLATFORM_ANDROID) - return CORE.Android.appEnabled; -#endif - return true; -} - -// Check if window has been resizedLastFrame -bool IsWindowResized(void) -{ -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - return CORE.Window.resizedLastFrame; -#else - return false; -#endif -} - // Check if one specific window flag is enabled bool IsWindowState(unsigned int flag) { return ((CORE.Window.flags & flag) > 0); } -// Toggle fullscreen mode (only PLATFORM_DESKTOP) -void ToggleFullscreen(void) -{ -#if defined(PLATFORM_DESKTOP) - if (!CORE.Window.fullscreen) - { - // Store previous window position (in case we exit fullscreen) - glfwGetWindowPos(CORE.Window.handle, &CORE.Window.position.x, &CORE.Window.position.y); - - int monitorCount = 0; - int monitorIndex = GetCurrentMonitor(); - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - // Use current monitor, so we correctly get the display the window is on - GLFWmonitor *monitor = (monitorIndex < monitorCount)? monitors[monitorIndex] : NULL; - - if (monitor == NULL) - { - TRACELOG(LOG_WARNING, "GLFW: Failed to get monitor"); - - CORE.Window.fullscreen = false; - CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; - - glfwSetWindowMonitor(CORE.Window.handle, NULL, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); - } - else - { - CORE.Window.fullscreen = true; - CORE.Window.flags |= FLAG_FULLSCREEN_MODE; - - glfwSetWindowMonitor(CORE.Window.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); - } - } - else - { - CORE.Window.fullscreen = false; - CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; - - glfwSetWindowMonitor(CORE.Window.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); - } - - // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) - // NOTE: V-Sync can be enabled by graphic driver configuration - if (CORE.Window.flags & FLAG_VSYNC_HINT) glfwSwapInterval(1); -#endif -#if defined(PLATFORM_WEB) -/* - EM_ASM - ( - // This strategy works well while using raylib minimal web shell for emscripten, - // it re-scales the canvas to fullscreen using monitor resolution, for tools this - // is a good strategy but maybe games prefer to keep current canvas resolution and - // display it in fullscreen, adjusting monitor resolution if possible - if (document.fullscreenElement) document.exitFullscreen(); - else Module.requestFullscreen(true, true); //false, true); - ); -*/ - //EM_ASM(Module.requestFullscreen(false, false);); -/* - if (!CORE.Window.fullscreen) - { - // Option 1: Request fullscreen for the canvas element - // This option does not seem to work at all: - // emscripten_request_pointerlock() and emscripten_request_fullscreen() are affected by web security, - // the user must click once on the canvas to hide the pointer or transition to full screen - //emscripten_request_fullscreen("#canvas", false); - - // Option 2: Request fullscreen for the canvas element with strategy - // This option does not seem to work at all - // Ref: https://github.com/emscripten-core/emscripten/issues/5124 - // EmscriptenFullscreenStrategy strategy = { - // .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH, //EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT, - // .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF, - // .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT, - // .canvasResizedCallback = EmscriptenWindowResizedCallback, - // .canvasResizedCallbackUserData = NULL - // }; - //emscripten_request_fullscreen_strategy("#canvas", EM_FALSE, &strategy); - - // Option 3: Request fullscreen for the canvas element with strategy - // It works as expected but only inside the browser (client area) - EmscriptenFullscreenStrategy strategy = { - .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT, - .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF, - .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT, - .canvasResizedCallback = EmscriptenWindowResizedCallback, - .canvasResizedCallbackUserData = NULL - }; - emscripten_enter_soft_fullscreen("#canvas", &strategy); - - int width, height; - emscripten_get_canvas_element_size("#canvas", &width, &height); - TRACELOG(LOG_WARNING, "Emscripten: Enter fullscreen: Canvas size: %i x %i", width, height); - - CORE.Window.fullscreen = true; // Toggle fullscreen flag - CORE.Window.flags |= FLAG_FULLSCREEN_MODE; - } - else - { - //emscripten_exit_fullscreen(); - //emscripten_exit_soft_fullscreen(); - - int width, height; - emscripten_get_canvas_element_size("#canvas", &width, &height); - TRACELOG(LOG_WARNING, "Emscripten: Exit fullscreen: Canvas size: %i x %i", width, height); - - CORE.Window.fullscreen = false; // Toggle fullscreen flag - CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; - } -*/ - - CORE.Window.fullscreen = !CORE.Window.fullscreen; // Toggle fullscreen flag -#endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) - TRACELOG(LOG_WARNING, "SYSTEM: Failed to toggle to windowed mode"); -#endif -} - -// Toggle borderless windowed mode (only PLATFORM_DESKTOP) -void ToggleBorderlessWindowed(void) -{ -#if defined(PLATFORM_DESKTOP) - // Leave fullscreen before attempting to set borderless windowed mode and get screen position from it - bool wasOnFullscreen = false; - if (CORE.Window.fullscreen) - { - CORE.Window.previousPosition = CORE.Window.position; - ToggleFullscreen(); - wasOnFullscreen = true; - } - - const int monitor = GetCurrentMonitor(); - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - if ((monitor >= 0) && (monitor < monitorCount)) - { - const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - if (mode) - { - if (!IsWindowState(FLAG_BORDERLESS_WINDOWED_MODE)) - { - // Store screen position and size - // NOTE: If it was on fullscreen, screen position was already stored, so skip setting it here - if (!wasOnFullscreen) glfwGetWindowPos(CORE.Window.handle, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y); - CORE.Window.previousScreen = CORE.Window.screen; - - // Set undecorated and topmost modes and flags - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE); - CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE); - CORE.Window.flags |= FLAG_WINDOW_TOPMOST; - - // Get monitor position and size - int monitorPosX = 0; - int monitorPosY = 0; - glfwGetMonitorPos(monitors[monitor], &monitorPosX, &monitorPosY); - const int monitorWidth = mode->width; - const int monitorHeight = mode->height; - glfwSetWindowSize(CORE.Window.handle, monitorWidth, monitorHeight); - - // Set screen position and size - glfwSetWindowPos(CORE.Window.handle, monitorPosX, monitorPosY); - glfwSetWindowSize(CORE.Window.handle, monitorWidth, monitorHeight); - - // Refocus window - glfwFocusWindow(CORE.Window.handle); - - CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE; - } - else - { - // Remove topmost and undecorated modes and flags - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE); - CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); - CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; - - // Return previous screen size and position - // NOTE: The order matters here, it must set size first, then set position, otherwise the screen will be positioned incorrectly - glfwSetWindowSize(CORE.Window.handle, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); - glfwSetWindowPos(CORE.Window.handle, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y); - - // Refocus window - glfwFocusWindow(CORE.Window.handle); - - CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; - } - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif -} - -// Set window state: maximized, if resizable (only PLATFORM_DESKTOP) -void MaximizeWindow(void) -{ -#if defined(PLATFORM_DESKTOP) - if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE) - { - glfwMaximizeWindow(CORE.Window.handle); - CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; - } -#endif -} - -// Set window state: minimized (only PLATFORM_DESKTOP) -void MinimizeWindow(void) -{ -#if defined(PLATFORM_DESKTOP) - // NOTE: Following function launches callback that sets appropriate flag! - glfwIconifyWindow(CORE.Window.handle); -#endif -} - -// Set window state: not minimized/maximized (only PLATFORM_DESKTOP) -void RestoreWindow(void) -{ -#if defined(PLATFORM_DESKTOP) - if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE) - { - // Restores the specified window if it was previously iconified (minimized) or maximized - glfwRestoreWindow(CORE.Window.handle); - CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; - CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; - } -#endif -} - -// Set window configuration state using flags -void SetWindowState(unsigned int flags) -{ -#if defined(PLATFORM_DESKTOP) - // Check previous state and requested state to apply required changes - // NOTE: In most cases the functions already change the flags internally - - // State change: FLAG_VSYNC_HINT - if (((CORE.Window.flags & FLAG_VSYNC_HINT) != (flags & FLAG_VSYNC_HINT)) && ((flags & FLAG_VSYNC_HINT) > 0)) - { - glfwSwapInterval(1); - CORE.Window.flags |= FLAG_VSYNC_HINT; - } - - // State change: FLAG_BORDERLESS_WINDOWED_MODE - // NOTE: This must be handled before FLAG_FULLSCREEN_MODE because ToggleBorderlessWindowed() needs to get some fullscreen values if fullscreen is running - if (((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) != (flags & FLAG_BORDERLESS_WINDOWED_MODE)) && ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)) - { - ToggleBorderlessWindowed(); // NOTE: Window state flag updated inside function - } - - // State change: FLAG_FULLSCREEN_MODE - if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) != (flags & FLAG_FULLSCREEN_MODE)) - { - ToggleFullscreen(); // NOTE: Window state flag updated inside function - } - - // State change: FLAG_WINDOW_RESIZABLE - if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != (flags & FLAG_WINDOW_RESIZABLE)) && ((flags & FLAG_WINDOW_RESIZABLE) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_TRUE); - CORE.Window.flags |= FLAG_WINDOW_RESIZABLE; - } - - // State change: FLAG_WINDOW_UNDECORATED - if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) != (flags & FLAG_WINDOW_UNDECORATED)) && (flags & FLAG_WINDOW_UNDECORATED)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE); - CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; - } - - // State change: FLAG_WINDOW_HIDDEN - if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) != (flags & FLAG_WINDOW_HIDDEN)) && ((flags & FLAG_WINDOW_HIDDEN) > 0)) - { - glfwHideWindow(CORE.Window.handle); - CORE.Window.flags |= FLAG_WINDOW_HIDDEN; - } - - // State change: FLAG_WINDOW_MINIMIZED - if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) != (flags & FLAG_WINDOW_MINIMIZED)) && ((flags & FLAG_WINDOW_MINIMIZED) > 0)) - { - //GLFW_ICONIFIED - MinimizeWindow(); // NOTE: Window state flag updated inside function - } - - // State change: FLAG_WINDOW_MAXIMIZED - if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) != (flags & FLAG_WINDOW_MAXIMIZED)) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0)) - { - //GLFW_MAXIMIZED - MaximizeWindow(); // NOTE: Window state flag updated inside function - } - - // State change: FLAG_WINDOW_UNFOCUSED - if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) != (flags & FLAG_WINDOW_UNFOCUSED)) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_FALSE); - CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; - } - - // State change: FLAG_WINDOW_TOPMOST - if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) != (flags & FLAG_WINDOW_TOPMOST)) && ((flags & FLAG_WINDOW_TOPMOST) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE); - CORE.Window.flags |= FLAG_WINDOW_TOPMOST; - } - - // State change: FLAG_WINDOW_ALWAYS_RUN - if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) != (flags & FLAG_WINDOW_ALWAYS_RUN)) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0)) - { - CORE.Window.flags |= FLAG_WINDOW_ALWAYS_RUN; - } - - // The following states can not be changed after window creation - - // State change: FLAG_WINDOW_TRANSPARENT - if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) != (flags & FLAG_WINDOW_TRANSPARENT)) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0)) - { - TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization"); - } - - // State change: FLAG_WINDOW_HIGHDPI - if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) != (flags & FLAG_WINDOW_HIGHDPI)) && ((flags & FLAG_WINDOW_HIGHDPI) > 0)) - { - TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization"); - } - - // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH - if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) != (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); - CORE.Window.flags |= FLAG_WINDOW_MOUSE_PASSTHROUGH; - } - - // State change: FLAG_MSAA_4X_HINT - if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) != (flags & FLAG_MSAA_4X_HINT)) && ((flags & FLAG_MSAA_4X_HINT) > 0)) - { - TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization"); - } - - // State change: FLAG_INTERLACED_HINT - if (((CORE.Window.flags & FLAG_INTERLACED_HINT) != (flags & FLAG_INTERLACED_HINT)) && ((flags & FLAG_INTERLACED_HINT) > 0)) - { - TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization"); - } -#endif -} - -// Clear window configuration state flags -void ClearWindowState(unsigned int flags) -{ -#if defined(PLATFORM_DESKTOP) - // Check previous state and requested state to apply required changes - // NOTE: In most cases the functions already change the flags internally - - // State change: FLAG_VSYNC_HINT - if (((CORE.Window.flags & FLAG_VSYNC_HINT) > 0) && ((flags & FLAG_VSYNC_HINT) > 0)) - { - glfwSwapInterval(0); - CORE.Window.flags &= ~FLAG_VSYNC_HINT; - } - - // State change: FLAG_BORDERLESS_WINDOWED_MODE - // NOTE: This must be handled before FLAG_FULLSCREEN_MODE because ToggleBorderlessWindowed() needs to get some fullscreen values if fullscreen is running - if (((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0) && ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)) - { - ToggleBorderlessWindowed(); // NOTE: Window state flag updated inside function - } - - // State change: FLAG_FULLSCREEN_MODE - if (((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) && ((flags & FLAG_FULLSCREEN_MODE) > 0)) - { - ToggleFullscreen(); // NOTE: Window state flag updated inside function - } - - // State change: FLAG_WINDOW_RESIZABLE - if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) && ((flags & FLAG_WINDOW_RESIZABLE) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_FALSE); - CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE; - } - - // State change: FLAG_WINDOW_HIDDEN - if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) && ((flags & FLAG_WINDOW_HIDDEN) > 0)) - { - glfwShowWindow(CORE.Window.handle); - CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; - } - - // State change: FLAG_WINDOW_MINIMIZED - if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) && ((flags & FLAG_WINDOW_MINIMIZED) > 0)) - { - RestoreWindow(); // NOTE: Window state flag updated inside function - } - - // State change: FLAG_WINDOW_MAXIMIZED - if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0)) - { - RestoreWindow(); // NOTE: Window state flag updated inside function - } - - // State change: FLAG_WINDOW_UNDECORATED - if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) && ((flags & FLAG_WINDOW_UNDECORATED) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); - CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; - } - - // State change: FLAG_WINDOW_UNFOCUSED - if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_TRUE); - CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; - } - - // State change: FLAG_WINDOW_TOPMOST - if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) && ((flags & FLAG_WINDOW_TOPMOST) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE); - CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; - } - - // State change: FLAG_WINDOW_ALWAYS_RUN - if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) > 0) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0)) - { - CORE.Window.flags &= ~FLAG_WINDOW_ALWAYS_RUN; - } - - // The following states can not be changed after window creation - - // State change: FLAG_WINDOW_TRANSPARENT - if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0)) - { - TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization"); - } - - // State change: FLAG_WINDOW_HIGHDPI - if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) && ((flags & FLAG_WINDOW_HIGHDPI) > 0)) - { - TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization"); - } - - // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH - if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); - CORE.Window.flags &= ~FLAG_WINDOW_MOUSE_PASSTHROUGH; - } - - // State change: FLAG_MSAA_4X_HINT - if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) > 0) && ((flags & FLAG_MSAA_4X_HINT) > 0)) - { - TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization"); - } - - // State change: FLAG_INTERLACED_HINT - if (((CORE.Window.flags & FLAG_INTERLACED_HINT) > 0) && ((flags & FLAG_INTERLACED_HINT) > 0)) - { - TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization"); - } -#endif -} - -// Set icon for window (only PLATFORM_DESKTOP) -// NOTE 1: Image must be in RGBA format, 8bit per channel -// NOTE 2: Image is scaled by the OS for all required sizes -void SetWindowIcon(Image image) -{ -#if defined(PLATFORM_DESKTOP) - if (image.data == NULL) - { - // Revert to the default window icon, pass in an empty image array - glfwSetWindowIcon(CORE.Window.handle, 0, NULL); - } - else - { - if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) - { - GLFWimage icon[1] = { 0 }; - - icon[0].width = image.width; - icon[0].height = image.height; - icon[0].pixels = (unsigned char *)image.data; - - // NOTE 1: We only support one image icon - // NOTE 2: The specified image data is copied before this function returns - glfwSetWindowIcon(CORE.Window.handle, 1, icon); - } - else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); - } -#endif -} - -// Set icon for window (multiple images, only PLATFORM_DESKTOP) -// NOTE 1: Images must be in RGBA format, 8bit per channel -// NOTE 2: The multiple images are used depending on provided sizes -// Standard Windows icon sizes: 256, 128, 96, 64, 48, 32, 24, 16 -void SetWindowIcons(Image *images, int count) -{ -#if defined(PLATFORM_DESKTOP) - if ((images == NULL) || (count <= 0)) - { - // Revert to the default window icon, pass in an empty image array - glfwSetWindowIcon(CORE.Window.handle, 0, NULL); - } - else - { - int valid = 0; - GLFWimage *icons = RL_CALLOC(count, sizeof(GLFWimage)); - - for (int i = 0; i < count; i++) - { - if (images[i].format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) - { - icons[valid].width = images[i].width; - icons[valid].height = images[i].height; - icons[valid].pixels = (unsigned char *)images[i].data; - - valid++; - } - else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); - } - // NOTE: Images data is copied internally before this function returns - glfwSetWindowIcon(CORE.Window.handle, valid, icons); - - RL_FREE(icons); - } -#endif -} - -// Set title for window (only PLATFORM_DESKTOP and PLATFORM_WEB) -void SetWindowTitle(const char *title) -{ - CORE.Window.title = title; -#if defined(PLATFORM_DESKTOP) - glfwSetWindowTitle(CORE.Window.handle, title); -#endif -#if defined(PLATFORM_WEB) - emscripten_set_window_title(title); -#endif -} - -// Set window position on screen (windowed mode) -void SetWindowPosition(int x, int y) -{ -#if defined(PLATFORM_DESKTOP) - glfwSetWindowPos(CORE.Window.handle, x, y); -#endif -} - -// Set monitor for the current window -void SetWindowMonitor(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount = 0; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - if (CORE.Window.fullscreen) - { - TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); - - const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - glfwSetWindowMonitor(CORE.Window.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate); - } - else - { - TRACELOG(LOG_INFO, "GLFW: Selected monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); - - const int screenWidth = CORE.Window.screen.width; - const int screenHeight = CORE.Window.screen.height; - int monitorWorkareaX = 0; - int monitorWorkareaY = 0; - int monitorWorkareaWidth = 0; - int monitorWorkareaHeight = 0; - glfwGetMonitorWorkarea(monitors[monitor], &monitorWorkareaX, &monitorWorkareaY, &monitorWorkareaWidth, &monitorWorkareaHeight); - - // If the screen size is larger than the monitor workarea, anchor it on the top left corner, otherwise, center it - if ((screenWidth >= monitorWorkareaWidth) || (screenHeight >= monitorWorkareaHeight)) glfwSetWindowPos(CORE.Window.handle, monitorWorkareaX, monitorWorkareaY); - else - { - const int x = monitorWorkareaX + (monitorWorkareaWidth/2) - (screenWidth/2); - const int y = monitorWorkareaY + (monitorWorkareaHeight/2) - (screenHeight/2); - glfwSetWindowPos(CORE.Window.handle, x, y); - } - } - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif -} - -// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) -void SetWindowMinSize(int width, int height) -{ - CORE.Window.screenMin.width = width; - CORE.Window.screenMin.height = height; -#if defined(PLATFORM_DESKTOP) - int minWidth = (CORE.Window.screenMin.width == 0) ? GLFW_DONT_CARE : CORE.Window.screenMin.width; - int minHeight = (CORE.Window.screenMin.height == 0) ? GLFW_DONT_CARE : CORE.Window.screenMin.height; - int maxWidth = (CORE.Window.screenMax.width == 0) ? GLFW_DONT_CARE : CORE.Window.screenMax.width; - int maxHeight = (CORE.Window.screenMax.height == 0) ? GLFW_DONT_CARE : CORE.Window.screenMax.height; - glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); -#endif -#if defined(PLATFORM_WEB) - // Trigger the resize event once to update the window minimum width and height - if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); -#endif -} - -// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) -void SetWindowMaxSize(int width, int height) -{ - CORE.Window.screenMax.width = width; - CORE.Window.screenMax.height = height; -#if defined(PLATFORM_DESKTOP) - int minWidth = (CORE.Window.screenMin.width == 0) ? GLFW_DONT_CARE : CORE.Window.screenMin.width; - int minHeight = (CORE.Window.screenMin.height == 0) ? GLFW_DONT_CARE : CORE.Window.screenMin.height; - int maxWidth = (CORE.Window.screenMax.width == 0) ? GLFW_DONT_CARE : CORE.Window.screenMax.width; - int maxHeight = (CORE.Window.screenMax.height == 0) ? GLFW_DONT_CARE : CORE.Window.screenMax.height; - glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); -#endif -#if defined(PLATFORM_WEB) - // Trigger the resize event once to update the window maximum width and height - if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); -#endif -} - -// Set window dimensions -void SetWindowSize(int width, int height) -{ -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - glfwSetWindowSize(CORE.Window.handle, width, height); -#endif -} - -// Set window opacity, value opacity is between 0.0 and 1.0 -void SetWindowOpacity(float opacity) -{ -#if defined(PLATFORM_DESKTOP) - if (opacity >= 1.0f) opacity = 1.0f; - else if (opacity <= 0.0f) opacity = 0.0f; - glfwSetWindowOpacity(CORE.Window.handle, opacity); -#endif -} - -// Set window focused -void SetWindowFocused(void) -{ -#if defined(PLATFORM_DESKTOP) - glfwFocusWindow(CORE.Window.handle); -#endif -} - // Get current screen width int GetScreenWidth(void) { @@ -1872,335 +433,6 @@ int GetRenderHeight(void) return CORE.Window.render.height; } -// Get native window handle -void *GetWindowHandle(void) -{ -#if defined(PLATFORM_DESKTOP) && defined(_WIN32) - // NOTE: Returned handle is: void *HWND (windows.h) - return glfwGetWin32Window(CORE.Window.handle); -#endif -#if defined(PLATFORM_DESKTOP) && defined(__linux__) - // NOTE: Returned handle is: unsigned long Window (X.h) - // typedef unsigned long XID; - // typedef XID Window; - //unsigned long id = (unsigned long)glfwGetX11Window(CORE.Window.handle); - //return NULL; // TODO: Find a way to return value... cast to void *? - return (void *)CORE.Window.handle; -#endif -#if defined(__APPLE__) - // NOTE: Returned handle is: (objc_object *) - return (void *)glfwGetCocoaWindow(CORE.Window.handle); -#endif - - return NULL; -} - -// Get number of monitors -int GetMonitorCount(void) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - glfwGetMonitors(&monitorCount); - return monitorCount; -#else - return 1; -#endif -} - -// Get number of monitors -int GetCurrentMonitor(void) -{ - int index = 0; - -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - GLFWmonitor *monitor = NULL; - - if (monitorCount > 1) - { - if (IsWindowFullscreen()) - { - // Get the handle of the monitor that the specified window is in full screen on - monitor = glfwGetWindowMonitor(CORE.Window.handle); - - for (int i = 0; i < monitorCount; i++) - { - if (monitors[i] == monitor) - { - index = i; - break; - } - } - } - else - { - int x = 0; - int y = 0; - - glfwGetWindowPos(CORE.Window.handle, &x, &y); - - for (int i = 0; i < monitorCount; i++) - { - int mx = 0; - int my = 0; - - monitor = monitors[i]; - glfwGetMonitorPos(monitor, &mx, &my); - const GLFWvidmode *mode = glfwGetVideoMode(monitor); - if (mode) - { - const int width = mode->width; - const int height = mode->height; - - if ((x >= mx) && - (x < (mx + width)) && - (y >= my) && - (y < (my + height))) - { - index = i; - break; - } - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); - } - } - } -#endif - - return index; -} - -// Get selected monitor position -Vector2 GetMonitorPosition(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - int x, y; - glfwGetMonitorPos(monitors[monitor], &x, &y); - - return (Vector2){ (float)x, (float)y }; - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif - return (Vector2){ 0, 0 }; -} - -// Get selected monitor width (currently used by monitor) -int GetMonitorWidth(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - - if (mode) return mode->width; - else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif -#if defined(PLATFORM_ANDROID) - if (CORE.Android.app->window != NULL) - { - return ANativeWindow_getWidth(CORE.Android.app->window); - } -#endif - return 0; -} - -// Get selected monitor height (currently used by monitor) -int GetMonitorHeight(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - - if (mode) return mode->height; - else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif -#if defined(PLATFORM_ANDROID) - if (CORE.Android.app->window != NULL) - { - return ANativeWindow_getHeight(CORE.Android.app->window); - } -#endif - return 0; -} - -// Get selected monitor physical width in millimetres -int GetMonitorPhysicalWidth(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - int physicalWidth; - glfwGetMonitorPhysicalSize(monitors[monitor], &physicalWidth, NULL); - return physicalWidth; - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif - return 0; -} - -// Get selected monitor physical height in millimetres -int GetMonitorPhysicalHeight(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - int physicalHeight; - glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &physicalHeight); - return physicalHeight; - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif - return 0; -} - -// Get selected monitor refresh rate -int GetMonitorRefreshRate(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - const GLFWvidmode *vidmode = glfwGetVideoMode(monitors[monitor]); - return vidmode->refreshRate; - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif -#if defined(PLATFORM_DRM) - if ((CORE.Window.connector) && (CORE.Window.modeIndex >= 0)) - { - return CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh; - } -#endif - return 0; -} - -// Get window position XY on monitor -Vector2 GetWindowPosition(void) -{ - int x = 0; - int y = 0; -#if defined(PLATFORM_DESKTOP) - glfwGetWindowPos(CORE.Window.handle, &x, &y); -#endif - return (Vector2){ (float)x, (float)y }; -} - -// Get window scale DPI factor for current monitor -Vector2 GetWindowScaleDPI(void) -{ - Vector2 scale = { 1.0f, 1.0f }; - -#if defined(PLATFORM_DESKTOP) - float xdpi = 1.0; - float ydpi = 1.0; - Vector2 windowPos = GetWindowPosition(); - - int monitorCount = 0; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - // Check window monitor - for (int i = 0; i < monitorCount; i++) - { - glfwGetMonitorContentScale(monitors[i], &xdpi, &ydpi); - - int xpos, ypos, width, height; - glfwGetMonitorWorkarea(monitors[i], &xpos, &ypos, &width, &height); - - if ((windowPos.x >= xpos) && (windowPos.x < xpos + width) && - (windowPos.y >= ypos) && (windowPos.y < ypos + height)) - { - scale.x = xdpi; - scale.y = ydpi; - break; - } - } -#endif - - return scale; -} - -// Get the human-readable, UTF-8 encoded name of the selected monitor -const char *GetMonitorName(int monitor) -{ -#if defined(PLATFORM_DESKTOP) - int monitorCount; - GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - - if ((monitor >= 0) && (monitor < monitorCount)) - { - return glfwGetMonitorName(monitors[monitor]); - } - else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); -#endif - return ""; -} - -// Set clipboard text content -void SetClipboardText(const char *text) -{ -#if defined(PLATFORM_DESKTOP) - glfwSetClipboardString(CORE.Window.handle, text); -#endif -#if defined(PLATFORM_WEB) - // Security check to (partially) avoid malicious code - if (strchr(text, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided Clipboard could be potentially malicious, avoid [\'] character"); - else EM_ASM( { navigator.clipboard.writeText(UTF8ToString($0)); }, text); -#endif -} - -// Get clipboard text content -// NOTE: returned string is allocated and freed by GLFW -const char *GetClipboardText(void) -{ -#if defined(PLATFORM_DESKTOP) - return glfwGetClipboardString(CORE.Window.handle); -#endif -#if defined(PLATFORM_WEB) -/* - // Accessing clipboard data from browser is tricky due to security reasons - // The method to use is navigator.clipboard.readText() but this is an asynchronous method - // that will return at some moment after the function is called with the required data - emscripten_run_script_string("navigator.clipboard.readText() \ - .then(text => { document.getElementById('clipboard').innerText = text; console.log('Pasted content: ', text); }) \ - .catch(err => { console.error('Failed to read clipboard contents: ', err); });" - ); - - // The main issue is getting that data, one approach could be using ASYNCIFY and wait - // for the data but it requires adding Asyncify emscripten library on compilation - - // Another approach could be just copy the data in a HTML text field and try to retrieve it - // later on if available... and clean it for future accesses -*/ - return NULL; -#endif - return NULL; -} - // Enable waiting for events on EndDrawing(), no automatic event polling void EnableEventWaiting(void) { @@ -2213,62 +445,12 @@ void DisableEventWaiting(void) CORE.Window.eventWaiting = false; } -// Show mouse cursor -void ShowCursor(void) -{ -#if defined(PLATFORM_DESKTOP) - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); -#endif - - CORE.Input.Mouse.cursorHidden = false; -} - -// Hides mouse cursor -void HideCursor(void) -{ -#if defined(PLATFORM_DESKTOP) - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); -#endif - - CORE.Input.Mouse.cursorHidden = true; -} - // Check if cursor is not visible bool IsCursorHidden(void) { return CORE.Input.Mouse.cursorHidden; } -// Enables cursor (unlock cursor) -void EnableCursor(void) -{ -#if defined(PLATFORM_DESKTOP) - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); -#endif -#if defined(PLATFORM_WEB) - emscripten_exit_pointerlock(); -#endif - // Set cursor position in the middle - SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); - - CORE.Input.Mouse.cursorHidden = false; -} - -// Disables cursor (lock cursor) -void DisableCursor(void) -{ -#if defined(PLATFORM_DESKTOP) - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED); -#endif -#if defined(PLATFORM_WEB) - emscripten_request_pointerlock("#canvas", 1); -#endif - // Set cursor position in the middle - SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); - - CORE.Input.Mouse.cursorHidden = true; -} - // Check if cursor is on the current screen. bool IsCursorOnScreen(void) { @@ -2674,7 +856,7 @@ VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device) // Unload VR stereo config properties void UnloadVrStereoConfig(VrStereoConfig config) { - //... + TRACELOG(LOG_INFO, "UnloadVrStereoConfig not implemented in rcore.c"); } // Load shader from files and bind default locations @@ -2952,7 +1134,7 @@ Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int heigh } else if (camera.projection == CAMERA_ORTHOGRAPHIC) { - double aspect = ((double)width/(double)height); + float aspect = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height; double top = camera.fovy/2.0; double right = top*aspect; @@ -2963,6 +1145,8 @@ Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int heigh // Calculate view matrix from camera look at (and transpose it) Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up); + // TODO: Why not use Vector3Transform(Vector3 v, Matrix mat)? + // Convert world position vector to quaternion Quaternion worldPos = { position.x, position.y, position.z, 1.0f }; @@ -3047,26 +1231,6 @@ float GetFrameTime(void) return (float)CORE.Time.frame; } -// Get elapsed time measure in seconds since InitTimer() -// NOTE: On PLATFORM_DESKTOP InitTimer() is called on InitWindow() -// NOTE: On PLATFORM_DESKTOP, timer is initialized on glfwInit() -double GetTime(void) -{ - double time = 0.0; -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - time = glfwGetTime(); // Elapsed time since glfwInit() -#endif - -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) - struct timespec ts = { 0 }; - clock_gettime(CLOCK_MONOTONIC, &ts); - unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; - - time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer() -#endif - return time; -} - // Setup window configuration flags (view FLAGS) // NOTE: This function is expected to be called before window creation, // because it sets up some flags for the window creation process. @@ -3078,63 +1242,9 @@ void SetConfigFlags(unsigned int flags) CORE.Window.flags |= flags; } -// NOTE TRACELOG() function is located in [utils.h] - -// Takes a screenshot of current screen (saved a .png) -void TakeScreenshot(const char *fileName) -{ -#if defined(SUPPORT_MODULE_RTEXTURES) - // Security check to (partially) avoid malicious code on PLATFORM_WEB - if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } - - Vector2 scale = GetWindowScaleDPI(); - unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); - Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - - char path[2048] = { 0 }; - strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); - - ExportImage(image, path); // WARNING: Module required: rtextures - RL_FREE(imgData); - -#if defined(PLATFORM_WEB) - // Download file from MEMFS (emscripten memory filesystem) - // saveFileFromMEMFSToDisk() function is defined in raylib/src/shell.html - emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", GetFileName(path), GetFileName(path))); -#endif - - TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); -#else - TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); -#endif -} - -// Get a random value between min and max (both included) -// WARNING: Ranges higher than RAND_MAX will return invalid results -// More specifically, if (max - min) > INT_MAX there will be an overflow, -// and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold -int GetRandomValue(int min, int max) -{ - if (min > max) - { - int tmp = max; - max = min; - min = tmp; - } - - if ((unsigned int)(max - min) > (unsigned int)RAND_MAX) - { - TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); - } - - return (rand()%(abs(max - min) + 1) + min); -} - -// Set the seed for the random number generator -void SetRandomSeed(unsigned int seed) -{ - srand(seed); -} +//---------------------------------------------------------------------------------- +// Module Functions Definition: FileSystem +//---------------------------------------------------------------------------------- // Check if the file exists bool FileExists(const char *fileName) @@ -3290,7 +1400,7 @@ const char *GetFileNameWithoutExt(const char *filePath) // Get directory for a given filePath const char *GetDirectoryPath(const char *filePath) { -/* + /* // NOTE: Directory separator is different in Windows and other platforms, // fortunately, Windows also support the '/' separator, that's the one should be used #if defined(_WIN32) @@ -3298,7 +1408,7 @@ const char *GetDirectoryPath(const char *filePath) #else char separator = '/'; #endif -*/ + */ const char *lastSlash = NULL; static char dirPath[MAX_FILEPATH_LENGTH] = { 0 }; memset(dirPath, 0, MAX_FILEPATH_LENGTH); @@ -3325,8 +1435,10 @@ const char *GetDirectoryPath(const char *filePath) else { // NOTE: Be careful, strncpy() is not safe, it does not care about '\0' - memcpy(dirPath + (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/' ? 2 : 0), filePath, strlen(filePath) - (strlen(lastSlash) - 1)); - dirPath[strlen(filePath) - strlen(lastSlash) + (filePath[1] != ':' && filePath[0] != '\\' && filePath[0] != '/' ? 2 : 0)] = '\0'; // Add '\0' manually + unsigned char *dirPathPtr = dirPath; + if ((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/')) dirPathPtr += 2; // Skip drive letter, "C:" + memcpy(dirPathPtr, filePath, strlen(filePath) - (strlen(lastSlash) - 1)); + dirPath[strlen(filePath) - strlen(lastSlash) + ((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/'))? 2 : 0] = '\0'; // Add '\0' manually } } @@ -3578,6 +1690,37 @@ long GetFileModTime(const char *fileName) return 0; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + +// Get a random value between min and max (both included) +// WARNING: Ranges higher than RAND_MAX will return invalid results +// More specifically, if (max - min) > INT_MAX there will be an overflow, +// and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold +int GetRandomValue(int min, int max) +{ + if (min > max) + { + int tmp = max; + max = min; + min = tmp; + } + + if ((unsigned int)(max - min) > (unsigned int)RAND_MAX) + { + TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); + } + + return (rand()%(abs(max - min) + 1) + min); +} + +// Set the seed for the random number generator +void SetRandomSeed(unsigned int seed) +{ + srand(seed); +} + // Compress data (DEFLATE algorithm) unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize) { @@ -3588,12 +1731,12 @@ unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDa #if defined(SUPPORT_COMPRESSION_API) // Compress data and generate a valid DEFLATE stream struct sdefl *sdefl = RL_CALLOC(1, sizeof(struct sdefl)); // WARNING: Possible stack overflow, struct sdefl is almost 1MB - int bounds = dataSize*2;//sdefl_bound(dataSize); + int bounds = sdefl_bound(dataSize); compData = (unsigned char *)RL_CALLOC(bounds, 1); - + *compDataSize = sdeflate(sdefl, compData, data, dataSize, COMPRESSION_QUALITY_DEFLATE); // Compression level 8, same as stbiw RL_FREE(sdefl); - + TRACELOG(LOG_INFO, "SYSTEM: Compress data: Original size: %i -> Comp. size: %i", dataSize, *compDataSize); #endif @@ -3719,67 +1862,31 @@ unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize) return decodedData; } -// Open URL with default system browser (if available) -// NOTE: This function is only safe to use if you control the URL given. -// A user could craft a malicious string performing another action. -// Only call this function yourself not with user input or make sure to check the string yourself. -// Ref: https://github.com/raysan5/raylib/issues/686 -void OpenURL(const char *url) -{ - // Security check to (partially) avoid malicious code on PLATFORM_WEB - if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); - else - { -#if defined(PLATFORM_DESKTOP) - char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char)); - #if defined(_WIN32) - sprintf(cmd, "explorer \"%s\"", url); - #endif - #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) - sprintf(cmd, "xdg-open '%s'", url); // Alternatives: firefox, x-www-browser - #endif - #if defined(__APPLE__) - sprintf(cmd, "open '%s'", url); - #endif - int result = system(cmd); - if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created"); - RL_FREE(cmd); -#endif -#if defined(PLATFORM_WEB) - emscripten_run_script(TextFormat("window.open('%s', '_blank')", url)); -#endif -#if defined(PLATFORM_ANDROID) - JNIEnv *env = NULL; - JavaVM *vm = CORE.Android.app->activity->vm; - (*vm)->AttachCurrentThread(vm, &env, NULL); - - jstring urlString = (*env)->NewStringUTF(env, url); - jclass uriClass = (*env)->FindClass(env, "android/net/Uri"); - jmethodID uriParse = (*env)->GetStaticMethodID(env, uriClass, "parse", "(Ljava/lang/String;)Landroid/net/Uri;"); - jobject uri = (*env)->CallStaticObjectMethod(env, uriClass, uriParse, urlString); - - jclass intentClass = (*env)->FindClass(env, "android/content/Intent"); - jfieldID actionViewId = (*env)->GetStaticFieldID(env, intentClass, "ACTION_VIEW", "Ljava/lang/String;"); - jobject actionView = (*env)->GetStaticObjectField(env, intentClass, actionViewId); - jmethodID newIntent = (*env)->GetMethodID(env, intentClass, "", "(Ljava/lang/String;Landroid/net/Uri;)V"); - jobject intent = (*env)->AllocObject(env, intentClass); - - (*env)->CallVoidMethod(env, intent, newIntent, actionView, uri); - jclass activityClass = (*env)->FindClass(env, "android/app/Activity"); - jmethodID startActivity = (*env)->GetMethodID(env, activityClass, "startActivity", "(Landroid/content/Intent;)V"); - (*env)->CallVoidMethod(env, CORE.Android.app->activity->clazz, startActivity, intent); - - (*vm)->DetachCurrentThread(vm); -#endif - } -} - //---------------------------------------------------------------------------------- -// Module Functions Definition - Input (Keyboard, Mouse, Gamepad) Functions +// Module Functions Definition: Inputs //---------------------------------------------------------------------------------- + +// Platform-specific functions +//void SetExitKey(int key) +//const char *GetGamepadName(int gamepad) +//int GetGamepadAxisCount(int gamepad) +//int SetGamepadMappings(const char *mappings) +//int GetMouseX(void) +//int GetMouseY(void) +//Vector2 GetMousePosition(void) +//void SetMousePosition(int x, int y) +//float GetMouseWheelMove(void) +//void SetMouseCursor(int cursor) +//int GetTouchX(void) +//int GetTouchY(void) +//Vector2 GetTouchPosition(int index) +//void SwapScreenBuffer(void) +//void PollInputEvents(void) + // Check if a key has been pressed once bool IsKeyPressed(int key) { + bool pressed = false; if ((key > 0) && (key < MAX_KEYBOARD_KEYS)) @@ -3886,17 +1993,6 @@ int GetCharPressed(void) return value; } -// Set a custom key to exit program -// NOTE: default exitKey is ESCAPE -void SetExitKey(int key) -{ -#if !defined(PLATFORM_ANDROID) - CORE.Input.Keyboard.exitKey = key; -#endif -} - -// NOTE: Gamepad support not implemented in emscripten GLFW3 (PLATFORM_WEB) - // Check if a gamepad is available bool IsGamepadAvailable(int gamepad) { @@ -3907,40 +2003,6 @@ bool IsGamepadAvailable(int gamepad) return result; } -// Get gamepad internal name id -const char *GetGamepadName(int gamepad) -{ - const char *name = NULL; - -#if defined(PLATFORM_DESKTOP) - if (CORE.Input.Gamepad.ready[gamepad]) name = glfwGetJoystickName(gamepad); -#endif -#if defined(PLATFORM_DRM) - if (CORE.Input.Gamepad.ready[gamepad]) - { - ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); - name = CORE.Input.Gamepad.name[gamepad]; - } -#endif -#if defined(PLATFORM_WEB) - name = CORE.Input.Gamepad.name[gamepad]; -#endif - - return name; -} - -// Get gamepad axis count -int GetGamepadAxisCount(int gamepad) -{ -#if defined(PLATFORM_DRM) - int axisCount = 0; - if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGAXES, &axisCount); - CORE.Input.Gamepad.axisCount = axisCount; -#endif - - return CORE.Input.Gamepad.axisCount; -} - // Get axis movement vector for a gamepad float GetGamepadAxisMovement(int gamepad, int axis) { @@ -4002,18 +2064,6 @@ int GetGamepadButtonPressed(void) return CORE.Input.Gamepad.lastButtonPressed; } -// Set internal gamepad mappings -int SetGamepadMappings(const char *mappings) -{ - int result = 0; - -#if defined(PLATFORM_DESKTOP) - result = glfwUpdateGamepadMappings(mappings); -#endif - - return result; -} - // Check if a mouse button has been pressed once bool IsMouseButtonPressed(int button) { @@ -4066,44 +2116,6 @@ bool IsMouseButtonUp(int button) return up; } -// Get mouse position X -int GetMouseX(void) -{ -#if defined(PLATFORM_ANDROID) - return (int)CORE.Input.Touch.position[0].x; -#else - return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); -#endif -} - -// Get mouse position Y -int GetMouseY(void) -{ -#if defined(PLATFORM_ANDROID) - return (int)CORE.Input.Touch.position[0].y; -#else - return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); -#endif -} - -// Get mouse position XY -Vector2 GetMousePosition(void) -{ - Vector2 position = { 0 }; - - // TODO: Review touch position on PLATFORM_WEB - -#if defined(PLATFORM_ANDROID) //|| defined(PLATFORM_WEB) - position = GetTouchPosition(0); -#else - // NOTE: On PLATFORM_WEB, even on canvas scaling, mouse position is proportionally returned - position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; - position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; -#endif - - return position; -} - // Get mouse delta between frames Vector2 GetMouseDelta(void) { @@ -4115,18 +2127,6 @@ Vector2 GetMouseDelta(void) return delta; } -// Set mouse position XY -void SetMousePosition(int x, int y) -{ - CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; - CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - // NOTE: emscripten not implemented - glfwSetCursorPos(CORE.Window.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); -#endif -} - // Set mouse offset // NOTE: Useful when rendering to different size targets void SetMouseOffset(int offsetX, int offsetY) @@ -4141,19 +2141,6 @@ void SetMouseScale(float scaleX, float scaleY) CORE.Input.Mouse.scale = (Vector2){ scaleX, scaleY }; } -// Get mouse wheel movement Y -float GetMouseWheelMove(void) -{ - float result = 0.0f; - -#if !defined(PLATFORM_ANDROID) - if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x; - else result = (float)CORE.Input.Mouse.currentWheelMove.y; -#endif - - return result; -} - // Get mouse wheel movement X/Y as a vector Vector2 GetMouseWheelMoveV(void) { @@ -4164,61 +2151,6 @@ Vector2 GetMouseWheelMoveV(void) return result; } -// Set mouse cursor -// NOTE: This is a no-op on platforms other than PLATFORM_DESKTOP -void SetMouseCursor(int cursor) -{ -#if defined(PLATFORM_DESKTOP) - CORE.Input.Mouse.cursor = cursor; - if (cursor == MOUSE_CURSOR_DEFAULT) glfwSetCursor(CORE.Window.handle, NULL); - else - { - // NOTE: We are relating internal GLFW enum values to our MouseCursor enum values - glfwSetCursor(CORE.Window.handle, glfwCreateStandardCursor(0x00036000 + cursor)); - } -#endif -} - -// Get touch position X for touch point 0 (relative to screen size) -int GetTouchX(void) -{ -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) - return (int)CORE.Input.Touch.position[0].x; -#else // PLATFORM_DESKTOP, PLATFORM_DRM - return GetMouseX(); -#endif -} - -// Get touch position Y for touch point 0 (relative to screen size) -int GetTouchY(void) -{ -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) - return (int)CORE.Input.Touch.position[0].y; -#else // PLATFORM_DESKTOP, PLATFORM_DRM - return GetMouseY(); -#endif -} - -// Get touch position XY for a touch point index (relative to screen size) -// TODO: Touch position should be scaled depending on display size and render size -Vector2 GetTouchPosition(int index) -{ - Vector2 position = { -1.0f, -1.0f }; - -#if defined(PLATFORM_DESKTOP) - // TODO: GLFW does not support multi-touch input just yet - // https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch - // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages - if (index == 0) position = GetMousePosition(); -#endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_WEB) || defined(PLATFORM_DRM) - if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index]; - else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS); -#endif - - return position; -} - // Get touch point identifier for given index int GetTouchPointId(int index) { @@ -4236,740 +2168,14 @@ int GetTouchPointCount(void) } //---------------------------------------------------------------------------------- -// Module specific Functions Definition +// Module Internal Functions Definition //---------------------------------------------------------------------------------- -// Initialize display device and framebuffer -// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size -// If width or height are 0, default display size will be used for framebuffer size -// NOTE: returns false in case graphic device could not be created -static bool InitGraphicsDevice(int width, int height) -{ - CORE.Window.screen.width = width; // User desired width - CORE.Window.screen.height = height; // User desired height - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - - // Set the screen minimum and maximum default values to 0 - CORE.Window.screenMin.width = 0; - CORE.Window.screenMin.height = 0; - CORE.Window.screenMax.width = 0; - CORE.Window.screenMax.height = 0; - - // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... - // ...in top-down or left-right to match display aspect ratio (no weird scaling) - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - glfwSetErrorCallback(ErrorCallback); -/* - // TODO: Setup GLFW custom allocators to match raylib ones - const GLFWallocator allocator = { - .allocate = MemAlloc, - .deallocate = MemFree, - .reallocate = MemRealloc, - .user = NULL - }; - - glfwInitAllocator(&allocator); -*/ -#if defined(__APPLE__) - glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE); -#endif - - if (!glfwInit()) - { - TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); - return false; - } - - glfwDefaultWindowHints(); // Set default windows hints - //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits - //glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits - //glfwWindowHint(GLFW_BLUE_BITS, 8); // Framebuffer blue color component bits - //glfwWindowHint(GLFW_ALPHA_BITS, 8); // Framebuffer alpha color component bits - //glfwWindowHint(GLFW_DEPTH_BITS, 24); // Depthbuffer bits - //glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window - //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API - //glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers - - // Check window creation flags - if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true; - - if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window - else glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); // Window initially hidden - - if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window - else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); // Decorated window - - if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window - else glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); // Avoid window being resizable - - // Disable FLAG_WINDOW_MINIMIZED, not supported on initialization - if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; - - // Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization - if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; - - if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE); - else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE); - - if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE); - else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE); - - // NOTE: Some GLFW flags are not supported on HTML5 -#if defined(PLATFORM_DESKTOP) - if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); // Transparent framebuffer - else glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_FALSE); // Opaque framebuffer - - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // Resize window content area based on the monitor content scale. - // NOTE: This hint only has an effect on platforms where screen coordinates and pixels always map 1:1 such as Windows and X11. - // On platforms like macOS the resolution of the framebuffer is changed independently of the window size. - glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); // Scale content area based on the monitor content scale where window is placed on - #if defined(__APPLE__) - glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE); - #endif - } - else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE); - - // Mouse passthrough - if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); - else glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); -#endif - - if (CORE.Window.flags & FLAG_MSAA_4X_HINT) - { - // NOTE: MSAA is only enabled for main framebuffer, not user-created FBOs - TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4"); - glfwWindowHint(GLFW_SAMPLES, 4); // Tries to enable multisampling x4 (MSAA), default is 0 - } - - // NOTE: When asking for an OpenGL context version, most drivers provide the highest supported version - // with backward compatibility to older OpenGL versions. - // For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context. - - // Check selection OpenGL version - if (rlGetVersion() == RL_OPENGL_21) - { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); // Choose OpenGL major version (just hint) - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); // Choose OpenGL minor version (just hint) - } - else if (rlGetVersion() == RL_OPENGL_33) - { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint) - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above! - // Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE -#if defined(__APPLE__) - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); // OSX Requires forward compatibility -#else - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Forward Compatibility Hint: Only 3.3 and above! -#endif - //glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context - } - else if (rlGetVersion() == RL_OPENGL_43) - { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Choose OpenGL major version (just hint) - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); -#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) - glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Enable OpenGL Debug Context -#endif - } - else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context - { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); -#if defined(PLATFORM_DESKTOP) - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); -#else - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); -#endif - } - else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context - { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); - glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); -#if defined(PLATFORM_DESKTOP) - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); -#else - glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); -#endif - } - -#if defined(PLATFORM_DESKTOP) - // NOTE: GLFW 3.4+ defers initialization of the Joystick subsystem on the first call to any Joystick related functions. - // Forcing this initialization here avoids doing it on PollInputEvents() called by EndDrawing() after first frame has been just drawn. - // The initialization will still happen and possible delays still occur, but before the window is shown, which is a nicer experience. - // REF: https://github.com/raysan5/raylib/issues/1554 - if (MAX_GAMEPADS > 0) glfwSetJoystickCallback(NULL); -#endif - -#if defined(PLATFORM_DESKTOP) - // Find monitor resolution - GLFWmonitor *monitor = glfwGetPrimaryMonitor(); - if (!monitor) - { - TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor"); - return false; - } - - const GLFWvidmode *mode = glfwGetVideoMode(monitor); - - CORE.Window.display.width = mode->width; - CORE.Window.display.height = mode->height; - - // Set screen width/height to the display width/height if they are 0 - if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width; - if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height; -#endif // PLATFORM_DESKTOP - -#if defined(PLATFORM_WEB) - // NOTE: Getting video modes is not implemented in emscripten GLFW3 version - CORE.Window.display.width = CORE.Window.screen.width; - CORE.Window.display.height = CORE.Window.screen.height; -#endif // PLATFORM_WEB - - if (CORE.Window.fullscreen) - { - // remember center for switchinging from fullscreen to window - if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width)) - { - // If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed. - // Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11. - CORE.Window.position.x = CORE.Window.display.width/4; - CORE.Window.position.y = CORE.Window.display.height/4; - } - else - { - CORE.Window.position.x = CORE.Window.display.width/2 - CORE.Window.screen.width/2; - CORE.Window.position.y = CORE.Window.display.height/2 - CORE.Window.screen.height/2; - } - - if (CORE.Window.position.x < 0) CORE.Window.position.x = 0; - if (CORE.Window.position.y < 0) CORE.Window.position.y = 0; - - // Obtain recommended CORE.Window.display.width/CORE.Window.display.height from a valid videomode for the monitor - int count = 0; - const GLFWvidmode *modes = glfwGetVideoModes(glfwGetPrimaryMonitor(), &count); - - // Get closest video mode to desired CORE.Window.screen.width/CORE.Window.screen.height - for (int i = 0; i < count; i++) - { - if ((unsigned int)modes[i].width >= CORE.Window.screen.width) - { - if ((unsigned int)modes[i].height >= CORE.Window.screen.height) - { - CORE.Window.display.width = modes[i].width; - CORE.Window.display.height = modes[i].height; - break; - } - } - } - TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height); - - // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example, - // for a desired screen size of 800x450 (16:9), closest supported videomode is 800x600 (4:3), - // framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched - // by the sides to fit all monitor space... - - // Try to setup the most appropriate fullscreen framebuffer for the requested screenWidth/screenHeight - // It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset) - // Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale - // TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed... - // HighDPI monitors are properly considered in a following similar function: SetupViewport() - SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); - - CORE.Window.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL); - - // NOTE: Full-screen change, not working properly... - //glfwSetWindowMonitor(CORE.Window.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); - } - else - { -#if defined(PLATFORM_DESKTOP) - // If we are windowed fullscreen, ensures that window does not minimize when focus is lost - if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width)) - { - glfwWindowHint(GLFW_AUTO_ICONIFY, 0); - } -#endif - // No-fullscreen window creation - CORE.Window.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); - - if (CORE.Window.handle) - { - CORE.Window.render.width = CORE.Window.screen.width; - CORE.Window.render.height = CORE.Window.screen.height; - } - } - - if (!CORE.Window.handle) - { - glfwTerminate(); - TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); - return false; - } - -// glfwCreateWindow title doesn't work with emscripten. -#if defined(PLATFORM_WEB) - emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " "); -#endif - - // Set window callback events - glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! -#if !defined(PLATFORM_WEB) - glfwSetWindowMaximizeCallback(CORE.Window.handle, WindowMaximizeCallback); -#endif - glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback); - glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback); - glfwSetDropCallback(CORE.Window.handle, WindowDropCallback); - - // Set input callback events - glfwSetKeyCallback(CORE.Window.handle, KeyCallback); - glfwSetCharCallback(CORE.Window.handle, CharCallback); - glfwSetMouseButtonCallback(CORE.Window.handle, MouseButtonCallback); - glfwSetCursorPosCallback(CORE.Window.handle, MouseCursorPosCallback); // Track mouse position changes - glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback); - glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback); - - glfwMakeContextCurrent(CORE.Window.handle); - -#if !defined(PLATFORM_WEB) - glfwSetInputMode(CORE.Window.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) - - glfwSwapInterval(0); // No V-Sync by default -#endif - - // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) - // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need - // to be activated on web platforms since VSync is enforced there. -#if !defined(PLATFORM_WEB) - if (CORE.Window.flags & FLAG_VSYNC_HINT) - { - // WARNING: It seems to hit a critical render path in Intel HD Graphics - glfwSwapInterval(1); - TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC"); - } -#endif - - int fbWidth = CORE.Window.screen.width; - int fbHeight = CORE.Window.screen.height; - -#if defined(PLATFORM_DESKTOP) - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling. - // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE); - #if !defined(__APPLE__) - glfwGetFramebufferSize(CORE.Window.handle, &fbWidth, &fbHeight); - - // Screen scaling matrix is required in case desired screen area is different from display area - CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f); - - // Mouse input scaling for the new screen size - SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight); - #endif - } -#endif - - CORE.Window.render.width = fbWidth; - CORE.Window.render.height = fbHeight; - CORE.Window.currentFbo.width = fbWidth; - CORE.Window.currentFbo.height = fbHeight; - - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); - TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); - TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); - TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); - TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); - -#endif // PLATFORM_DESKTOP || PLATFORM_WEB - -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) - CORE.Window.fullscreen = true; - CORE.Window.flags |= FLAG_FULLSCREEN_MODE; - -#if defined(PLATFORM_DRM) - CORE.Window.fd = -1; - CORE.Window.connector = NULL; - CORE.Window.modeIndex = -1; - CORE.Window.crtc = NULL; - CORE.Window.gbmDevice = NULL; - CORE.Window.gbmSurface = NULL; - CORE.Window.prevBO = NULL; - CORE.Window.prevFB = 0; - -#if defined(DEFAULT_GRAPHIC_DEVICE_DRM) - CORE.Window.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR); -#else - TRACELOG(LOG_INFO, "DISPLAY: No graphic card set, trying platform-gpu-card"); - CORE.Window.fd = open("/dev/dri/by-path/platform-gpu-card", O_RDWR); // VideoCore VI (Raspberry Pi 4) - - if ((-1 == CORE.Window.fd) || (drmModeGetResources(CORE.Window.fd) == NULL)) - { - TRACELOG(LOG_INFO, "DISPLAY: Failed to open platform-gpu-card, trying card1"); - CORE.Window.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded - } - - if ((-1 == CORE.Window.fd) || (drmModeGetResources(CORE.Window.fd) == NULL)) - { - TRACELOG(LOG_INFO, "DISPLAY: Failed to open graphic card1, trying card0"); - CORE.Window.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3) - } -#endif - if (-1 == CORE.Window.fd) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card"); - return false; - } - - drmModeRes *res = drmModeGetResources(CORE.Window.fd); - if (!res) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed get DRM resources"); - return false; - } - - TRACELOG(LOG_TRACE, "DISPLAY: Connectors found: %i", res->count_connectors); - for (size_t i = 0; i < res->count_connectors; i++) - { - TRACELOG(LOG_TRACE, "DISPLAY: Connector index %i", i); - drmModeConnector *con = drmModeGetConnector(CORE.Window.fd, res->connectors[i]); - TRACELOG(LOG_TRACE, "DISPLAY: Connector modes detected: %i", con->count_modes); - if ((con->connection == DRM_MODE_CONNECTED) && (con->encoder_id)) - { - TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected"); - CORE.Window.connector = con; - break; - } - else - { - TRACELOG(LOG_TRACE, "DISPLAY: DRM mode NOT connected (deleting)"); - drmModeFreeConnector(con); - } - } - - if (!CORE.Window.connector) - { - TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found"); - drmModeFreeResources(res); - return false; - } - - drmModeEncoder *enc = drmModeGetEncoder(CORE.Window.fd, CORE.Window.connector->encoder_id); - if (!enc) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder"); - drmModeFreeResources(res); - return false; - } - - CORE.Window.crtc = drmModeGetCrtc(CORE.Window.fd, enc->crtc_id); - if (!CORE.Window.crtc) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc"); - drmModeFreeEncoder(enc); - drmModeFreeResources(res); - return false; - } - - // If InitWindow should use the current mode find it in the connector's mode list - if ((CORE.Window.screen.width <= 0) || (CORE.Window.screen.height <= 0)) - { - TRACELOG(LOG_TRACE, "DISPLAY: Selecting DRM connector mode for current used mode..."); - - CORE.Window.modeIndex = FindMatchingConnectorMode(CORE.Window.connector, &CORE.Window.crtc->mode); - - if (CORE.Window.modeIndex < 0) - { - TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found"); - drmModeFreeEncoder(enc); - drmModeFreeResources(res); - return false; - } - - CORE.Window.screen.width = CORE.Window.display.width; - CORE.Window.screen.height = CORE.Window.display.height; - } - - const bool allowInterlaced = CORE.Window.flags & FLAG_INTERLACED_HINT; - const int fps = (CORE.Time.target > 0) ? (1.0/CORE.Time.target) : 60; - - // Try to find an exact matching mode - CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced); - - // If nothing found, try to find a nearly matching mode - if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced); - - // If nothing found, try to find an exactly matching mode including interlaced - if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true); - - // If nothing found, try to find a nearly matching mode including interlaced - if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true); - - // If nothing found, there is no suitable mode - if (CORE.Window.modeIndex < 0) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode"); - drmModeFreeEncoder(enc); - drmModeFreeResources(res); - return false; - } - - CORE.Window.display.width = CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay; - CORE.Window.display.height = CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay; - - TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u)", CORE.Window.connector->modes[CORE.Window.modeIndex].name, - CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, - (CORE.Window.connector->modes[CORE.Window.modeIndex].flags & DRM_MODE_FLAG_INTERLACE) ? 'i' : 'p', - CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh); - - // Use the width and height of the surface for render - CORE.Window.render.width = CORE.Window.screen.width; - CORE.Window.render.height = CORE.Window.screen.height; - - drmModeFreeEncoder(enc); - enc = NULL; - - drmModeFreeResources(res); - res = NULL; - - CORE.Window.gbmDevice = gbm_create_device(CORE.Window.fd); - if (!CORE.Window.gbmDevice) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM device"); - return false; - } - - CORE.Window.gbmSurface = gbm_surface_create(CORE.Window.gbmDevice, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, - CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); - if (!CORE.Window.gbmSurface) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM surface"); - return false; - } -#endif - - EGLint samples = 0; - EGLint sampleBuffer = 0; - if (CORE.Window.flags & FLAG_MSAA_4X_HINT) - { - samples = 4; - sampleBuffer = 1; - TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4"); - } - - const EGLint framebufferAttribs[] = - { - EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, // Type of context support -#if defined(PLATFORM_DRM) - EGL_SURFACE_TYPE, EGL_WINDOW_BIT, // Don't use it on Android! -#endif - EGL_RED_SIZE, 8, // RED color bit depth (alternative: 5) - EGL_GREEN_SIZE, 8, // GREEN color bit depth (alternative: 6) - EGL_BLUE_SIZE, 8, // BLUE color bit depth (alternative: 5) -#if defined(PLATFORM_DRM) - EGL_ALPHA_SIZE, 8, // ALPHA bit depth (required for transparent framebuffer) -#endif - //EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI) - EGL_DEPTH_SIZE, 16, // Depth buffer size (Required to use Depth testing!) - //EGL_STENCIL_SIZE, 8, // Stencil buffer size - EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA - EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs) - EGL_NONE - }; - - const EGLint contextAttribs[] = - { - EGL_CONTEXT_CLIENT_VERSION, 2, - EGL_NONE - }; - -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) - EGLint numConfigs = 0; - - // Get an EGL device connection -#if defined(PLATFORM_DRM) - CORE.Window.device = eglGetDisplay((EGLNativeDisplayType)CORE.Window.gbmDevice); -#else - CORE.Window.device = eglGetDisplay(EGL_DEFAULT_DISPLAY); -#endif - if (CORE.Window.device == EGL_NO_DISPLAY) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); - return false; - } - - // Initialize the EGL device connection - if (eglInitialize(CORE.Window.device, NULL, NULL) == EGL_FALSE) - { - // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred. - TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); - return false; - } - -#if defined(PLATFORM_DRM) - if (!eglChooseConfig(CORE.Window.device, NULL, NULL, 0, &numConfigs)) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError()); - return false; - } - - TRACELOG(LOG_TRACE, "DISPLAY: EGL configs available: %d", numConfigs); - - EGLConfig *configs = RL_CALLOC(numConfigs, sizeof(*configs)); - if (!configs) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to get memory for EGL configs"); - return false; - } - - EGLint matchingNumConfigs = 0; - if (!eglChooseConfig(CORE.Window.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs)) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError()); - free(configs); - return false; - } - - TRACELOG(LOG_TRACE, "DISPLAY: EGL matching configs available: %d", matchingNumConfigs); - - // find the EGL config that matches the previously setup GBM format - int found = 0; - for (EGLint i = 0; i < matchingNumConfigs; ++i) - { - EGLint id = 0; - if (!eglGetConfigAttrib(CORE.Window.device, configs[i], EGL_NATIVE_VISUAL_ID, &id)) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config attribute: 0x%x", eglGetError()); - continue; - } - - if (GBM_FORMAT_ARGB8888 == id) - { - TRACELOG(LOG_TRACE, "DISPLAY: Using EGL config: %d", i); - CORE.Window.config = configs[i]; - found = 1; - break; - } - } - - RL_FREE(configs); - - if (!found) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable EGL config"); - return false; - } -#else - // Get an appropriate EGL framebuffer configuration - eglChooseConfig(CORE.Window.device, framebufferAttribs, &CORE.Window.config, 1, &numConfigs); -#endif - - // Set rendering API - eglBindAPI(EGL_OPENGL_ES_API); - - // Create an EGL rendering context - CORE.Window.context = eglCreateContext(CORE.Window.device, CORE.Window.config, EGL_NO_CONTEXT, contextAttribs); - if (CORE.Window.context == EGL_NO_CONTEXT) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); - return false; - } -#endif - - // Create an EGL window surface - //--------------------------------------------------------------------------------- -#if defined(PLATFORM_ANDROID) - EGLint displayFormat = 0; - - // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry() - // As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID - eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat); - - // At this point we need to manage render size vs screen size - // NOTE: This function use and modify global module variables: - // -> CORE.Window.screen.width/CORE.Window.screen.height - // -> CORE.Window.render.width/CORE.Window.render.height - // -> CORE.Window.screenScale - SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); - - ANativeWindow_setBuffersGeometry(CORE.Android.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat); - //ANativeWindow_setBuffersGeometry(CORE.Android.app->window, 0, 0, displayFormat); // Force use of native display size - - CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, CORE.Android.app->window, NULL); -#endif // PLATFORM_ANDROID - -#if defined(PLATFORM_DRM) - CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, (EGLNativeWindowType)CORE.Window.gbmSurface, NULL); - if (EGL_NO_SURFACE == CORE.Window.surface) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL window surface: 0x%04x", eglGetError()); - return false; - } - - // At this point we need to manage render size vs screen size - // NOTE: This function use and modify global module variables: - // -> CORE.Window.screen.width/CORE.Window.screen.height - // -> CORE.Window.render.width/CORE.Window.render.height - // -> CORE.Window.screenScale - SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); -#endif // PLATFORM_DRM - - // There must be at least one frame displayed before the buffers are swapped - //eglSwapInterval(CORE.Window.device, 1); - - if (eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context) == EGL_FALSE) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); - return false; - } - else - { - CORE.Window.render.width = CORE.Window.screen.width; - CORE.Window.render.height = CORE.Window.screen.height; - CORE.Window.currentFbo.width = CORE.Window.render.width; - CORE.Window.currentFbo.height = CORE.Window.render.height; - - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); - TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); - TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); - TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); - TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); - } -#endif // PLATFORM_ANDROID || PLATFORM_DRM - - // Load OpenGL extensions - // NOTE: GL procedures address loader is required to load extensions -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - rlLoadExtensions(glfwGetProcAddress); -#else - rlLoadExtensions(eglGetProcAddress); -#endif - - // Initialize OpenGL context (states and resources) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - -#if defined(PLATFORM_ANDROID) - CORE.Window.ready = true; -#endif - - if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); - - return true; -} +// Platform-specific functions +//static bool InitGraphicsDevice(int width, int height) // Set viewport for a provided width and height -static void SetupViewport(int width, int height) +void SetupViewport(int width, int height) { CORE.Window.render.width = width; CORE.Window.render.height = height; @@ -4998,7 +2204,7 @@ static void SetupViewport(int width, int height) // Compute framebuffer size relative to screen size and display size // NOTE: Global variables CORE.Window.render.width/CORE.Window.render.height and CORE.Window.renderOffset.x/CORE.Window.renderOffset.y can be modified -static void SetupFramebuffer(int width, int height) +void SetupFramebuffer(int width, int height) { // Calculate CORE.Window.render.width and CORE.Window.render.height, we have the display size (input params) and the desired screen size (global var) if ((CORE.Window.screen.width > CORE.Window.display.width) || (CORE.Window.screen.height > CORE.Window.display.height)) @@ -5075,7 +2281,7 @@ static void SetupFramebuffer(int width, int height) } // Initialize hi-resolution timer -static void InitTimer(void) +void InitTimer(void) { // Setting a higher resolution can improve the accuracy of time-out intervals in wait functions. // However, it can also reduce overall system performance, because the thread scheduler switches tasks more often. @@ -5142,323 +2348,6 @@ void WaitTime(double seconds) #endif } -// Swap back buffer with front buffer (screen drawing) -void SwapScreenBuffer(void) -{ -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - glfwSwapBuffers(CORE.Window.handle); -#endif - -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) - eglSwapBuffers(CORE.Window.device, CORE.Window.surface); - -#if defined(PLATFORM_DRM) - - if (!CORE.Window.gbmSurface || (-1 == CORE.Window.fd) || !CORE.Window.connector || !CORE.Window.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); - - struct gbm_bo *bo = gbm_surface_lock_front_buffer(CORE.Window.gbmSurface); - if (!bo) TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer"); - - uint32_t fb = 0; - int result = drmModeAddFB(CORE.Window.fd, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, 24, 32, gbm_bo_get_stride(bo), gbm_bo_get_handle(bo).u32, &fb); - if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d", result); - - result = drmModeSetCrtc(CORE.Window.fd, CORE.Window.crtc->crtc_id, fb, 0, 0, &CORE.Window.connector->connector_id, 1, &CORE.Window.connector->modes[CORE.Window.modeIndex]); - if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result); - - if (CORE.Window.prevFB) - { - result = drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB); - if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result); - } - - CORE.Window.prevFB = fb; - - if (CORE.Window.prevBO) gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO); - - CORE.Window.prevBO = bo; - -#endif // PLATFORM_DRM -#endif // PLATFORM_ANDROID || PLATFORM_DRM -} - -// Register all input events -void PollInputEvents(void) -{ -#if defined(SUPPORT_GESTURES_SYSTEM) - // NOTE: Gestures update must be called every frame to reset gestures correctly - // because ProcessGestureEvent() is just called on an event, not every frame - UpdateGestures(); -#endif - - // Reset keys/chars pressed registered - CORE.Input.Keyboard.keyPressedQueueCount = 0; - CORE.Input.Keyboard.charPressedQueueCount = 0; - // Reset key repeats - for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; - - // Reset last gamepad button/axis registered state - CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Input.Gamepad.axisCount = 0; - -#if defined(PLATFORM_DRM) - // Register previous keys states - for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) - { - CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; - CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; - } - - PollKeyboardEvents(); - - // Register previous mouse states - CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; - CORE.Input.Mouse.currentWheelMove = CORE.Input.Mouse.eventWheelMove; - CORE.Input.Mouse.eventWheelMove = (Vector2){ 0.0f, 0.0f }; - for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) - { - CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; - CORE.Input.Mouse.currentButtonState[i] = CORE.Input.Mouse.currentButtonStateEvdev[i]; - } - - // Register gamepads buttons events - for (int i = 0; i < MAX_GAMEPADS; i++) - { - if (CORE.Input.Gamepad.ready[i]) - { - // Register previous gamepad states - for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k]; - } - } -#endif - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback) - - // Register previous keys states - for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) - { - CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; - CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; - } - - // Register previous mouse states - for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; - - // Register previous mouse wheel state - CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; - CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f }; - - // Register previous mouse position - CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; -#endif - - // Register previous touch states - for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; - - // Reset touch positions - // TODO: It resets on PLATFORM_WEB the mouse position and not filled again until a move-event, - // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! - //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; - -#if defined(PLATFORM_DESKTOP) - // Check if gamepads are ready - // NOTE: We do it here in case of disconnection - for (int i = 0; i < MAX_GAMEPADS; i++) - { - if (glfwJoystickPresent(i)) CORE.Input.Gamepad.ready[i] = true; - else CORE.Input.Gamepad.ready[i] = false; - } - - // Register gamepads buttons events - for (int i = 0; i < MAX_GAMEPADS; i++) - { - if (CORE.Input.Gamepad.ready[i]) // Check if gamepad is available - { - // Register previous gamepad states - for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k]; - - // Get current gamepad state - // NOTE: There is no callback available, so we get it manually - GLFWgamepadstate state = { 0 }; - glfwGetGamepadState(i, &state); // This remapps all gamepads so they have their buttons mapped like an xbox controller - - const unsigned char *buttons = state.buttons; - - for (int k = 0; (buttons != NULL) && (k < GLFW_GAMEPAD_BUTTON_DPAD_LEFT + 1) && (k < MAX_GAMEPAD_BUTTONS); k++) - { - int button = -1; // GamepadButton enum values assigned - - switch (k) - { - case GLFW_GAMEPAD_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; - case GLFW_GAMEPAD_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; - case GLFW_GAMEPAD_BUTTON_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; - case GLFW_GAMEPAD_BUTTON_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; - - case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; - case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; - - case GLFW_GAMEPAD_BUTTON_BACK: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break; - case GLFW_GAMEPAD_BUTTON_GUIDE: button = GAMEPAD_BUTTON_MIDDLE; break; - case GLFW_GAMEPAD_BUTTON_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; - - case GLFW_GAMEPAD_BUTTON_DPAD_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break; - case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; - case GLFW_GAMEPAD_BUTTON_DPAD_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; - case GLFW_GAMEPAD_BUTTON_DPAD_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; - - case GLFW_GAMEPAD_BUTTON_LEFT_THUMB: button = GAMEPAD_BUTTON_LEFT_THUMB; break; - case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: button = GAMEPAD_BUTTON_RIGHT_THUMB; break; - default: break; - } - - if (button != -1) // Check for valid button - { - if (buttons[k] == GLFW_PRESS) - { - CORE.Input.Gamepad.currentButtonState[i][button] = 1; - CORE.Input.Gamepad.lastButtonPressed = button; - } - else CORE.Input.Gamepad.currentButtonState[i][button] = 0; - } - } - - // Get current axis state - const float *axes = state.axes; - - for (int k = 0; (axes != NULL) && (k < GLFW_GAMEPAD_AXIS_LAST + 1) && (k < MAX_GAMEPAD_AXIS); k++) - { - CORE.Input.Gamepad.axisState[i][k] = axes[k]; - } - - // Register buttons for 2nd triggers (because GLFW doesn't count these as buttons but rather axis) - CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1f); - CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.1f); - - CORE.Input.Gamepad.axisCount = GLFW_GAMEPAD_AXIS_LAST + 1; - } - } - - CORE.Window.resizedLastFrame = false; - - if (CORE.Window.eventWaiting) glfwWaitEvents(); // Wait for in input events before continue (drawing is paused) - else glfwPollEvents(); // Poll input events: keyboard/mouse/window events (callbacks) -#endif // PLATFORM_DESKTOP - -#if defined(PLATFORM_WEB) - CORE.Window.resizedLastFrame = false; -#endif // PLATFORM_WEB - -// Gamepad support using emscripten API -// NOTE: GLFW3 joystick functionality not available in web -#if defined(PLATFORM_WEB) - // Get number of gamepads connected - int numGamepads = 0; - if (emscripten_sample_gamepad_data() == EMSCRIPTEN_RESULT_SUCCESS) numGamepads = emscripten_get_num_gamepads(); - - for (int i = 0; (i < numGamepads) && (i < MAX_GAMEPADS); i++) - { - // Register previous gamepad button states - for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k]; - - EmscriptenGamepadEvent gamepadState; - - int result = emscripten_get_gamepad_status(i, &gamepadState); - - if (result == EMSCRIPTEN_RESULT_SUCCESS) - { - // Register buttons data for every connected gamepad - for (int j = 0; (j < gamepadState.numButtons) && (j < MAX_GAMEPAD_BUTTONS); j++) - { - GamepadButton button = -1; - - // Gamepad Buttons reference: https://www.w3.org/TR/gamepad/#gamepad-interface - switch (j) - { - case 0: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; - case 1: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; - case 2: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; - case 3: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; - case 4: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; - case 5: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; - case 6: button = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break; - case 7: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break; - case 8: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break; - case 9: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; - case 10: button = GAMEPAD_BUTTON_LEFT_THUMB; break; - case 11: button = GAMEPAD_BUTTON_RIGHT_THUMB; break; - case 12: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break; - case 13: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; - case 14: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; - case 15: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; - default: break; - } - - if (button != -1) // Check for valid button - { - if (gamepadState.digitalButton[j] == 1) - { - CORE.Input.Gamepad.currentButtonState[i][button] = 1; - CORE.Input.Gamepad.lastButtonPressed = button; - } - else CORE.Input.Gamepad.currentButtonState[i][button] = 0; - } - - //TRACELOGD("INPUT: Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]); - } - - // Register axis data for every connected gamepad - for (int j = 0; (j < gamepadState.numAxes) && (j < MAX_GAMEPAD_AXIS); j++) - { - CORE.Input.Gamepad.axisState[i][j] = gamepadState.axis[j]; - } - - CORE.Input.Gamepad.axisCount = gamepadState.numAxes; - } - } -#endif - -#if defined(PLATFORM_ANDROID) - // Register previous keys states - // NOTE: Android supports up to 260 keys - for (int i = 0; i < 260; i++) - { - CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; - CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; - } - - // Android ALooper_pollAll() variables - int pollResult = 0; - int pollEvents = 0; - - // Poll Events (registered events) - // NOTE: Activity is paused if not enabled (CORE.Android.appEnabled) - while ((pollResult = ALooper_pollAll(CORE.Android.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) - { - // Process this event - if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); - - // NOTE: Never close window, native activity is controlled by the system! - if (CORE.Android.app->destroyRequested != 0) - { - //CORE.Window.shouldClose = true; - //ANativeActivity_finish(CORE.Android.app->activity); - } - } -#endif - -#if defined(PLATFORM_DRM) && defined(SUPPORT_SSH_KEYBOARD_RPI) - // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here. - // stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console - - if (!CORE.Input.Keyboard.evtMode) ProcessKeyboard(); - - // NOTE: Mouse input events polling is done asynchronously in another pthread - EventThread() - // NOTE: Gamepad (Joystick) input events polling is done asynchonously in another pthread - GamepadThread() -#endif -} - // Scan all files and directories in a base path // WARNING: files.paths[] must be previously allocated and // contain enough space to store all required paths @@ -5549,1638 +2438,6 @@ static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *fi else TRACELOG(LOG_WARNING, "FILEIO: Directory cannot be opened (%s)", basePath); } -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) -// GLFW3 Error Callback, runs on GLFW3 error -static void ErrorCallback(int error, const char *description) -{ - TRACELOG(LOG_WARNING, "GLFW: Error: %i Description: %s", error, description); -} - -// GLFW3 WindowSize Callback, runs when window is resizedLastFrame -// NOTE: Window resizing not allowed by default -static void WindowSizeCallback(GLFWwindow *window, int width, int height) -{ - // Reset viewport and projection matrix for new size - SetupViewport(width, height); - - CORE.Window.currentFbo.width = width; - CORE.Window.currentFbo.height = height; - CORE.Window.resizedLastFrame = true; - - if (IsWindowFullscreen()) return; - - // Set current screen size -#if defined(__APPLE__) - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; -#else - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - Vector2 windowScaleDPI = GetWindowScaleDPI(); - - CORE.Window.screen.width = (unsigned int)(width/windowScaleDPI.x); - CORE.Window.screen.height = (unsigned int)(height/windowScaleDPI.y); - } - else - { - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - } -#endif - - // NOTE: Postprocessing texture is not scaled to new size -} - -// GLFW3 WindowIconify Callback, runs when window is minimized/restored -static void WindowIconifyCallback(GLFWwindow *window, int iconified) -{ - if (iconified) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; // The window was iconified - else CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // The window was restored -} - -#if !defined(PLATFORM_WEB) -// GLFW3 WindowMaximize Callback, runs when window is maximized/restored -static void WindowMaximizeCallback(GLFWwindow *window, int maximized) -{ - if (maximized) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // The window was maximized - else CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; // The window was restored -} -#endif - -// GLFW3 WindowFocus Callback, runs when window get/lose focus -static void WindowFocusCallback(GLFWwindow *window, int focused) -{ - if (focused) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // The window was focused - else CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; // The window lost focus -} - -// GLFW3 Keyboard Callback, runs on key pressed -static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) -{ - if (key < 0) return; // Security check, macOS fn key generates -1 - - // WARNING: GLFW could return GLFW_REPEAT, we need to consider it as 1 - // to work properly with our implementation (IsKeyDown/IsKeyUp checks) - if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0; - else if(action == GLFW_PRESS) CORE.Input.Keyboard.currentKeyState[key] = 1; - else if(action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1; - -#if !defined(PLATFORM_WEB) - // WARNING: Check if CAPS/NUM key modifiers are enabled and force down state for those keys - if (((key == KEY_CAPS_LOCK) && ((mods & GLFW_MOD_CAPS_LOCK) > 0)) || - ((key == KEY_NUM_LOCK) && ((mods & GLFW_MOD_NUM_LOCK) > 0))) CORE.Input.Keyboard.currentKeyState[key] = 1; -#endif - - // Check if there is space available in the key queue - if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS)) - { - // Add character to the queue - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key; - CORE.Input.Keyboard.keyPressedQueueCount++; - } - - // Check the exit key to set close window - if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(CORE.Window.handle, GLFW_TRUE); - -#if defined(SUPPORT_SCREEN_CAPTURE) - if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) - { -#if defined(SUPPORT_GIF_RECORDING) - if (mods & GLFW_MOD_CONTROL) - { - if (gifRecording) - { - gifRecording = false; - - MsfGifResult result = msf_gif_end(&gifState); - - SaveFileData(TextFormat("%s/screenrec%03i.gif", CORE.Storage.basePath, screenshotCounter), result.data, (unsigned int)result.dataSize); - msf_gif_free(result); - - #if defined(PLATFORM_WEB) - // Download file from MEMFS (emscripten memory filesystem) - // saveFileFromMEMFSToDisk() function is defined in raylib/templates/web_shel/shell.html - emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", TextFormat("screenrec%03i.gif", screenshotCounter - 1), TextFormat("screenrec%03i.gif", screenshotCounter - 1))); - #endif - - TRACELOG(LOG_INFO, "SYSTEM: Finish animated GIF recording"); - } - else - { - gifRecording = true; - gifFrameCounter = 0; - - Vector2 scale = GetWindowScaleDPI(); - msf_gif_begin(&gifState, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); - screenshotCounter++; - - TRACELOG(LOG_INFO, "SYSTEM: Start animated GIF recording: %s", TextFormat("screenrec%03i.gif", screenshotCounter)); - } - } - else -#endif // SUPPORT_GIF_RECORDING - { - TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); - screenshotCounter++; - } - } -#endif // SUPPORT_SCREEN_CAPTURE - -#if defined(SUPPORT_EVENTS_AUTOMATION) - if ((key == GLFW_KEY_F11) && (action == GLFW_PRESS)) - { - eventsRecording = !eventsRecording; - - // On finish recording, we export events into a file - if (!eventsRecording) ExportAutomationEvents("eventsrec.rep"); - } - else if ((key == GLFW_KEY_F9) && (action == GLFW_PRESS)) - { - LoadAutomationEvents("eventsrec.rep"); - eventsPlaying = true; - - TRACELOG(LOG_WARNING, "eventsPlaying enabled!"); - } -#endif -} - -// GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value) -static void CharCallback(GLFWwindow *window, unsigned int key) -{ - //TRACELOG(LOG_DEBUG, "Char Callback: KEY:%i(%c)", key, key); - - // NOTE: Registers any key down considering OS keyboard layout but - // does not detect action events, those should be managed by user... - // Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907 - // Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char - - // Check if there is space available in the queue - if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) - { - // Add character to the queue - CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = key; - CORE.Input.Keyboard.charPressedQueueCount++; - } -} - -// GLFW3 Mouse Button Callback, runs on mouse button pressed -static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods) -{ - // WARNING: GLFW could only return GLFW_PRESS (1) or GLFW_RELEASE (0) for now, - // but future releases may add more actions (i.e. GLFW_REPEAT) - CORE.Input.Mouse.currentButtonState[button] = action; - -#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) // PLATFORM_DESKTOP - // Process mouse events as touches to be able to use mouse-gestures - GestureEvent gestureEvent = { 0 }; - - // Register touch actions - if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) gestureEvent.touchAction = TOUCH_ACTION_DOWN; - else if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) gestureEvent.touchAction = TOUCH_ACTION_UP; - - // NOTE: TOUCH_ACTION_MOVE event is registered in MouseCursorPosCallback() - - // Assign a pointer ID - gestureEvent.pointId[0] = 0; - - // Register touch points count - gestureEvent.pointCount = 1; - - // Register touch points position, only one point registered - gestureEvent.position[0] = GetMousePosition(); - - // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height - gestureEvent.position[0].x /= (float)GetScreenWidth(); - gestureEvent.position[0].y /= (float)GetScreenHeight(); - - // Gesture data is sent to gestures-system for processing -#if defined(PLATFORM_WEB) - // Prevent calling ProcessGestureEvent() when Emscripten is present and there's a touch gesture, so EmscriptenTouchCallback() can handle it itself - if (GetMouseX() != 0 || GetMouseY() != 0) ProcessGestureEvent(gestureEvent); -#else - ProcessGestureEvent(gestureEvent); -#endif - -#endif -} - -// GLFW3 Cursor Position Callback, runs on mouse move -static void MouseCursorPosCallback(GLFWwindow *window, double x, double y) -{ - CORE.Input.Mouse.currentPosition.x = (float)x; - CORE.Input.Mouse.currentPosition.y = (float)y; - CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; - -#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) // PLATFORM_DESKTOP - // Process mouse events as touches to be able to use mouse-gestures - GestureEvent gestureEvent = { 0 }; - - gestureEvent.touchAction = TOUCH_ACTION_MOVE; - - // Assign a pointer ID - gestureEvent.pointId[0] = 0; - - // Register touch points count - gestureEvent.pointCount = 1; - - // Register touch points position, only one point registered - gestureEvent.position[0] = CORE.Input.Touch.position[0]; - - // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height - gestureEvent.position[0].x /= (float)GetScreenWidth(); - gestureEvent.position[0].y /= (float)GetScreenHeight(); - - // Gesture data is sent to gestures-system for processing - ProcessGestureEvent(gestureEvent); -#endif -} - -// GLFW3 Scrolling Callback, runs on mouse wheel -static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset) -{ - CORE.Input.Mouse.currentWheelMove = (Vector2){ (float)xoffset, (float)yoffset }; -} - -// GLFW3 CursorEnter Callback, when cursor enters the window -static void CursorEnterCallback(GLFWwindow *window, int enter) -{ - if (enter == true) CORE.Input.Mouse.cursorOnScreen = true; - else CORE.Input.Mouse.cursorOnScreen = false; -} - -// GLFW3 Window Drop Callback, runs when drop files into window -static void WindowDropCallback(GLFWwindow *window, int count, const char **paths) -{ - if (count > 0) - { - // In case previous dropped filepaths have not been freed, we free them - if (CORE.Window.dropFileCount > 0) - { - for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilepaths[i]); - - RL_FREE(CORE.Window.dropFilepaths); - - CORE.Window.dropFileCount = 0; - CORE.Window.dropFilepaths = NULL; - } - - // WARNING: Paths are freed by GLFW when the callback returns, we must keep an internal copy - CORE.Window.dropFileCount = count; - CORE.Window.dropFilepaths = (char **)RL_CALLOC(CORE.Window.dropFileCount, sizeof(char *)); - - for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) - { - CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); - strcpy(CORE.Window.dropFilepaths[i], paths[i]); - } - } -} -#endif - -#if defined(PLATFORM_ANDROID) -// ANDROID: Process activity lifecycle commands -static void AndroidCommandCallback(struct android_app *app, int32_t cmd) -{ - switch (cmd) - { - case APP_CMD_START: - { - //rendering = true; - } break; - case APP_CMD_RESUME: break; - case APP_CMD_INIT_WINDOW: - { - if (app->window != NULL) - { - if (CORE.Android.contextRebindRequired) - { - // Reset screen scaling to full display size - EGLint displayFormat = 0; - eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat); - - // Adding renderOffset here feels rather hackish, but the viewport scaling is wrong after the - // context rebinding if the screen is scaled unless offsets are added. There's probably a more - // appropriate way to fix this - ANativeWindow_setBuffersGeometry(app->window, - CORE.Window.render.width + CORE.Window.renderOffset.x, - CORE.Window.render.height + CORE.Window.renderOffset.y, - displayFormat); - - // Recreate display surface and re-attach OpenGL context - CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, app->window, NULL); - eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context); - - CORE.Android.contextRebindRequired = false; - } - else - { - CORE.Window.display.width = ANativeWindow_getWidth(CORE.Android.app->window); - CORE.Window.display.height = ANativeWindow_getHeight(CORE.Android.app->window); - - // Initialize graphics device (display device and OpenGL context) - InitGraphicsDevice(CORE.Window.screen.width, CORE.Window.screen.height); - - // Initialize hi-res timer - InitTimer(); - - // Initialize random seed - srand((unsigned int)time(NULL)); - - #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - // Load default font - // WARNING: External function: Module required: rtext - LoadFontDefault(); - Rectangle rec = GetFontDefault().recs[95]; - // NOTE: We setup a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering - #if defined(SUPPORT_MODULE_RSHAPES) - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); // WARNING: Module required: rshapes - #endif - #endif - - // TODO: GPU assets reload in case of lost focus (lost context) - // NOTE: This problem has been solved just unbinding and rebinding context from display - /* - if (assetsReloadRequired) - { - for (int i = 0; i < assetCount; i++) - { - // TODO: Unload old asset if required - - // Load texture again to pointed texture - (*textureAsset + i) = LoadTexture(assetPath[i]); - } - } - */ - } - } - } break; - case APP_CMD_GAINED_FOCUS: - { - CORE.Android.appEnabled = true; - //ResumeMusicStream(); - } break; - case APP_CMD_PAUSE: break; - case APP_CMD_LOST_FOCUS: - { - CORE.Android.appEnabled = false; - //PauseMusicStream(); - } break; - case APP_CMD_TERM_WINDOW: - { - // Detach OpenGL context and destroy display surface - // NOTE 1: This case is used when the user exits the app without closing it. We detach the context to ensure everything is recoverable upon resuming. - // NOTE 2: Detaching context before destroying display surface avoids losing our resources (textures, shaders, VBOs...) - // NOTE 3: In some cases (too many context loaded), OS could unload context automatically... :( - if (CORE.Window.device != EGL_NO_DISPLAY) - { - eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - - if (CORE.Window.surface != EGL_NO_SURFACE) - { - eglDestroySurface(CORE.Window.device, CORE.Window.surface); - CORE.Window.surface = EGL_NO_SURFACE; - } - - CORE.Android.contextRebindRequired = true; - } - // If 'CORE.Window.device' is already set to 'EGL_NO_DISPLAY' - // this means that the user has already called 'CloseWindow()' - - } break; - case APP_CMD_SAVE_STATE: break; - case APP_CMD_STOP: break; - case APP_CMD_DESTROY: break; - case APP_CMD_CONFIG_CHANGED: - { - //AConfiguration_fromAssetManager(CORE.Android.app->config, CORE.Android.app->activity->assetManager); - //print_cur_config(CORE.Android.app); - - // Check screen orientation here! - } break; - default: break; - } -} - -static GamepadButton AndroidTranslateGamepadButton(int button) -{ - switch (button) - { - case AKEYCODE_BUTTON_A: return GAMEPAD_BUTTON_RIGHT_FACE_DOWN; - case AKEYCODE_BUTTON_B: return GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; - case AKEYCODE_BUTTON_X: return GAMEPAD_BUTTON_RIGHT_FACE_LEFT; - case AKEYCODE_BUTTON_Y: return GAMEPAD_BUTTON_RIGHT_FACE_UP; - case AKEYCODE_BUTTON_L1: return GAMEPAD_BUTTON_LEFT_TRIGGER_1; - case AKEYCODE_BUTTON_R1: return GAMEPAD_BUTTON_RIGHT_TRIGGER_1; - case AKEYCODE_BUTTON_L2: return GAMEPAD_BUTTON_LEFT_TRIGGER_2; - case AKEYCODE_BUTTON_R2: return GAMEPAD_BUTTON_RIGHT_TRIGGER_2; - case AKEYCODE_BUTTON_THUMBL: return GAMEPAD_BUTTON_LEFT_THUMB; - case AKEYCODE_BUTTON_THUMBR: return GAMEPAD_BUTTON_RIGHT_THUMB; - case AKEYCODE_BUTTON_START: return GAMEPAD_BUTTON_MIDDLE_RIGHT; - case AKEYCODE_BUTTON_SELECT: return GAMEPAD_BUTTON_MIDDLE_LEFT; - case AKEYCODE_BUTTON_MODE: return GAMEPAD_BUTTON_MIDDLE; - // On some (most?) gamepads dpad events are reported as axis motion instead - case AKEYCODE_DPAD_DOWN: return GAMEPAD_BUTTON_LEFT_FACE_DOWN; - case AKEYCODE_DPAD_RIGHT: return GAMEPAD_BUTTON_LEFT_FACE_RIGHT; - case AKEYCODE_DPAD_LEFT: return GAMEPAD_BUTTON_LEFT_FACE_LEFT; - case AKEYCODE_DPAD_UP: return GAMEPAD_BUTTON_LEFT_FACE_UP; - default: return GAMEPAD_BUTTON_UNKNOWN; - } -} - -// ANDROID: Get input events -static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) -{ - // If additional inputs are required check: - // https://developer.android.com/ndk/reference/group/input - // https://developer.android.com/training/game-controllers/controller-input - - int type = AInputEvent_getType(event); - int source = AInputEvent_getSource(event); - - if (type == AINPUT_EVENT_TYPE_MOTION) - { - if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) || - ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD)) - { - // For now we'll assume a single gamepad which we "detect" on its input event - CORE.Input.Gamepad.ready[0] = true; - - CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_X] = AMotionEvent_getAxisValue( - event, AMOTION_EVENT_AXIS_X, 0); - CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_Y] = AMotionEvent_getAxisValue( - event, AMOTION_EVENT_AXIS_Y, 0); - CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_X] = AMotionEvent_getAxisValue( - event, AMOTION_EVENT_AXIS_Z, 0); - CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_Y] = AMotionEvent_getAxisValue( - event, AMOTION_EVENT_AXIS_RZ, 0); - CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_TRIGGER] = AMotionEvent_getAxisValue( - event, AMOTION_EVENT_AXIS_BRAKE, 0) * 2.0f - 1.0f; - CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_TRIGGER] = AMotionEvent_getAxisValue( - event, AMOTION_EVENT_AXIS_GAS, 0) * 2.0f - 1.0f; - - // dpad is reported as an axis on android - float dpadX = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_X, 0); - float dpadY = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_Y, 0); - - if (dpadX == 1.0f) - { - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 1; - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0; - } - else if (dpadX == -1.0f) - { - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0; - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 1; - } - else - { - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0; - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0; - } - - if (dpadY == 1.0f) - { - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 1; - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0; - } - else if (dpadY == -1.0f) - { - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0; - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 1; - } - else - { - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0; - CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0; - } - - return 1; // Handled gamepad axis motion - } - } - else if (type == AINPUT_EVENT_TYPE_KEY) - { - int32_t keycode = AKeyEvent_getKeyCode(event); - //int32_t AKeyEvent_getMetaState(event); - - // Handle gamepad button presses and releases - if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) || - ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD)) - { - // For now we'll assume a single gamepad which we "detect" on its input event - CORE.Input.Gamepad.ready[0] = true; - - GamepadButton button = AndroidTranslateGamepadButton(keycode); - - if (button == GAMEPAD_BUTTON_UNKNOWN) return 1; - - if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) - { - CORE.Input.Gamepad.currentButtonState[0][button] = 1; - } - else CORE.Input.Gamepad.currentButtonState[0][button] = 0; // Key up - - return 1; // Handled gamepad button - } - - // Save current button and its state - // NOTE: Android key action is 0 for down and 1 for up - if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) - { - CORE.Input.Keyboard.currentKeyState[keycode] = 1; // Key down - - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; - CORE.Input.Keyboard.keyPressedQueueCount++; - } - else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_MULTIPLE) CORE.Input.Keyboard.keyRepeatInFrame[keycode] = 1; - else CORE.Input.Keyboard.currentKeyState[keycode] = 0; // Key up - - if (keycode == AKEYCODE_POWER) - { - // Let the OS handle input to avoid app stuck. Behaviour: CMD_PAUSE -> CMD_SAVE_STATE -> CMD_STOP -> CMD_CONFIG_CHANGED -> CMD_LOST_FOCUS - // Resuming Behaviour: CMD_START -> CMD_RESUME -> CMD_CONFIG_CHANGED -> CMD_CONFIG_CHANGED -> CMD_GAINED_FOCUS - // It seems like locking mobile, screen size (CMD_CONFIG_CHANGED) is affected. - // NOTE: AndroidManifest.xml must have - // Before that change, activity was calling CMD_TERM_WINDOW and CMD_DESTROY when locking mobile, so that was not a normal behaviour - return 0; - } - else if ((keycode == AKEYCODE_BACK) || (keycode == AKEYCODE_MENU)) - { - // Eat BACK_BUTTON and AKEYCODE_MENU, just do nothing... and don't let to be handled by OS! - return 1; - } - else if ((keycode == AKEYCODE_VOLUME_UP) || (keycode == AKEYCODE_VOLUME_DOWN)) - { - // Set default OS behaviour - return 0; - } - - return 0; - } - - // Register touch points count - CORE.Input.Touch.pointCount = AMotionEvent_getPointerCount(event); - - for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++) - { - // Register touch points id - CORE.Input.Touch.pointId[i] = AMotionEvent_getPointerId(event, i); - - // Register touch points position - CORE.Input.Touch.position[i] = (Vector2){ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i) }; - - // Normalize CORE.Input.Touch.position[i] for CORE.Window.screen.width and CORE.Window.screen.height - float widthRatio = (float)(CORE.Window.screen.width + CORE.Window.renderOffset.x) / (float)CORE.Window.display.width; - float heightRatio = (float)(CORE.Window.screen.height + CORE.Window.renderOffset.y) / (float)CORE.Window.display.height; - CORE.Input.Touch.position[i].x = CORE.Input.Touch.position[i].x * widthRatio - (float)CORE.Window.renderOffset.x / 2; - CORE.Input.Touch.position[i].y = CORE.Input.Touch.position[i].y * heightRatio - (float)CORE.Window.renderOffset.y / 2; - } - - int32_t action = AMotionEvent_getAction(event); - unsigned int flags = action & AMOTION_EVENT_ACTION_MASK; - -#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_ANDROID - GestureEvent gestureEvent = { 0 }; - - gestureEvent.pointCount = CORE.Input.Touch.pointCount; - - // Register touch actions - if (flags == AMOTION_EVENT_ACTION_DOWN) gestureEvent.touchAction = TOUCH_ACTION_DOWN; - else if (flags == AMOTION_EVENT_ACTION_UP) gestureEvent.touchAction = TOUCH_ACTION_UP; - else if (flags == AMOTION_EVENT_ACTION_MOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE; - else if (flags == AMOTION_EVENT_ACTION_CANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL; - - for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++) - { - gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i]; - gestureEvent.position[i] = CORE.Input.Touch.position[i]; - } - - // Gesture data is sent to gestures system for processing - ProcessGestureEvent(gestureEvent); -#endif - - int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - - if (flags == AMOTION_EVENT_ACTION_POINTER_UP || flags == AMOTION_EVENT_ACTION_UP) - { - // One of the touchpoints is released, remove it from touch point arrays - for (int i = pointerIndex; (i < CORE.Input.Touch.pointCount - 1) && (i < MAX_TOUCH_POINTS); i++) - { - CORE.Input.Touch.pointId[i] = CORE.Input.Touch.pointId[i+1]; - CORE.Input.Touch.position[i] = CORE.Input.Touch.position[i+1]; - } - - CORE.Input.Touch.pointCount--; - } - - // When all touchpoints are tapped and released really quickly, this event is generated - if (flags == AMOTION_EVENT_ACTION_CANCEL) CORE.Input.Touch.pointCount = 0; - - if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1; - else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0; - - return 0; -} -#endif - -#if defined(PLATFORM_WEB) -// Register fullscreen change events -static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData) -{ - // TODO: Implement EmscriptenFullscreenChangeCallback()? - - return 1; // The event was consumed by the callback handler -} - -// Register window resize event -static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData) -{ - // TODO: Implement EmscriptenWindowResizedCallback()? - - return 1; // The event was consumed by the callback handler -} - -EM_JS(int, GetWindowInnerWidth, (), { return window.innerWidth; }); -EM_JS(int, GetWindowInnerHeight, (), { return window.innerHeight; }); - -// Register DOM element resize event -static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData) -{ - // Don't resize non-resizeable windows - if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) return 1; - - // This event is called whenever the window changes sizes, - // so the size of the canvas object is explicitly retrieved below - int width = GetWindowInnerWidth(); - int height = GetWindowInnerHeight(); - - if (width < CORE.Window.screenMin.width) width = CORE.Window.screenMin.width; - else if (width > CORE.Window.screenMax.width && CORE.Window.screenMax.width > 0) width = CORE.Window.screenMax.width; - - if (height < CORE.Window.screenMin.height) height = CORE.Window.screenMin.height; - else if (height > CORE.Window.screenMax.height && CORE.Window.screenMax.height > 0) height = CORE.Window.screenMax.height; - - emscripten_set_canvas_element_size("#canvas",width,height); - - SetupViewport(width, height); // Reset viewport and projection matrix for new size - - CORE.Window.currentFbo.width = width; - CORE.Window.currentFbo.height = height; - CORE.Window.resizedLastFrame = true; - - if (IsWindowFullscreen()) return 1; - - // Set current screen size - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - - // NOTE: Postprocessing texture is not scaled to new size - - return 0; -} - -// Register mouse input events -static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) -{ - // This is only for registering mouse click events with emscripten and doesn't need to do anything - - return 1; // The event was consumed by the callback handler -} - -// Register connected/disconnected gamepads events -static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) -{ - /* - TRACELOGD("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"", - eventType != 0? emscripten_event_type_to_string(eventType) : "Gamepad state", - gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping); - - for (int i = 0; i < gamepadEvent->numAxes; ++i) TRACELOGD("Axis %d: %g", i, gamepadEvent->axis[i]); - for (int i = 0; i < gamepadEvent->numButtons; ++i) TRACELOGD("Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]); - */ - - if ((gamepadEvent->connected) && (gamepadEvent->index < MAX_GAMEPADS)) - { - CORE.Input.Gamepad.ready[gamepadEvent->index] = true; - sprintf(CORE.Input.Gamepad.name[gamepadEvent->index],"%s",gamepadEvent->id); - } - else CORE.Input.Gamepad.ready[gamepadEvent->index] = false; - - return 1; // The event was consumed by the callback handler -} - -// Register touch input events -static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData) -{ - // Register touch points count - CORE.Input.Touch.pointCount = touchEvent->numTouches; - - double canvasWidth = 0.0; - double canvasHeight = 0.0; - // NOTE: emscripten_get_canvas_element_size() returns canvas.width and canvas.height but - // we are looking for actual CSS size: canvas.style.width and canvas.style.height - //EMSCRIPTEN_RESULT res = emscripten_get_canvas_element_size("#canvas", &canvasWidth, &canvasHeight); - emscripten_get_element_css_size("#canvas", &canvasWidth, &canvasHeight); - - for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++) - { - // Register touch points id - CORE.Input.Touch.pointId[i] = touchEvent->touches[i].identifier; - - // Register touch points position - CORE.Input.Touch.position[i] = (Vector2){ touchEvent->touches[i].targetX, touchEvent->touches[i].targetY }; - - // Normalize gestureEvent.position[x] for CORE.Window.screen.width and CORE.Window.screen.height - CORE.Input.Touch.position[i].x *= ((float)GetScreenWidth()/(float)canvasWidth); - CORE.Input.Touch.position[i].y *= ((float)GetScreenHeight()/(float)canvasHeight); - - if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) CORE.Input.Touch.currentTouchState[i] = 1; - else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) CORE.Input.Touch.currentTouchState[i] = 0; - } - -#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_WEB - GestureEvent gestureEvent = { 0 }; - - gestureEvent.pointCount = CORE.Input.Touch.pointCount; - - // Register touch actions - if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) gestureEvent.touchAction = TOUCH_ACTION_DOWN; - else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) gestureEvent.touchAction = TOUCH_ACTION_UP; - else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE; - else if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL; - - for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++) - { - gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i]; - gestureEvent.position[i] = CORE.Input.Touch.position[i]; - - // Normalize gestureEvent.position[i] - gestureEvent.position[i].x /= (float)GetScreenWidth(); - gestureEvent.position[i].y /= (float)GetScreenHeight(); - } - - // Gesture data is sent to gestures system for processing - ProcessGestureEvent(gestureEvent); - - // Reset the pointCount for web, if it was the last Touch End event - if (eventType == EMSCRIPTEN_EVENT_TOUCHEND && CORE.Input.Touch.pointCount == 1) CORE.Input.Touch.pointCount = 0; -#endif - - return 1; // The event was consumed by the callback handler -} -#endif - -#if defined(PLATFORM_DRM) -// Initialize Keyboard system (using standard input) -static void InitKeyboard(void) -{ - // NOTE: We read directly from Standard Input (stdin) - STDIN_FILENO file descriptor, - // Reading directly from stdin will give chars already key-mapped by kernel to ASCII or UNICODE - - // Save terminal keyboard settings - tcgetattr(STDIN_FILENO, &CORE.Input.Keyboard.defaultSettings); - - // Reconfigure terminal with new settings - struct termios keyboardNewSettings = { 0 }; - keyboardNewSettings = CORE.Input.Keyboard.defaultSettings; - - // New terminal settings for keyboard: turn off buffering (non-canonical mode), echo and key processing - // NOTE: ISIG controls if ^C and ^Z generate break signals or not - keyboardNewSettings.c_lflag &= ~(ICANON | ECHO | ISIG); - //keyboardNewSettings.c_iflag &= ~(ISTRIP | INLCR | ICRNL | IGNCR | IXON | IXOFF); - keyboardNewSettings.c_cc[VMIN] = 1; - keyboardNewSettings.c_cc[VTIME] = 0; - - // Set new keyboard settings (change occurs immediately) - tcsetattr(STDIN_FILENO, TCSANOW, &keyboardNewSettings); - - // Save old keyboard mode to restore it at the end - CORE.Input.Keyboard.defaultFileFlags = fcntl(STDIN_FILENO, F_GETFL, 0); // F_GETFL: Get the file access mode and the file status flags - fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags | O_NONBLOCK); // F_SETFL: Set the file status flags to the value specified - - // NOTE: If ioctl() returns -1, it means the call failed for some reason (error code set in errno) - int result = ioctl(STDIN_FILENO, KDGKBMODE, &CORE.Input.Keyboard.defaultMode); - - // In case of failure, it could mean a remote keyboard is used (SSH) - if (result < 0) TRACELOG(LOG_WARNING, "RPI: Failed to change keyboard mode, an SSH keyboard is probably used"); - else - { - // Reconfigure keyboard mode to get: - // - scancodes (K_RAW) - // - keycodes (K_MEDIUMRAW) - // - ASCII chars (K_XLATE) - // - UNICODE chars (K_UNICODE) - ioctl(STDIN_FILENO, KDSKBMODE, K_XLATE); // ASCII chars - } - - // Register keyboard restore when program finishes - atexit(RestoreKeyboard); -} - -// Restore default keyboard input -static void RestoreKeyboard(void) -{ - // Reset to default keyboard settings - tcsetattr(STDIN_FILENO, TCSANOW, &CORE.Input.Keyboard.defaultSettings); - - // Reconfigure keyboard to default mode - fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags); - ioctl(STDIN_FILENO, KDSKBMODE, CORE.Input.Keyboard.defaultMode); -} - -#if defined(SUPPORT_SSH_KEYBOARD_RPI) -// Process keyboard inputs -static void ProcessKeyboard(void) -{ - #define MAX_KEYBUFFER_SIZE 32 // Max size in bytes to read - - // Keyboard input polling (fill keys[256] array with status) - int bufferByteCount = 0; // Bytes available on the buffer - char keysBuffer[MAX_KEYBUFFER_SIZE] = { 0 }; // Max keys to be read at a time - - // Read availables keycodes from stdin - bufferByteCount = read(STDIN_FILENO, keysBuffer, MAX_KEYBUFFER_SIZE); // POSIX system call - - // Reset pressed keys array (it will be filled below) - for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) - { - CORE.Input.Keyboard.currentKeyState[i] = 0; - CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; - } - - // Fill all read bytes (looking for keys) - for (int i = 0; i < bufferByteCount; i++) - { - // NOTE: If (key == 0x1b), depending on next key, it could be a special keymap code! - // Up -> 1b 5b 41 / Left -> 1b 5b 44 / Right -> 1b 5b 43 / Down -> 1b 5b 42 - if (keysBuffer[i] == 0x1b) - { - // Check if ESCAPE key has been pressed to stop program - if (bufferByteCount == 1) CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] = 1; - else - { - if (keysBuffer[i + 1] == 0x5b) // Special function key - { - if ((keysBuffer[i + 2] == 0x5b) || (keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32)) - { - // Process special function keys (F1 - F12) - switch (keysBuffer[i + 3]) - { - case 0x41: CORE.Input.Keyboard.currentKeyState[290] = 1; break; // raylib KEY_F1 - case 0x42: CORE.Input.Keyboard.currentKeyState[291] = 1; break; // raylib KEY_F2 - case 0x43: CORE.Input.Keyboard.currentKeyState[292] = 1; break; // raylib KEY_F3 - case 0x44: CORE.Input.Keyboard.currentKeyState[293] = 1; break; // raylib KEY_F4 - case 0x45: CORE.Input.Keyboard.currentKeyState[294] = 1; break; // raylib KEY_F5 - case 0x37: CORE.Input.Keyboard.currentKeyState[295] = 1; break; // raylib KEY_F6 - case 0x38: CORE.Input.Keyboard.currentKeyState[296] = 1; break; // raylib KEY_F7 - case 0x39: CORE.Input.Keyboard.currentKeyState[297] = 1; break; // raylib KEY_F8 - case 0x30: CORE.Input.Keyboard.currentKeyState[298] = 1; break; // raylib KEY_F9 - case 0x31: CORE.Input.Keyboard.currentKeyState[299] = 1; break; // raylib KEY_F10 - case 0x33: CORE.Input.Keyboard.currentKeyState[300] = 1; break; // raylib KEY_F11 - case 0x34: CORE.Input.Keyboard.currentKeyState[301] = 1; break; // raylib KEY_F12 - default: break; - } - - if (keysBuffer[i + 2] == 0x5b) i += 4; - else if ((keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32)) i += 5; - } - else - { - switch (keysBuffer[i + 2]) - { - case 0x41: CORE.Input.Keyboard.currentKeyState[265] = 1; break; // raylib KEY_UP - case 0x42: CORE.Input.Keyboard.currentKeyState[264] = 1; break; // raylib KEY_DOWN - case 0x43: CORE.Input.Keyboard.currentKeyState[262] = 1; break; // raylib KEY_RIGHT - case 0x44: CORE.Input.Keyboard.currentKeyState[263] = 1; break; // raylib KEY_LEFT - default: break; - } - - i += 3; // Jump to next key - } - - // NOTE: Some keys are not directly keymapped (CTRL, ALT, SHIFT) - } - } - } - else if (keysBuffer[i] == 0x0a) // raylib KEY_ENTER (don't mix with KEY_*) - { - CORE.Input.Keyboard.currentKeyState[257] = 1; - - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257; // Add keys pressed into queue - CORE.Input.Keyboard.keyPressedQueueCount++; - } - else if (keysBuffer[i] == 0x7f) // raylib KEY_BACKSPACE - { - CORE.Input.Keyboard.currentKeyState[259] = 1; - - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257; // Add keys pressed into queue - CORE.Input.Keyboard.keyPressedQueueCount++; - } - else - { - // Translate lowercase a-z letters to A-Z - if ((keysBuffer[i] >= 97) && (keysBuffer[i] <= 122)) - { - CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i] - 32] = 1; - } - else CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i]] = 1; - - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keysBuffer[i]; // Add keys pressed into queue - CORE.Input.Keyboard.keyPressedQueueCount++; - } - } - - // Check exit key (same functionality as GLFW3 KeyCallback()) - if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true; - -#if defined(SUPPORT_SCREEN_CAPTURE) - // Check screen capture key (raylib key: KEY_F12) - if (CORE.Input.Keyboard.currentKeyState[301] == 1) - { - TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); - screenshotCounter++; - } -#endif -} -#endif // SUPPORT_SSH_KEYBOARD_RPI - -// Initialise user input from evdev(/dev/input/event) this means mouse, keyboard or gamepad devices -static void InitEvdevInput(void) -{ - char path[MAX_FILEPATH_LENGTH] = { 0 }; - DIR *directory = NULL; - struct dirent *entity = NULL; - - // Initialise keyboard file descriptor - CORE.Input.Keyboard.fd = -1; - - // Reset variables - for (int i = 0; i < MAX_TOUCH_POINTS; ++i) - { - CORE.Input.Touch.position[i].x = -1; - CORE.Input.Touch.position[i].y = -1; - } - - // Reset keyboard key state - for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) - { - CORE.Input.Keyboard.currentKeyState[i] = 0; - CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; - } - - // Open the linux directory of "/dev/input" - directory = opendir(DEFAULT_EVDEV_PATH); - - if (directory) - { - while ((entity = readdir(directory)) != NULL) - { - if ((strncmp("event", entity->d_name, strlen("event")) == 0) || // Search for devices named "event*" - (strncmp("mouse", entity->d_name, strlen("mouse")) == 0)) // Search for devices named "mouse*" - { - sprintf(path, "%s%s", DEFAULT_EVDEV_PATH, entity->d_name); - ConfigureEvdevDevice(path); // Configure the device if appropriate - } - } - - closedir(directory); - } - else TRACELOG(LOG_WARNING, "RPI: Failed to open linux event directory: %s", DEFAULT_EVDEV_PATH); -} - -// Identifies a input device and configures it for use if appropriate -static void ConfigureEvdevDevice(char *device) -{ - #define BITS_PER_LONG (8*sizeof(long)) - #define NBITS(x) ((((x) - 1)/BITS_PER_LONG) + 1) - #define OFF(x) ((x)%BITS_PER_LONG) - #define BIT(x) (1UL<> OFF(bit)) & 1) - - struct input_absinfo absinfo = { 0 }; - unsigned long evBits[NBITS(EV_MAX)] = { 0 }; - unsigned long absBits[NBITS(ABS_MAX)] = { 0 }; - unsigned long relBits[NBITS(REL_MAX)] = { 0 }; - unsigned long keyBits[NBITS(KEY_MAX)] = { 0 }; - bool hasAbs = false; - bool hasRel = false; - bool hasAbsMulti = false; - int freeWorkerId = -1; - int fd = -1; - - InputEventWorker *worker = NULL; - - // Open the device and allocate worker - //------------------------------------------------------------------------------------------------------- - // Find a free spot in the workers array - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) - { - if (CORE.Input.eventWorker[i].threadId == 0) - { - freeWorkerId = i; - break; - } - } - - // Select the free worker from array - if (freeWorkerId >= 0) - { - worker = &(CORE.Input.eventWorker[freeWorkerId]); // Grab a pointer to the worker - memset(worker, 0, sizeof(InputEventWorker)); // Clear the worker - } - else - { - TRACELOG(LOG_WARNING, "RPI: Failed to create input device thread for %s, out of worker slots", device); - return; - } - - // Open the device - fd = open(device, O_RDONLY | O_NONBLOCK); - if (fd < 0) - { - TRACELOG(LOG_WARNING, "RPI: Failed to open input device: %s", device); - return; - } - worker->fd = fd; - - // Grab number on the end of the devices name "event" - int devNum = 0; - char *ptrDevName = strrchr(device, 't'); - worker->eventNum = -1; - - if (ptrDevName != NULL) - { - if (sscanf(ptrDevName, "t%d", &devNum) == 1) worker->eventNum = devNum; - } - else worker->eventNum = 0; // TODO: HACK: Grab number for mouse0 device! - - // At this point we have a connection to the device, but we don't yet know what the device is. - // It could be many things, even as simple as a power button... - //------------------------------------------------------------------------------------------------------- - - // Identify the device - //------------------------------------------------------------------------------------------------------- - ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits); // Read a bitfield of the available device properties - - // Check for absolute input devices - if (TEST_BIT(evBits, EV_ABS)) - { - ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits); - - // Check for absolute movement support (usually touchscreens, but also joysticks) - if (TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y)) - { - hasAbs = true; - - // Get the scaling values - ioctl(fd, EVIOCGABS(ABS_X), &absinfo); - worker->absRange.x = absinfo.minimum; - worker->absRange.width = absinfo.maximum - absinfo.minimum; - ioctl(fd, EVIOCGABS(ABS_Y), &absinfo); - worker->absRange.y = absinfo.minimum; - worker->absRange.height = absinfo.maximum - absinfo.minimum; - } - - // Check for multiple absolute movement support (usually multitouch touchscreens) - if (TEST_BIT(absBits, ABS_MT_POSITION_X) && TEST_BIT(absBits, ABS_MT_POSITION_Y)) - { - hasAbsMulti = true; - - // Get the scaling values - ioctl(fd, EVIOCGABS(ABS_X), &absinfo); - worker->absRange.x = absinfo.minimum; - worker->absRange.width = absinfo.maximum - absinfo.minimum; - ioctl(fd, EVIOCGABS(ABS_Y), &absinfo); - worker->absRange.y = absinfo.minimum; - worker->absRange.height = absinfo.maximum - absinfo.minimum; - } - } - - // Check for relative movement support (usually mouse) - if (TEST_BIT(evBits, EV_REL)) - { - ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits); - - if (TEST_BIT(relBits, REL_X) && TEST_BIT(relBits, REL_Y)) hasRel = true; - } - - // Check for button support to determine the device type(usually on all input devices) - if (TEST_BIT(evBits, EV_KEY)) - { - ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits); - - if (hasAbs || hasAbsMulti) - { - if (TEST_BIT(keyBits, BTN_TOUCH)) worker->isTouch = true; // This is a touchscreen - if (TEST_BIT(keyBits, BTN_TOOL_FINGER)) worker->isTouch = true; // This is a drawing tablet - if (TEST_BIT(keyBits, BTN_TOOL_PEN)) worker->isTouch = true; // This is a drawing tablet - if (TEST_BIT(keyBits, BTN_STYLUS)) worker->isTouch = true; // This is a drawing tablet - if (worker->isTouch || hasAbsMulti) worker->isMultitouch = true; // This is a multitouch capable device - } - - if (hasRel) - { - if (TEST_BIT(keyBits, BTN_LEFT)) worker->isMouse = true; // This is a mouse - if (TEST_BIT(keyBits, BTN_RIGHT)) worker->isMouse = true; // This is a mouse - } - - if (TEST_BIT(keyBits, BTN_A)) worker->isGamepad = true; // This is a gamepad - if (TEST_BIT(keyBits, BTN_TRIGGER)) worker->isGamepad = true; // This is a gamepad - if (TEST_BIT(keyBits, BTN_START)) worker->isGamepad = true; // This is a gamepad - if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad - if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad - - if (TEST_BIT(keyBits, KEY_SPACE)) worker->isKeyboard = true; // This is a keyboard - } - //------------------------------------------------------------------------------------------------------- - - // Decide what to do with the device - //------------------------------------------------------------------------------------------------------- - if (worker->isKeyboard && (CORE.Input.Keyboard.fd == -1)) - { - // Use the first keyboard encountered. This assumes that a device that says it's a keyboard is just a - // keyboard. The keyboard is polled synchronously, whereas other input devices are polled in separate - // threads so that they don't drop events when the frame rate is slow. - TRACELOG(LOG_INFO, "RPI: Opening keyboard device: %s", device); - CORE.Input.Keyboard.fd = worker->fd; - } - else if (worker->isTouch || worker->isMouse) - { - // Looks like an interesting device - TRACELOG(LOG_INFO, "RPI: Opening input device: %s (%s%s%s%s)", device, - worker->isMouse? "mouse " : "", - worker->isMultitouch? "multitouch " : "", - worker->isTouch? "touchscreen " : "", - worker->isGamepad? "gamepad " : ""); - - // Create a thread for this device - int error = pthread_create(&worker->threadId, NULL, &EventThread, (void *)worker); - if (error != 0) - { - TRACELOG(LOG_WARNING, "RPI: Failed to create input device thread: %s (error: %d)", device, error); - worker->threadId = 0; - close(fd); - } - -#if defined(USE_LAST_TOUCH_DEVICE) - // Find touchscreen with the highest index - int maxTouchNumber = -1; - - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) - { - if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum > maxTouchNumber)) maxTouchNumber = CORE.Input.eventWorker[i].eventNum; - } - - // Find touchscreens with lower indexes - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) - { - if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum < maxTouchNumber)) - { - if (CORE.Input.eventWorker[i].threadId != 0) - { - TRACELOG(LOG_WARNING, "RPI: Found duplicate touchscreen, killing touchscreen on event: %d", i); - pthread_cancel(CORE.Input.eventWorker[i].threadId); - close(CORE.Input.eventWorker[i].fd); - } - } - } -#endif - } - else close(fd); // We are not interested in this device - //------------------------------------------------------------------------------------------------------- -} - -static void PollKeyboardEvents(void) -{ - // Scancode to keycode mapping for US keyboards - // TODO: Replace this with a keymap from the X11 to get the correct regional map for the keyboard: - // Currently non US keyboards will have the wrong mapping for some keys - static const int keymapUS[] = { - 0, 256, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 259, 258, 81, 87, 69, 82, 84, - 89, 85, 73, 79, 80, 91, 93, 257, 341, 65, 83, 68, 70, 71, 72, 74, 75, 76, 59, 39, 96, - 340, 92, 90, 88, 67, 86, 66, 78, 77, 44, 46, 47, 344, 332, 342, 32, 280, 290, 291, - 292, 293, 294, 295, 296, 297, 298, 299, 282, 281, 327, 328, 329, 333, 324, 325, - 326, 334, 321, 322, 323, 320, 330, 0, 85, 86, 300, 301, 89, 90, 91, 92, 93, 94, 95, - 335, 345, 331, 283, 346, 101, 268, 265, 266, 263, 262, 269, 264, 267, 260, 261, - 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 347, 127, - 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, - 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, - 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, - 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, - 192, 193, 194, 0, 0, 0, 0, 0, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, - 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, - 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, - 243, 244, 245, 246, 247, 248, 0, 0, 0, 0, 0, 0, 0 - }; - - int fd = CORE.Input.Keyboard.fd; - if (fd == -1) return; - - struct input_event event = { 0 }; - int keycode = -1; - - // Try to read data from the keyboard and only continue if successful - while (read(fd, &event, sizeof(event)) == (int)sizeof(event)) - { - // Button parsing - if (event.type == EV_KEY) - { -#if defined(SUPPORT_SSH_KEYBOARD_RPI) - // Change keyboard mode to events - CORE.Input.Keyboard.evtMode = true; -#endif - // Keyboard button parsing - if ((event.code >= 1) && (event.code <= 255)) //Keyboard keys appear for codes 1 to 255 - { - keycode = keymapUS[event.code & 0xFF]; // The code we get is a scancode so we look up the appropriate keycode - - // Make sure we got a valid keycode - if ((keycode > 0) && (keycode < sizeof(CORE.Input.Keyboard.currentKeyState))) - { - // WARNING: https://www.kernel.org/doc/Documentation/input/input.txt - // Event interface: 'value' is the value the event carries. Either a relative change for EV_REL, - // absolute new value for EV_ABS (joysticks ...), or 0 for EV_KEY for release, 1 for keypress and 2 for autorepeat - CORE.Input.Keyboard.currentKeyState[keycode] = (event.value >= 1)? 1 : 0; - if (event.value >= 1) - { - CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; // Register last key pressed - CORE.Input.Keyboard.keyPressedQueueCount++; - } - - #if defined(SUPPORT_SCREEN_CAPTURE) - // Check screen capture key (raylib key: KEY_F12) - if (CORE.Input.Keyboard.currentKeyState[301] == 1) - { - TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); - screenshotCounter++; - } - #endif - - if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true; - - TRACELOGD("RPI: KEY_%s ScanCode: %4i KeyCode: %4i", event.value == 0 ? "UP":"DOWN", event.code, keycode); - } - } - } - } -} - -// Input device events reading thread -static void *EventThread(void *arg) -{ - struct input_event event = { 0 }; - InputEventWorker *worker = (InputEventWorker *)arg; - - int touchAction = -1; // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE - bool gestureUpdate = false; // Flag to note gestures require to update - - while (!CORE.Window.shouldClose) - { - // Try to read data from the device and only continue if successful - while (read(worker->fd, &event, sizeof(event)) == (int)sizeof(event)) - { - // Relative movement parsing - if (event.type == EV_REL) - { - if (event.code == REL_X) - { - CORE.Input.Mouse.currentPosition.x += event.value; - CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x; - - touchAction = 2; // TOUCH_ACTION_MOVE - gestureUpdate = true; - } - - if (event.code == REL_Y) - { - CORE.Input.Mouse.currentPosition.y += event.value; - CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y; - - touchAction = 2; // TOUCH_ACTION_MOVE - gestureUpdate = true; - } - - if (event.code == REL_WHEEL) CORE.Input.Mouse.eventWheelMove.y += event.value; - } - - // Absolute movement parsing - if (event.type == EV_ABS) - { - // Basic movement - if (event.code == ABS_X) - { - CORE.Input.Mouse.currentPosition.x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange - CORE.Input.Touch.position[0].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange - - touchAction = 2; // TOUCH_ACTION_MOVE - gestureUpdate = true; - } - - if (event.code == ABS_Y) - { - CORE.Input.Mouse.currentPosition.y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange - CORE.Input.Touch.position[0].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange - - touchAction = 2; // TOUCH_ACTION_MOVE - gestureUpdate = true; - } - - // Multitouch movement - if (event.code == ABS_MT_SLOT) worker->touchSlot = event.value; // Remember the slot number for the folowing events - - if (event.code == ABS_MT_POSITION_X) - { - if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange - } - - if (event.code == ABS_MT_POSITION_Y) - { - if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange - } - - if (event.code == ABS_MT_TRACKING_ID) - { - if ((event.value < 0) && (worker->touchSlot < MAX_TOUCH_POINTS)) - { - // Touch has ended for this point - CORE.Input.Touch.position[worker->touchSlot].x = -1; - CORE.Input.Touch.position[worker->touchSlot].y = -1; - } - } - - // Touchscreen tap - if (event.code == ABS_PRESSURE) - { - int previousMouseLeftButtonState = CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT]; - - if (!event.value && previousMouseLeftButtonState) - { - CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0; - - touchAction = 0; // TOUCH_ACTION_UP - gestureUpdate = true; - } - - if (event.value && !previousMouseLeftButtonState) - { - CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1; - - touchAction = 1; // TOUCH_ACTION_DOWN - gestureUpdate = true; - } - } - - } - - // Button parsing - if (event.type == EV_KEY) - { - // Mouse button parsing - if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT)) - { - CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value; - - if (event.value > 0) touchAction = 1; // TOUCH_ACTION_DOWN - else touchAction = 0; // TOUCH_ACTION_UP - gestureUpdate = true; - } - - if (event.code == BTN_RIGHT) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value; - if (event.code == BTN_MIDDLE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_MIDDLE] = event.value; - if (event.code == BTN_SIDE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_SIDE] = event.value; - if (event.code == BTN_EXTRA) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_EXTRA] = event.value; - if (event.code == BTN_FORWARD) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_FORWARD] = event.value; - if (event.code == BTN_BACK) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_BACK] = event.value; - } - - // Screen confinement - if (!CORE.Input.Mouse.cursorHidden) - { - if (CORE.Input.Mouse.currentPosition.x < 0) CORE.Input.Mouse.currentPosition.x = 0; - if (CORE.Input.Mouse.currentPosition.x > CORE.Window.screen.width/CORE.Input.Mouse.scale.x) CORE.Input.Mouse.currentPosition.x = CORE.Window.screen.width/CORE.Input.Mouse.scale.x; - - if (CORE.Input.Mouse.currentPosition.y < 0) CORE.Input.Mouse.currentPosition.y = 0; - if (CORE.Input.Mouse.currentPosition.y > CORE.Window.screen.height/CORE.Input.Mouse.scale.y) CORE.Input.Mouse.currentPosition.y = CORE.Window.screen.height/CORE.Input.Mouse.scale.y; - } - - // Update touch point count - CORE.Input.Touch.pointCount = 0; - for (int i = 0; i < MAX_TOUCH_POINTS; i++) - { - if (CORE.Input.Touch.position[i].x >= 0) CORE.Input.Touch.pointCount++; - } - -#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_DRM - if (gestureUpdate) - { - GestureEvent gestureEvent = { 0 }; - - gestureEvent.touchAction = touchAction; - gestureEvent.pointCount = CORE.Input.Touch.pointCount; - - for (int i = 0; i < MAX_TOUCH_POINTS; i++) - { - gestureEvent.pointId[i] = i; - gestureEvent.position[i] = CORE.Input.Touch.position[i]; - } - - ProcessGestureEvent(gestureEvent); - } -#endif - } - - WaitTime(0.005); // Sleep for 5ms to avoid hogging CPU time - } - - close(worker->fd); - - return NULL; -} - -// Initialize gamepad system -static void InitGamepad(void) -{ - char gamepadDev[128] = { 0 }; - - for (int i = 0; i < MAX_GAMEPADS; i++) - { - sprintf(gamepadDev, "%s%i", DEFAULT_GAMEPAD_DEV, i); - - if ((CORE.Input.Gamepad.streamId[i] = open(gamepadDev, O_RDONLY | O_NONBLOCK)) < 0) - { - // NOTE: Only show message for first gamepad - if (i == 0) TRACELOG(LOG_WARNING, "RPI: Failed to open Gamepad device, no gamepad available"); - } - else - { - CORE.Input.Gamepad.ready[i] = true; - - // NOTE: Only create one thread - if (i == 0) - { - int error = pthread_create(&CORE.Input.Gamepad.threadId, NULL, &GamepadThread, NULL); - - if (error != 0) TRACELOG(LOG_WARNING, "RPI: Failed to create gamepad input event thread"); - else TRACELOG(LOG_INFO, "RPI: Gamepad device initialized successfully"); - } - } - } -} - -// Process Gamepad (/dev/input/js0) -static void *GamepadThread(void *arg) -{ - #define JS_EVENT_BUTTON 0x01 // Button pressed/released - #define JS_EVENT_AXIS 0x02 // Joystick axis moved - #define JS_EVENT_INIT 0x80 // Initial state of device - - struct js_event { - unsigned int time; // event timestamp in milliseconds - short value; // event value - unsigned char type; // event type - unsigned char number; // event axis/button number - }; - - // Read gamepad event - struct js_event gamepadEvent = { 0 }; - - while (!CORE.Window.shouldClose) - { - for (int i = 0; i < MAX_GAMEPADS; i++) - { - if (read(CORE.Input.Gamepad.streamId[i], &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event)) - { - gamepadEvent.type &= ~JS_EVENT_INIT; // Ignore synthetic events - - // Process gamepad events by type - if (gamepadEvent.type == JS_EVENT_BUTTON) - { - //TRACELOG(LOG_WARNING, "RPI: Gamepad button: %i, value: %i", gamepadEvent.number, gamepadEvent.value); - - if (gamepadEvent.number < MAX_GAMEPAD_BUTTONS) - { - // 1 - button pressed, 0 - button released - CORE.Input.Gamepad.currentButtonState[i][gamepadEvent.number] = (int)gamepadEvent.value; - - if ((int)gamepadEvent.value == 1) CORE.Input.Gamepad.lastButtonPressed = gamepadEvent.number; - else CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - } - } - else if (gamepadEvent.type == JS_EVENT_AXIS) - { - //TRACELOG(LOG_WARNING, "RPI: Gamepad axis: %i, value: %i", gamepadEvent.number, gamepadEvent.value); - - if (gamepadEvent.number < MAX_GAMEPAD_AXIS) - { - // NOTE: Scaling of gamepadEvent.value to get values between -1..1 - CORE.Input.Gamepad.axisState[i][gamepadEvent.number] = (float)gamepadEvent.value/32768; - } - } - } - else WaitTime(0.001); // Sleep for 1 ms to avoid hogging CPU time - } - } - - return NULL; -} -#endif // PLATFORM_DRM - -#if defined(PLATFORM_DRM) -// Search matching DRM mode in connector's mode list -static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode) -{ - if (NULL == connector) return -1; - if (NULL == mode) return -1; - - // safe bitwise comparison of two modes - #define BINCMP(a, b) memcmp((a), (b), (sizeof(a) < sizeof(b)) ? sizeof(a) : sizeof(b)) - - for (size_t i = 0; i < connector->count_modes; i++) - { - TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, connector->modes[i].hdisplay, connector->modes[i].vdisplay, - connector->modes[i].vrefresh, (connector->modes[i].flags & DRM_MODE_FLAG_INTERLACE) ? "interlaced" : "progressive"); - - if (0 == BINCMP(&CORE.Window.crtc->mode, &CORE.Window.connector->modes[i])) return i; - } - - return -1; - - #undef BINCMP -} - -// Search exactly matching DRM connector mode in connector's list -static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced) -{ - TRACELOG(LOG_TRACE, "DISPLAY: Searching exact connector mode for %ux%u@%u, selecting an interlaced mode is allowed: %s", width, height, fps, allowInterlaced ? "yes" : "no"); - - if (NULL == connector) return -1; - - for (int i = 0; i < CORE.Window.connector->count_modes; i++) - { - const drmModeModeInfo *const mode = &CORE.Window.connector->modes[i]; - - TRACELOG(LOG_TRACE, "DISPLAY: DRM Mode %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE) ? "interlaced" : "progressive"); - - if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced)) continue; - - if ((mode->hdisplay == width) && (mode->vdisplay == height) && (mode->vrefresh == fps)) return i; - } - - TRACELOG(LOG_TRACE, "DISPLAY: No DRM exact matching mode found"); - return -1; -} - -// Search the nearest matching DRM connector mode in connector's list -static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced) -{ - TRACELOG(LOG_TRACE, "DISPLAY: Searching nearest connector mode for %ux%u@%u, selecting an interlaced mode is allowed: %s", width, height, fps, allowInterlaced ? "yes" : "no"); - - if (NULL == connector) return -1; - - int nearestIndex = -1; - for (int i = 0; i < CORE.Window.connector->count_modes; i++) - { - const drmModeModeInfo *const mode = &CORE.Window.connector->modes[i]; - - TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh, - (mode->flags & DRM_MODE_FLAG_INTERLACE) ? "interlaced" : "progressive"); - - if ((mode->hdisplay < width) || (mode->vdisplay < height)) - { - TRACELOG(LOG_TRACE, "DISPLAY: DRM mode is too small"); - continue; - } - - if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced)) - { - TRACELOG(LOG_TRACE, "DISPLAY: DRM shouldn't choose an interlaced mode"); - continue; - } - - if (nearestIndex < 0) - { - nearestIndex = i; - continue; - } - - const int widthDiff = abs(mode->hdisplay - width); - const int heightDiff = abs(mode->vdisplay - height); - const int fpsDiff = abs(mode->vrefresh - fps); - - const int nearestWidthDiff = abs(CORE.Window.connector->modes[nearestIndex].hdisplay - width); - const int nearestHeightDiff = abs(CORE.Window.connector->modes[nearestIndex].vdisplay - height); - const int nearestFpsDiff = abs(CORE.Window.connector->modes[nearestIndex].vrefresh - fps); - - if ((widthDiff < nearestWidthDiff) || (heightDiff < nearestHeightDiff) || (fpsDiff < nearestFpsDiff)) { - nearestIndex = i; - } - } - - return nearestIndex; -} -#endif - #if defined(SUPPORT_EVENTS_AUTOMATION) // NOTE: Loading happens over AutomationEvent *events // TODO: This system should probably be redesigned diff --git a/src/rcore.h b/src/rcore.h new file mode 100644 index 000000000..5d9e3bd74 --- /dev/null +++ b/src/rcore.h @@ -0,0 +1,318 @@ +/********************************************************************************************** +* +* rcore - Common types and globals (all platforms) +* +* LIMITATIONS: +* - Limitation 01 +* - Limitation 02 +* +* POSSIBLE IMPROVEMENTS: +* - Improvement 01 +* - Improvement 02 +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) and contributors +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef RCORE_H +#define RCORE_H + +#include // Required for: srand(), rand(), atexit() +#include // Required for: sprintf() [Used in OpenURL()] +#include // Required for: strrchr(), strcmp(), strlen(), memset() +#include // Required for: time() [Used in InitTimer()] +#include // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()] + +#include "utils.h" // Required for: TRACELOG() macros + +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) + #define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 + // NOTE: Already provided by rlgl implementation (on glad.h) + #include "GLFW/glfw3.h" // GLFW3 library: Windows, OpenGL context and Input management + // NOTE: GLFW3 already includes gl.h (OpenGL) headers +#endif + +#if defined(PLATFORM_ANDROID) + #include // Native platform windowing system interface + //#include // OpenGL ES 2.0 library (not required in this module, only in rlgl) +#endif + +#if defined(PLATFORM_DRM) + + #include // POSIX file control definitions - open(), creat(), fcntl() + #include // POSIX standard function definitions - read(), close(), STDIN_FILENO + #include // POSIX terminal control definitions - tcgetattr(), tcsetattr() + #include // POSIX threads management (inputs reading) + #include // POSIX directory browsing + + #include // Required for: ioctl() - UNIX System call for device-specific input/output operations + #include // Linux: KDSKBMODE, K_MEDIUMRAM constants definition + #include // Linux: Keycodes constants definition (KEY_A, ...) + #include // Linux: Joystick support library + + #include // Generic Buffer Management (native platform for EGL on DRM) + #include // Direct Rendering Manager user-level library interface + #include // Direct Rendering Manager mode setting (KMS) interface + + #include "EGL/egl.h" // Native platform windowing system interface + #include "EGL/eglext.h" // EGL extensions + + typedef struct + { + pthread_t threadId; // Event reading thread id + int fd; // File descriptor to the device it is assigned to + int eventNum; // Number of 'event' device + Rectangle absRange; // Range of values for absolute pointing devices (touchscreens) + int touchSlot; // Hold the touch slot number of the currently being sent multitouch block + bool isMouse; // True if device supports relative X Y movements + bool isTouch; // True if device supports absolute X Y movements and has BTN_TOUCH + bool isMultitouch; // True if device supports multiple absolute movevents and has BTN_TOUCH + bool isKeyboard; // True if device has letter keycodes + bool isGamepad; // True if device has gamepad buttons + } InputEventWorker; + +#endif + +// TODO: PROVIDE A HEADER TO BE USED BY ALL THE rcore_* IMPLEMENTATIONS + +#include "raylib.h" + +#include "rlgl.h" + +#define RAYMATH_IMPLEMENTATION +#include "raymath.h" + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#if defined(PLATFORM_DRM) + #define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event number + + #define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...) + #define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events +#endif + +#ifndef MAX_FILEPATH_CAPACITY + #define MAX_FILEPATH_CAPACITY 8192 // Maximum capacity for filepath +#endif +#ifndef MAX_FILEPATH_LENGTH + #define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value) +#endif + +#ifndef MAX_KEYBOARD_KEYS + #define MAX_KEYBOARD_KEYS 512 // Maximum number of keyboard keys supported +#endif +#ifndef MAX_MOUSE_BUTTONS + #define MAX_MOUSE_BUTTONS 8 // Maximum number of mouse buttons supported +#endif +#ifndef MAX_GAMEPADS + #define MAX_GAMEPADS 4 // Maximum number of gamepads supported +#endif +#ifndef MAX_GAMEPAD_AXIS + #define MAX_GAMEPAD_AXIS 8 // Maximum number of axis supported (per gamepad) +#endif +#ifndef MAX_GAMEPAD_BUTTONS + #define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad) +#endif +#ifndef MAX_TOUCH_POINTS + #define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported +#endif +#ifndef MAX_KEY_PRESSED_QUEUE + #define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue +#endif +#ifndef MAX_CHAR_PRESSED_QUEUE + #define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue +#endif + +#ifndef MAX_DECOMPRESSION_SIZE + #define MAX_DECOMPRESSION_SIZE 64 // Maximum size allocated for decompression in MB +#endif + +// Flags operation macros +#define FLAG_SET(n, f) ((n) |= (f)) +#define FLAG_CLEAR(n, f) ((n) &= ~(f)) +#define FLAG_TOGGLE(n, f) ((n) ^= (f)) +#define FLAG_CHECK(n, f) ((n) & (f)) + +// TODO: HACK: Added flag if not provided by GLFW when using external library +// Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev +#if !defined(GLFW_MOUSE_PASSTHROUGH) + #define GLFW_MOUSE_PASSTHROUGH 0x0002000D +#endif + +#if (defined(__linux__) || defined(PLATFORM_WEB)) && (_POSIX_C_SOURCE < 199309L) + #undef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext. +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef struct { int x; int y; } Point; +typedef struct { unsigned int width; unsigned int height; } Size; + +// Core global state context data +typedef struct CoreData { + struct { +#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) + GLFWwindow *handle; // GLFW window handle (graphic device) +#endif +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) +#if defined(PLATFORM_DRM) + int fd; // File descriptor for /dev/dri/... + drmModeConnector *connector; // Direct Rendering Manager (DRM) mode connector + drmModeCrtc *crtc; // CRT Controller + int modeIndex; // Index of the used mode of connector->modes + struct gbm_device *gbmDevice; // GBM device + struct gbm_surface *gbmSurface; // GBM surface + struct gbm_bo *prevBO; // Previous GBM buffer object (during frame swapping) + uint32_t prevFB; // Previous GBM framebufer (during frame swapping) +#endif // PLATFORM_DRM + EGLDisplay device; // Native display device (physical screen connection) + EGLSurface surface; // Surface to draw on, framebuffers (connected to context) + EGLContext context; // Graphic context, mode in which drawing can be done + EGLConfig config; // Graphic config +#endif + const char *title; // Window text title const pointer + unsigned int flags; // Configuration flags (bit based), keeps window state + bool ready; // Check if window has been initialized successfully + bool fullscreen; // Check if fullscreen mode is enabled + bool shouldClose; // Check if window set for closing + bool resizedLastFrame; // Check if window has been resized last frame + bool eventWaiting; // Wait for events before ending frame + + Point position; // Window position (required on fullscreen toggle) + Point previousPosition; // Window previous position (required on borderless windowed toggle) + Size display; // Display width and height (monitor, device-screen, LCD, ...) + Size screen; // Screen width and height (used render area) + Size previousScreen; // Screen previous width and height (required on borderless windowed toggle) + Size currentFbo; // Current render width and height (depends on active fbo) + Size render; // Framebuffer width and height (render area, including black bars if required) + Point renderOffset; // Offset from render area (must be divided by 2) + Size screenMin; // Screen minimum width and height (for resizable window) + Size screenMax; // Screen maximum width and height (for resizable window) + Size windowMin; // Window minimum width and height + Size windowMax; // Window maximum width and height + Matrix screenScale; // Matrix to scale screen (framebuffer rendering) + + char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) + unsigned int dropFileCount; // Count dropped files strings + + } Window; +#if defined(PLATFORM_ANDROID) + struct { + bool appEnabled; // Flag to detect if app is active ** = true + struct android_app *app; // Android activity + struct android_poll_source *source; // Android events polling source + bool contextRebindRequired; // Used to know context rebind required + } Android; +#endif + struct { + const char *basePath; // Base path for data storage + } Storage; + struct { +#if defined(PLATFORM_DRM) + InputEventWorker eventWorker[10]; // List of worker threads for every monitored "/dev/input/event" +#endif + struct { + int exitKey; // Default exit key + char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state + char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state + // NOTE: Since key press logic involves comparing prev vs cur key state, we need to handle key repeats specially + char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame. + + int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue + int keyPressedQueueCount; // Input keys queue count + + int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode) + int charPressedQueueCount; // Input characters queue count + +#if defined(PLATFORM_DRM) + int defaultMode; // Default keyboard mode +#if defined(SUPPORT_SSH_KEYBOARD_RPI) + bool evtMode; // Keyboard in event mode +#endif + int defaultFileFlags; // Default IO file flags + struct termios defaultSettings; // Default keyboard settings + int fd; // File descriptor for the evdev keyboard +#endif + } Keyboard; + struct { + Vector2 offset; // Mouse offset + Vector2 scale; // Mouse scaling + Vector2 currentPosition; // Mouse position on screen + Vector2 previousPosition; // Previous mouse position + + int cursor; // Tracks current mouse cursor + bool cursorHidden; // Track if cursor is hidden + bool cursorOnScreen; // Tracks if cursor is inside client area + + char currentButtonState[MAX_MOUSE_BUTTONS]; // Registers current mouse button state + char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state + Vector2 currentWheelMove; // Registers current mouse wheel variation + Vector2 previousWheelMove; // Registers previous mouse wheel variation +#if defined(PLATFORM_DRM) + Vector2 eventWheelMove; // Registers the event mouse wheel variation + // NOTE: currentButtonState[] can't be written directly due to multithreading, app could miss the update + char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab +#endif + } Mouse; + struct { + int pointCount; // Number of touch points active + int pointId[MAX_TOUCH_POINTS]; // Point identifiers + Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen + char currentTouchState[MAX_TOUCH_POINTS]; // Registers current touch state + char previousTouchState[MAX_TOUCH_POINTS]; // Registers previous touch state + } Touch; + struct { + int lastButtonPressed; // Register last gamepad button pressed + int axisCount; // Register number of available gamepad axis + bool ready[MAX_GAMEPADS]; // Flag to know if gamepad is ready + char name[MAX_GAMEPADS][64]; // Gamepad name holder + char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state + char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state + float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state +#if defined(PLATFORM_DRM) + pthread_t threadId; // Gamepad reading thread id + int streamId[MAX_GAMEPADS]; // Gamepad device file descriptor +#endif + } Gamepad; + } Input; + struct { + double current; // Current time measure + double previous; // Previous time measure + double update; // Time measure for frame update + double draw; // Time measure for frame draw + double frame; // Time measure for one frame + double target; // Desired time for one frame, if 0 not applied +#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) + unsigned long long int base; // Base time measure for hi-res timer +#endif + unsigned int frameCounter; // Frame counter + } Time; +} CoreData; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; + +#endif diff --git a/src/rcore_android.c b/src/rcore_android.c new file mode 100644 index 000000000..61d729a1c --- /dev/null +++ b/src/rcore_android.c @@ -0,0 +1,1267 @@ +/********************************************************************************************** +* +* rcore_android - Functions to manage window, graphics device and inputs +* +* PLATFORM: ANDROID +* - Android (ARM, ARM64) +* +* LIMITATIONS: +* - Limitation 01 +* - Limitation 02 +* +* POSSIBLE IMPROVEMENTS: +* - Improvement 01 +* - Improvement 02 +* +* ADDITIONAL NOTES: +* - TRACELOG() function is located in raylib [utils] module +* +* CONFIGURATION: +* #define RCORE_ANDROID_CUSTOM_FLAG +* Custom flag for rcore on PLATFORM_ANDROID -not used- +* +* DEPENDENCIES: +* Android NDK - Provides C API to access Android functionality +* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) and contributors +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#include "rcore.h" + +//#include // Required for: Android sensors functions (accelerometer, gyroscope, light...) +#include // Required for: AWINDOW_FLAG_FULLSCREEN definition and others +#include // Required for: android_app struct and activity management +#include // Required for: JNIEnv and JavaVM [Used in OpenURL()] + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; // Global CORE state context + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static bool InitGraphicsDevice(int width, int height); // Initialize graphics device + +static void AndroidCommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands +static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event); // Process Android inputs +static GamepadButton AndroidTranslateGamepadButton(int button); // Map Android gamepad button to raylib gamepad button + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +// NOTE: Functions declaration is provided by raylib.h + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// To allow easier porting to android, we allow the user to define a +// main function which we call from android_main, defined by ourselves +extern int main(int argc, char *argv[]); + +// Android main function +void android_main(struct android_app *app) +{ + char arg0[] = "raylib"; // NOTE: argv[] are mutable + CORE.Android.app = app; + + // NOTE: Return from main is ignored + (void)main(1, (char *[]) { arg0, NULL }); + + // Request to end the native activity + ANativeActivity_finish(app->activity); + + // Android ALooper_pollAll() variables + int pollResult = 0; + int pollEvents = 0; + + // Waiting for application events before complete finishing + while (!app->destroyRequested) + { + while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void **)&CORE.Android.source)) >= 0) + { + if (CORE.Android.source != NULL) CORE.Android.source->process(app, CORE.Android.source); + } + } +} + +// NOTE: Add this to header (if apps really need it) +struct android_app *GetAndroidApp(void) +{ + return CORE.Android.app; +} + +// Initialize window and OpenGL context +// NOTE: data parameter could be used to pass any kind of required data to the initialization +void InitWindow(int width, int height, const char *title) +{ + TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); + + TRACELOG(LOG_INFO, "Supported raylib modules:"); + TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); + TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); +#if defined(SUPPORT_MODULE_RSHAPES) + TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXTURES) + TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXT) + TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RMODELS) + TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RAUDIO) + TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); +#endif + + // NOTE: Keep internal pointer to input title string (no copy) + if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; + + // Initialize global input state + memset(&CORE.Input, 0, sizeof(CORE.Input)); + CORE.Input.Keyboard.exitKey = KEY_ESCAPE; + CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; + CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Window.eventWaiting = false; + + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + CORE.Window.currentFbo.width = width; + CORE.Window.currentFbo.height = height; + + // Set desired windows flags before initializing anything + ANativeActivity_setWindowFlags(CORE.Android.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER + + int orientation = AConfiguration_getOrientation(CORE.Android.app->config); + + if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait"); + else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape"); + + // TODO: Automatic orientation doesn't seem to work + if (width <= height) + { + AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_PORT); + TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait"); + } + else + { + AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_LAND); + TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape"); + } + + //AConfiguration_getDensity(CORE.Android.app->config); + //AConfiguration_getKeyboard(CORE.Android.app->config); + //AConfiguration_getScreenSize(CORE.Android.app->config); + //AConfiguration_getScreenLong(CORE.Android.app->config); + + // Initialize App command system + // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()... + CORE.Android.app->onAppCmd = AndroidCommandCallback; + + // Initialize input events system + CORE.Android.app->onInputEvent = AndroidInputCallback; + + // Initialize assets manager + InitAssetManager(CORE.Android.app->activity->assetManager, CORE.Android.app->activity->internalDataPath); + + // Initialize base path for storage + CORE.Storage.basePath = CORE.Android.app->activity->internalDataPath; + + TRACELOG(LOG_INFO, "ANDROID: App initialized successfully"); + + // Android ALooper_pollAll() variables + int pollResult = 0; + int pollEvents = 0; + + // Wait for window to be initialized (display and context) + while (!CORE.Window.ready) + { + // Process events loop + while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) + { + // Process this event + if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); + + // NOTE: Never close window, native activity is controlled by the system! + //if (CORE.Android.app->destroyRequested != 0) CORE.Window.shouldClose = true; + } + } +} + +// Close window and unload OpenGL context +void CloseWindow(void) +{ +#if defined(SUPPORT_GIF_RECORDING) + if (gifRecording) + { + MsfGifResult result = msf_gif_end(&gifState); + msf_gif_free(result); + gifRecording = false; + } +#endif + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + UnloadFontDefault(); // WARNING: Module required: rtext +#endif + + rlglClose(); // De-init rlgl + +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + timeEndPeriod(1); // Restore time period +#endif + + // Close surface, context and display + if (CORE.Window.device != EGL_NO_DISPLAY) + { + eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (CORE.Window.surface != EGL_NO_SURFACE) + { + eglDestroySurface(CORE.Window.device, CORE.Window.surface); + CORE.Window.surface = EGL_NO_SURFACE; + } + + if (CORE.Window.context != EGL_NO_CONTEXT) + { + eglDestroyContext(CORE.Window.device, CORE.Window.context); + CORE.Window.context = EGL_NO_CONTEXT; + } + + eglTerminate(CORE.Window.device); + CORE.Window.device = EGL_NO_DISPLAY; + } + +#if defined(SUPPORT_EVENTS_AUTOMATION) + RL_FREE(events); +#endif + + CORE.Window.ready = false; + TRACELOG(LOG_INFO, "Window closed successfully"); +} + +// Check if application should close +bool WindowShouldClose(void) +{ + if (CORE.Window.ready) return CORE.Window.shouldClose; + else return true; +} + +// Check if window is currently hidden +bool IsWindowHidden(void) +{ + return false; +} + +// Check if window has been minimized +bool IsWindowMinimized(void) +{ + return false; +} + +// Check if window has been maximized +bool IsWindowMaximized(void) +{ + return false; +} + +// Check if window has the focus +bool IsWindowFocused(void) +{ + return CORE.Android.appEnabled; +} + +// Check if window has been resizedLastFrame +bool IsWindowResized(void) +{ + return false; +} + +// Toggle fullscreen mode +void ToggleFullscreen(void) +{ + TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on PLATFORM_ANDROID"); +} + +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_ANDROID"); +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_ANDROID"); +} + +// Set window state: not minimized/maximized +void RestoreWindow(void) +{ + TRACELOG(LOG_WARNING, "RestoreWindow() not available on PLATFORM_ANDROID"); +} + +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_ANDROID"); +} + +// Set window configuration state using flags +void SetWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_ANDROID"); +} + +// Clear window configuration state flags +void ClearWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "ClearWindowState() not available on PLATFORM_ANDROID"); +} + +// Set icon for window +void SetWindowIcon(Image image) +{ + TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_ANDROID"); +} + +// Set icon for window +void SetWindowIcons(Image *images, int count) +{ + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on PLATFORM_ANDROID"); +} + +// Set title for window +void SetWindowTitle(const char *title) +{ + CORE.Window.title = title; +} + +// Set window position on screen (windowed mode) +void SetWindowPosition(int x, int y) +{ + TRACELOG(LOG_WARNING, "SetWindowPosition() not available on PLATFORM_ANDROID"); +} + +// Set monitor for the current window +void SetWindowMonitor(int monitor) +{ + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on PLATFORM_ANDROID"); +} + +// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMinSize(int width, int height) +{ + CORE.Window.windowMin.width = width; + CORE.Window.windowMin.height = height; +} + +// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMaxSize(int width, int height) +{ + CORE.Window.windowMax.width = width; + CORE.Window.windowMax.height = height; +} + +// Set window dimensions +void SetWindowSize(int width, int height) +{ + TRACELOG(LOG_WARNING, "SetWindowSize() not available on PLATFORM_ANDROID"); +} + +// Set window opacity, value opacity is between 0.0 and 1.0 +void SetWindowOpacity(float opacity) +{ + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on PLATFORM_ANDROID"); +} + +// Set window focused +void SetWindowFocused(void) +{ + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_ANDROID"); +} + +// Get native window handle +void *GetWindowHandle(void) +{ + TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_ANDROID"); + return NULL; +} + +// Get number of monitors +int GetMonitorCount(void) +{ + TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on PLATFORM_ANDROID"); + return 1; +} + +// Get number of monitors +int GetCurrentMonitor(void) +{ + TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on PLATFORM_ANDROID"); + return 0; +} + +// Get selected monitor position +Vector2 GetMonitorPosition(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on PLATFORM_ANDROID"); + return (Vector2){ 0, 0 }; +} + +// Get selected monitor width (currently used by monitor) +int GetMonitorWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on PLATFORM_ANDROID"); + return 0; +} + +// Get selected monitor height (currently used by monitor) +int GetMonitorHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on PLATFORM_ANDROID"); + return 0; +} + +// Get selected monitor physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on PLATFORM_ANDROID"); + return 0; +} + +// Get selected monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on PLATFORM_ANDROID"); + return 0; +} + +// Get selected monitor refresh rate +int GetMonitorRefreshRate(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on PLATFORM_ANDROID"); + return 0; +} + +// Get the human-readable, UTF-8 encoded name of the selected monitor +const char *GetMonitorName(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on PLATFORM_ANDROID"); + return ""; +} + +// Get window position XY on monitor +Vector2 GetWindowPosition(void) +{ + TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on PLATFORM_ANDROID"); + return (Vector2){ 0, 0 }; +} + +// Get window scale DPI factor for current monitor +Vector2 GetWindowScaleDPI(void) +{ + TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on PLATFORM_ANDROID"); + return (Vector2){ 1.0f, 1.0f }; +} + +// Set clipboard text content +void SetClipboardText(const char *text) +{ + TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on PLATFORM_ANDROID"); +} + +// Get clipboard text content +// NOTE: returned string is allocated and freed by GLFW +const char *GetClipboardText(void) +{ + TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on PLATFORM_ANDROID"); + return NULL; +} + +// Show mouse cursor +void ShowCursor(void) +{ + CORE.Input.Mouse.cursorHidden = false; +} + +// Hides mouse cursor +void HideCursor(void) +{ + CORE.Input.Mouse.cursorHidden = true; +} + +// Enables cursor (unlock cursor) +void EnableCursor(void) +{ + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + + CORE.Input.Mouse.cursorHidden = false; +} + +// Disables cursor (lock cursor) +void DisableCursor(void) +{ + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + + CORE.Input.Mouse.cursorHidden = true; +} + +// Get elapsed time measure in seconds since InitTimer() +double GetTime(void) +{ + double time = 0.0; + struct timespec ts = { 0 }; + clock_gettime(CLOCK_MONOTONIC, &ts); + unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; + + time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer() + + return time; +} + +// Takes a screenshot of current screen (saved a .png) +void TakeScreenshot(const char *fileName) +{ +#if defined(SUPPORT_MODULE_RTEXTURES) + // Security check to (partially) avoid malicious code on PLATFORM_ANDROID + if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } + + Vector2 scale = GetWindowScaleDPI(); + unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); + Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + + char path[2048] = { 0 }; + strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); + + ExportImage(image, path); // WARNING: Module required: rtextures + RL_FREE(imgData); + + TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); +#else + TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); +#endif +} + +// Open URL with default system browser (if available) +// NOTE: This function is only safe to use if you control the URL given. +// A user could craft a malicious string performing another action. +// Only call this function yourself not with user input or make sure to check the string yourself. +// Ref: https://github.com/raysan5/raylib/issues/686 +void OpenURL(const char *url) +{ + // Security check to (partially) avoid malicious code on PLATFORM_ANDROID + if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); + else + { + JNIEnv *env = NULL; + JavaVM *vm = CORE.Android.app->activity->vm; + (*vm)->AttachCurrentThread(vm, &env, NULL); + + jstring urlString = (*env)->NewStringUTF(env, url); + jclass uriClass = (*env)->FindClass(env, "android/net/Uri"); + jmethodID uriParse = (*env)->GetStaticMethodID(env, uriClass, "parse", "(Ljava/lang/String;)Landroid/net/Uri;"); + jobject uri = (*env)->CallStaticObjectMethod(env, uriClass, uriParse, urlString); + + jclass intentClass = (*env)->FindClass(env, "android/content/Intent"); + jfieldID actionViewId = (*env)->GetStaticFieldID(env, intentClass, "ACTION_VIEW", "Ljava/lang/String;"); + jobject actionView = (*env)->GetStaticObjectField(env, intentClass, actionViewId); + jmethodID newIntent = (*env)->GetMethodID(env, intentClass, "", "(Ljava/lang/String;Landroid/net/Uri;)V"); + jobject intent = (*env)->AllocObject(env, intentClass); + + (*env)->CallVoidMethod(env, intent, newIntent, actionView, uri); + jclass activityClass = (*env)->FindClass(env, "android/app/Activity"); + jmethodID startActivity = (*env)->GetMethodID(env, activityClass, "startActivity", "(Landroid/content/Intent;)V"); + (*env)->CallVoidMethod(env, CORE.Android.app->activity->clazz, startActivity, intent); + + (*vm)->DetachCurrentThread(vm); + } +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Inputs +//---------------------------------------------------------------------------------- + +// Set a custom key to exit program +void SetExitKey(int key) +{ + TRACELOG(LOG_WARNING, "SetExitKey() not implemented on PLATFORM_ANDROID"); +} + +// Get gamepad internal name id +const char *GetGamepadName(int gamepad) +{ + TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on PLATFORM_ANDROID"); + return NULL; +} + +// Get gamepad axis count +int GetGamepadAxisCount(int gamepad) +{ + return CORE.Input.Gamepad.axisCount; +} + +// Set internal gamepad mappings +int SetGamepadMappings(const char *mappings) +{ + TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on PLATFORM_ANDROID"); + return 0; +} + +// Get mouse position X +int GetMouseX(void) +{ + return (int)CORE.Input.Touch.position[0].x; +} + +// Get mouse position Y +int GetMouseY(void) +{ + return (int)CORE.Input.Touch.position[0].y; +} + +// Get mouse position XY +Vector2 GetMousePosition(void) +{ + return GetTouchPosition(0); +} + +// Set mouse position XY +void SetMousePosition(int x, int y) +{ + CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; +} + +// Get mouse wheel movement Y +float GetMouseWheelMove(void) +{ + TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on PLATFORM_ANDROID"); + return 0.0f; +} + +// Set mouse cursor +void SetMouseCursor(int cursor) +{ + TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on PLATFORM_ANDROID"); +} + +// Get touch position X for touch point 0 (relative to screen size) +int GetTouchX(void) +{ + return (int)CORE.Input.Touch.position[0].x; +} + +// Get touch position Y for touch point 0 (relative to screen size) +int GetTouchY(void) +{ + return (int)CORE.Input.Touch.position[0].y; +} + +// Get touch position XY for a touch point index (relative to screen size) +// TODO: Touch position should be scaled depending on display size and render size +Vector2 GetTouchPosition(int index) +{ + Vector2 position = { -1.0f, -1.0f }; + + if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index]; + else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS); + + return position; +} + +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + eglSwapBuffers(CORE.Window.device, CORE.Window.surface); +} + +// Register all input events +void PollInputEvents(void) +{ +#if defined(SUPPORT_GESTURES_SYSTEM) + // NOTE: Gestures update must be called every frame to reset gestures correctly + // because ProcessGestureEvent() is just called on an event, not every frame + UpdateGestures(); +#endif + + // Reset keys/chars pressed registered + CORE.Input.Keyboard.keyPressedQueueCount = 0; + CORE.Input.Keyboard.charPressedQueueCount = 0; + // Reset key repeats + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + + // Reset last gamepad button/axis registered state + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Input.Gamepad.axisCount = 0; + + // Register previous touch states + for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; + + // Reset touch positions + // TODO: It resets on PLATFORM_ANDROID the mouse position and not filled again until a move-event, + // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! + //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; + + // Register previous keys states + // NOTE: Android supports up to 260 keys + for (int i = 0; i < 260; i++) + { + CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } + + // Android ALooper_pollAll() variables + int pollResult = 0; + int pollEvents = 0; + + // Poll Events (registered events) + // NOTE: Activity is paused if not enabled (CORE.Android.appEnabled) + while ((pollResult = ALooper_pollAll(CORE.Android.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) + { + // Process this event + if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); + + // NOTE: Never close window, native activity is controlled by the system! + if (CORE.Android.app->destroyRequested != 0) + { + //CORE.Window.shouldClose = true; + //ANativeActivity_finish(CORE.Android.app->activity); + } + } +} + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize display device and framebuffer +// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size +// If width or height are 0, default display size will be used for framebuffer size +// NOTE: returns false in case graphic device could not be created +static bool InitGraphicsDevice(int width, int height) +{ + CORE.Window.screen.width = width; // User desired width + CORE.Window.screen.height = height; // User desired height + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + + // Set the screen minimum and maximum default values to 0 + CORE.Window.screenMin.width = 0; + CORE.Window.screenMin.height = 0; + CORE.Window.screenMax.width = 0; + CORE.Window.screenMax.height = 0; + + // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... + // ...in top-down or left-right to match display aspect ratio (no weird scaling) + + CORE.Window.fullscreen = true; + CORE.Window.flags |= FLAG_FULLSCREEN_MODE; + + EGLint samples = 0; + EGLint sampleBuffer = 0; + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + samples = 4; + sampleBuffer = 1; + TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4"); + } + + const EGLint framebufferAttribs[] = + { + EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, // Type of context support + EGL_RED_SIZE, 8, // RED color bit depth (alternative: 5) + EGL_GREEN_SIZE, 8, // GREEN color bit depth (alternative: 6) + EGL_BLUE_SIZE, 8, // BLUE color bit depth (alternative: 5) + //EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI) + EGL_DEPTH_SIZE, 16, // Depth buffer size (Required to use Depth testing!) + //EGL_STENCIL_SIZE, 8, // Stencil buffer size + EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA + EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs) + EGL_NONE + }; + + const EGLint contextAttribs[] = + { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + EGLint numConfigs = 0; + + // Get an EGL device connection + CORE.Window.device = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (CORE.Window.device == EGL_NO_DISPLAY) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); + return false; + } + + // Initialize the EGL device connection + if (eglInitialize(CORE.Window.device, NULL, NULL) == EGL_FALSE) + { + // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred. + TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); + return false; + } + + // Get an appropriate EGL framebuffer configuration + eglChooseConfig(CORE.Window.device, framebufferAttribs, &CORE.Window.config, 1, &numConfigs); + + // Set rendering API + eglBindAPI(EGL_OPENGL_ES_API); + + // Create an EGL rendering context + CORE.Window.context = eglCreateContext(CORE.Window.device, CORE.Window.config, EGL_NO_CONTEXT, contextAttribs); + if (CORE.Window.context == EGL_NO_CONTEXT) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); + return false; + } + + // Create an EGL window surface + //--------------------------------------------------------------------------------- + EGLint displayFormat = 0; + + // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry() + // As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID + eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat); + + // At this point we need to manage render size vs screen size + // NOTE: This function use and modify global module variables: + // -> CORE.Window.screen.width/CORE.Window.screen.height + // -> CORE.Window.render.width/CORE.Window.render.height + // -> CORE.Window.screenScale + SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); + + ANativeWindow_setBuffersGeometry(CORE.Android.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat); + //ANativeWindow_setBuffersGeometry(CORE.Android.app->window, 0, 0, displayFormat); // Force use of native display size + + CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, CORE.Android.app->window, NULL); + + // There must be at least one frame displayed before the buffers are swapped + //eglSwapInterval(CORE.Window.device, 1); + + if (eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context) == EGL_FALSE) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); + return false; + } + else + { + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + } + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(eglGetProcAddress); + + // Initialize OpenGL context (states and resources) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + CORE.Window.ready = true; + + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + return true; +} + +// ANDROID: Process activity lifecycle commands +static void AndroidCommandCallback(struct android_app *app, int32_t cmd) +{ + switch (cmd) + { + case APP_CMD_START: + { + //rendering = true; + } break; + case APP_CMD_RESUME: break; + case APP_CMD_INIT_WINDOW: + { + if (app->window != NULL) + { + if (CORE.Android.contextRebindRequired) + { + // Reset screen scaling to full display size + EGLint displayFormat = 0; + eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat); + + // Adding renderOffset here feels rather hackish, but the viewport scaling is wrong after the + // context rebinding if the screen is scaled unless offsets are added. There's probably a more + // appropriate way to fix this + ANativeWindow_setBuffersGeometry(app->window, + CORE.Window.render.width + CORE.Window.renderOffset.x, + CORE.Window.render.height + CORE.Window.renderOffset.y, + displayFormat); + + // Recreate display surface and re-attach OpenGL context + CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, app->window, NULL); + eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context); + + CORE.Android.contextRebindRequired = false; + } + else + { + CORE.Window.display.width = ANativeWindow_getWidth(CORE.Android.app->window); + CORE.Window.display.height = ANativeWindow_getHeight(CORE.Android.app->window); + + // Initialize graphics device (display device and OpenGL context) + InitGraphicsDevice(CORE.Window.screen.width, CORE.Window.screen.height); + + // Initialize hi-res timer + InitTimer(); + + // Initialize random seed + srand((unsigned int)time(NULL)); + + #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + // Load default font + // WARNING: External function: Module required: rtext + LoadFontDefault(); + Rectangle rec = GetFontDefault().recs[95]; + // NOTE: We setup a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering + #if defined(SUPPORT_MODULE_RSHAPES) + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); // WARNING: Module required: rshapes + #endif + #endif + + // TODO: GPU assets reload in case of lost focus (lost context) + // NOTE: This problem has been solved just unbinding and rebinding context from display + /* + if (assetsReloadRequired) + { + for (int i = 0; i < assetCount; i++) + { + // TODO: Unload old asset if required + + // Load texture again to pointed texture + (*textureAsset + i) = LoadTexture(assetPath[i]); + } + } + */ + } + } + } break; + case APP_CMD_GAINED_FOCUS: + { + CORE.Android.appEnabled = true; + //ResumeMusicStream(); + } break; + case APP_CMD_PAUSE: break; + case APP_CMD_LOST_FOCUS: + { + CORE.Android.appEnabled = false; + //PauseMusicStream(); + } break; + case APP_CMD_TERM_WINDOW: + { + // Detach OpenGL context and destroy display surface + // NOTE 1: This case is used when the user exits the app without closing it. We detach the context to ensure everything is recoverable upon resuming. + // NOTE 2: Detaching context before destroying display surface avoids losing our resources (textures, shaders, VBOs...) + // NOTE 3: In some cases (too many context loaded), OS could unload context automatically... :( + if (CORE.Window.device != EGL_NO_DISPLAY) + { + eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (CORE.Window.surface != EGL_NO_SURFACE) + { + eglDestroySurface(CORE.Window.device, CORE.Window.surface); + CORE.Window.surface = EGL_NO_SURFACE; + } + + CORE.Android.contextRebindRequired = true; + } + // If 'CORE.Window.device' is already set to 'EGL_NO_DISPLAY' + // this means that the user has already called 'CloseWindow()' + + } break; + case APP_CMD_SAVE_STATE: break; + case APP_CMD_STOP: break; + case APP_CMD_DESTROY: break; + case APP_CMD_CONFIG_CHANGED: + { + //AConfiguration_fromAssetManager(CORE.Android.app->config, CORE.Android.app->activity->assetManager); + //print_cur_config(CORE.Android.app); + + // Check screen orientation here! + } break; + default: break; + } +} + +// ANDROID: Map Android gamepad button to raylib gamepad button +static GamepadButton AndroidTranslateGamepadButton(int button) +{ + switch (button) + { + case AKEYCODE_BUTTON_A: return GAMEPAD_BUTTON_RIGHT_FACE_DOWN; + case AKEYCODE_BUTTON_B: return GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; + case AKEYCODE_BUTTON_X: return GAMEPAD_BUTTON_RIGHT_FACE_LEFT; + case AKEYCODE_BUTTON_Y: return GAMEPAD_BUTTON_RIGHT_FACE_UP; + case AKEYCODE_BUTTON_L1: return GAMEPAD_BUTTON_LEFT_TRIGGER_1; + case AKEYCODE_BUTTON_R1: return GAMEPAD_BUTTON_RIGHT_TRIGGER_1; + case AKEYCODE_BUTTON_L2: return GAMEPAD_BUTTON_LEFT_TRIGGER_2; + case AKEYCODE_BUTTON_R2: return GAMEPAD_BUTTON_RIGHT_TRIGGER_2; + case AKEYCODE_BUTTON_THUMBL: return GAMEPAD_BUTTON_LEFT_THUMB; + case AKEYCODE_BUTTON_THUMBR: return GAMEPAD_BUTTON_RIGHT_THUMB; + case AKEYCODE_BUTTON_START: return GAMEPAD_BUTTON_MIDDLE_RIGHT; + case AKEYCODE_BUTTON_SELECT: return GAMEPAD_BUTTON_MIDDLE_LEFT; + case AKEYCODE_BUTTON_MODE: return GAMEPAD_BUTTON_MIDDLE; + // On some (most?) gamepads dpad events are reported as axis motion instead + case AKEYCODE_DPAD_DOWN: return GAMEPAD_BUTTON_LEFT_FACE_DOWN; + case AKEYCODE_DPAD_RIGHT: return GAMEPAD_BUTTON_LEFT_FACE_RIGHT; + case AKEYCODE_DPAD_LEFT: return GAMEPAD_BUTTON_LEFT_FACE_LEFT; + case AKEYCODE_DPAD_UP: return GAMEPAD_BUTTON_LEFT_FACE_UP; + default: return GAMEPAD_BUTTON_UNKNOWN; + } +} + +// ANDROID: Get input events +static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) +{ + // If additional inputs are required check: + // https://developer.android.com/ndk/reference/group/input + // https://developer.android.com/training/game-controllers/controller-input + + int type = AInputEvent_getType(event); + int source = AInputEvent_getSource(event); + + if (type == AINPUT_EVENT_TYPE_MOTION) + { + if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) || + ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD)) + { + // For now we'll assume a single gamepad which we "detect" on its input event + CORE.Input.Gamepad.ready[0] = true; + + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_X] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_X, 0); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_Y] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_Y, 0); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_X] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_Z, 0); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_Y] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_RZ, 0); + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_LEFT_TRIGGER] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_BRAKE, 0) * 2.0f - 1.0f; + CORE.Input.Gamepad.axisState[0][GAMEPAD_AXIS_RIGHT_TRIGGER] = AMotionEvent_getAxisValue( + event, AMOTION_EVENT_AXIS_GAS, 0) * 2.0f - 1.0f; + + // dpad is reported as an axis on android + float dpadX = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_X, 0); + float dpadY = AMotionEvent_getAxisValue(event, AMOTION_EVENT_AXIS_HAT_Y, 0); + + if (dpadX == 1.0f) + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 1; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0; + } + else if (dpadX == -1.0f) + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 1; + } + else + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_RIGHT] = 0; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_LEFT] = 0; + } + + if (dpadY == 1.0f) + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 1; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0; + } + else if (dpadY == -1.0f) + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 1; + } + else + { + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_DOWN] = 0; + CORE.Input.Gamepad.currentButtonState[0][GAMEPAD_BUTTON_LEFT_FACE_UP] = 0; + } + + return 1; // Handled gamepad axis motion + } + } + else if (type == AINPUT_EVENT_TYPE_KEY) + { + int32_t keycode = AKeyEvent_getKeyCode(event); + //int32_t AKeyEvent_getMetaState(event); + + // Handle gamepad button presses and releases + if (((source & AINPUT_SOURCE_JOYSTICK) == AINPUT_SOURCE_JOYSTICK) || + ((source & AINPUT_SOURCE_GAMEPAD) == AINPUT_SOURCE_GAMEPAD)) + { + // For now we'll assume a single gamepad which we "detect" on its input event + CORE.Input.Gamepad.ready[0] = true; + + GamepadButton button = AndroidTranslateGamepadButton(keycode); + + if (button == GAMEPAD_BUTTON_UNKNOWN) return 1; + + if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) + { + CORE.Input.Gamepad.currentButtonState[0][button] = 1; + } + else CORE.Input.Gamepad.currentButtonState[0][button] = 0; // Key up + + return 1; // Handled gamepad button + } + + // Save current button and its state + // NOTE: Android key action is 0 for down and 1 for up + if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_DOWN) + { + CORE.Input.Keyboard.currentKeyState[keycode] = 1; // Key down + + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; + CORE.Input.Keyboard.keyPressedQueueCount++; + } + else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_MULTIPLE) CORE.Input.Keyboard.keyRepeatInFrame[keycode] = 1; + else CORE.Input.Keyboard.currentKeyState[keycode] = 0; // Key up + + if (keycode == AKEYCODE_POWER) + { + // Let the OS handle input to avoid app stuck. Behaviour: CMD_PAUSE -> CMD_SAVE_STATE -> CMD_STOP -> CMD_CONFIG_CHANGED -> CMD_LOST_FOCUS + // Resuming Behaviour: CMD_START -> CMD_RESUME -> CMD_CONFIG_CHANGED -> CMD_CONFIG_CHANGED -> CMD_GAINED_FOCUS + // It seems like locking mobile, screen size (CMD_CONFIG_CHANGED) is affected. + // NOTE: AndroidManifest.xml must have + // Before that change, activity was calling CMD_TERM_WINDOW and CMD_DESTROY when locking mobile, so that was not a normal behaviour + return 0; + } + else if ((keycode == AKEYCODE_BACK) || (keycode == AKEYCODE_MENU)) + { + // Eat BACK_BUTTON and AKEYCODE_MENU, just do nothing... and don't let to be handled by OS! + return 1; + } + else if ((keycode == AKEYCODE_VOLUME_UP) || (keycode == AKEYCODE_VOLUME_DOWN)) + { + // Set default OS behaviour + return 0; + } + + return 0; + } + + // Register touch points count + CORE.Input.Touch.pointCount = AMotionEvent_getPointerCount(event); + + for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++) + { + // Register touch points id + CORE.Input.Touch.pointId[i] = AMotionEvent_getPointerId(event, i); + + // Register touch points position + CORE.Input.Touch.position[i] = (Vector2){ AMotionEvent_getX(event, i), AMotionEvent_getY(event, i) }; + + // Normalize CORE.Input.Touch.position[i] for CORE.Window.screen.width and CORE.Window.screen.height + float widthRatio = (float)(CORE.Window.screen.width + CORE.Window.renderOffset.x) / (float)CORE.Window.display.width; + float heightRatio = (float)(CORE.Window.screen.height + CORE.Window.renderOffset.y) / (float)CORE.Window.display.height; + CORE.Input.Touch.position[i].x = CORE.Input.Touch.position[i].x * widthRatio - (float)CORE.Window.renderOffset.x / 2; + CORE.Input.Touch.position[i].y = CORE.Input.Touch.position[i].y * heightRatio - (float)CORE.Window.renderOffset.y / 2; + } + + int32_t action = AMotionEvent_getAction(event); + unsigned int flags = action & AMOTION_EVENT_ACTION_MASK; + +#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_ANDROID + GestureEvent gestureEvent = { 0 }; + + gestureEvent.pointCount = CORE.Input.Touch.pointCount; + + // Register touch actions + if (flags == AMOTION_EVENT_ACTION_DOWN) gestureEvent.touchAction = TOUCH_ACTION_DOWN; + else if (flags == AMOTION_EVENT_ACTION_UP) gestureEvent.touchAction = TOUCH_ACTION_UP; + else if (flags == AMOTION_EVENT_ACTION_MOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE; + else if (flags == AMOTION_EVENT_ACTION_CANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL; + + for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++) + { + gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i]; + gestureEvent.position[i] = CORE.Input.Touch.position[i]; + } + + // Gesture data is sent to gestures system for processing + ProcessGestureEvent(gestureEvent); +#endif + + int32_t pointerIndex = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + + if (flags == AMOTION_EVENT_ACTION_POINTER_UP || flags == AMOTION_EVENT_ACTION_UP) + { + // One of the touchpoints is released, remove it from touch point arrays + for (int i = pointerIndex; (i < CORE.Input.Touch.pointCount - 1) && (i < MAX_TOUCH_POINTS); i++) + { + CORE.Input.Touch.pointId[i] = CORE.Input.Touch.pointId[i+1]; + CORE.Input.Touch.position[i] = CORE.Input.Touch.position[i+1]; + } + + CORE.Input.Touch.pointCount--; + } + + // When all touchpoints are tapped and released really quickly, this event is generated + if (flags == AMOTION_EVENT_ACTION_CANCEL) CORE.Input.Touch.pointCount = 0; + + if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1; + else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0; + + return 0; +} + +// EOF diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c new file mode 100644 index 000000000..f8c6b0e2c --- /dev/null +++ b/src/rcore_desktop.c @@ -0,0 +1,2074 @@ +/********************************************************************************************** +* +* rcore_desktop - Functions to manage window, graphics device and inputs +* +* PLATFORM: DESKTOP +* - Windows (Win32, Win64) +* - Linux (X11/Wayland desktop mode) +* - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +* - OSX/macOS +* +* LIMITATIONS: +* - Limitation 01 +* - Limitation 02 +* +* POSSIBLE IMPROVEMENTS: +* - Improvement 01 +* - Improvement 02 +* +* ADDITIONAL NOTES: +* - TRACELOG() function is located in raylib [utils] module +* +* CONFIGURATION: +* #define RCORE_DESKTOP_CUSTOM_FLAG +* Custom flag for rcore on PLATFORM_DESKTOP -not used- +* +* DEPENDENCIES: +* rglfw - Manage graphic device, OpenGL context and inputs (Windows, Linux, OSX, FreeBSD...) +* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) and contributors +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 + // NOTE: Already provided by rlgl implementation (on glad.h) +#include "GLFW/glfw3.h" // GLFW3 library: Windows, OpenGL context and Input management + // NOTE: GLFW3 already includes gl.h (OpenGL) headers + +// Support retrieving native window handlers +#if defined(_WIN32) + typedef void *PVOID; + typedef PVOID HANDLE; + typedef HANDLE HWND; + #define GLFW_EXPOSE_NATIVE_WIN32 + #define GLFW_NATIVE_INCLUDE_NONE // To avoid some symbols re-definition in windows.h + #include "GLFW/glfw3native.h" + + #if defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + // NOTE: Those functions require linking with winmm library + unsigned int __stdcall timeBeginPeriod(unsigned int uPeriod); + unsigned int __stdcall timeEndPeriod(unsigned int uPeriod); + #endif +#endif +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) + #include // Required for: timespec, nanosleep(), select() - POSIX + + //#define GLFW_EXPOSE_NATIVE_X11 // WARNING: Exposing Xlib.h > X.h results in dup symbols for Font type + //#define GLFW_EXPOSE_NATIVE_WAYLAND + //#define GLFW_EXPOSE_NATIVE_MIR + #include "GLFW/glfw3native.h" // Required for: glfwGetX11Window() +#endif +#if defined(__APPLE__) + #include // Required for: usleep() + + //#define GLFW_EXPOSE_NATIVE_COCOA // WARNING: Fails due to type redefinition + void *glfwGetCocoaWindow(GLFWwindow* handle); + #include "GLFW/glfw3native.h" // Required for: glfwGetCocoaWindow() +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; // Global CORE state context + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static bool InitGraphicsDevice(int width, int height); // Initialize graphics device + +// Error callback event +static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error + +// Window callbacks events +static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized +static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored +static void WindowMaximizeCallback(GLFWwindow* window, int maximized); // GLFW3 Window Maximize Callback, runs when window is maximized +static void WindowFocusCallback(GLFWwindow *window, int focused); // GLFW3 WindowFocus Callback, runs when window get/lose focus +static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window + +// Input callbacks events +static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed +static void CharCallback(GLFWwindow *window, unsigned int key); // GLFW3 Char Key Callback, runs on key pressed (get char value) +static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods); // GLFW3 Mouse Button Callback, runs on mouse button pressed +static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); // GLFW3 Cursor Position Callback, runs on mouse move +static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel +static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +// NOTE: Functions declaration is provided by raylib.h + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize window and OpenGL context +// NOTE: data parameter could be used to pass any kind of required data to the initialization +void InitWindow(int width, int height, const char *title) +{ + TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); + + TRACELOG(LOG_INFO, "Supported raylib modules:"); + TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); + TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); +#if defined(SUPPORT_MODULE_RSHAPES) + TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXTURES) + TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXT) + TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RMODELS) + TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RAUDIO) + TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); +#endif + + // NOTE: Keep internal pointer to input title string (no copy) + if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; + + // Initialize global input state + memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE structure to 0 + CORE.Input.Keyboard.exitKey = KEY_ESCAPE; + CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; + CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Window.eventWaiting = false; + + // Initialize graphics device (display device and OpenGL context) + // NOTE: returns true if window and graphic device has been initialized successfully + CORE.Window.ready = InitGraphicsDevice(width, height); + + // If graphic device is no properly initialized, we end program + if (!CORE.Window.ready) + { + TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); + return; + } + else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); + + // Initialize hi-res timer + InitTimer(); + + // Initialize random seed + srand((unsigned int)time(NULL)); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + // Load default font + // WARNING: External function: Module required: rtext + LoadFontDefault(); + #if defined(SUPPORT_MODULE_RSHAPES) + // Set font white rectangle for shapes drawing, so shapes and text can be batched together + // WARNING: rshapes module is required, if not available, default internal white rectangle is used + Rectangle rec = GetFontDefault().recs[95]; + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); + } + else + { + // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); + } + #endif +#else + #if defined(SUPPORT_MODULE_RSHAPES) + // Set default texture and rectangle to be used for shapes drawing + // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 + Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes + #endif +#endif +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // Set default font texture filter for HighDPI (blurry) + // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); + } +#endif + +#if defined(SUPPORT_EVENTS_AUTOMATION) + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + CORE.Time.frameCounter = 0; +#endif +} + +// Close window and unload OpenGL context +void CloseWindow(void) +{ +#if defined(SUPPORT_GIF_RECORDING) + if (gifRecording) + { + MsfGifResult result = msf_gif_end(&gifState); + msf_gif_free(result); + gifRecording = false; + } +#endif + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + UnloadFontDefault(); // WARNING: Module required: rtext +#endif + + rlglClose(); // De-init rlgl + + glfwDestroyWindow(CORE.Window.handle); + glfwTerminate(); + +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + timeEndPeriod(1); // Restore time period +#endif + +#if defined(SUPPORT_EVENTS_AUTOMATION) + RL_FREE(events); +#endif + + CORE.Window.ready = false; + TRACELOG(LOG_INFO, "Window closed successfully"); +} + +// Check if application should close +// NOTE: By default, if KEY_ESCAPE pressed or window close icon clicked +bool WindowShouldClose(void) +{ + if (CORE.Window.ready) + { + // While window minimized, stop loop execution + while (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)) glfwWaitEvents(); + + CORE.Window.shouldClose = glfwWindowShouldClose(CORE.Window.handle); + + // Reset close status for next frame + glfwSetWindowShouldClose(CORE.Window.handle, GLFW_FALSE); + + return CORE.Window.shouldClose; + } + else return true; +} + +// Check if window is currently hidden +bool IsWindowHidden(void) +{ + return ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0); +} + +// Check if window has been minimized +bool IsWindowMinimized(void) +{ + return ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0); +} + +// Check if window has been maximized +bool IsWindowMaximized(void) +{ + return ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0); +} + +// Check if window has the focus +bool IsWindowFocused(void) +{ + return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0); +} + +// Check if window has been resizedLastFrame +bool IsWindowResized(void) +{ + return CORE.Window.resizedLastFrame; +} + +// Toggle fullscreen mode +void ToggleFullscreen(void) +{ + if (!CORE.Window.fullscreen) + { + // Store previous window position (in case we exit fullscreen) + glfwGetWindowPos(CORE.Window.handle, &CORE.Window.position.x, &CORE.Window.position.y); + + int monitorCount = 0; + int monitorIndex = GetCurrentMonitor(); + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + // Use current monitor, so we correctly get the display the window is on + GLFWmonitor *monitor = (monitorIndex < monitorCount)? monitors[monitorIndex] : NULL; + + if (monitor == NULL) + { + TRACELOG(LOG_WARNING, "GLFW: Failed to get monitor"); + + CORE.Window.fullscreen = false; + CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; + + glfwSetWindowMonitor(CORE.Window.handle, NULL, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + } + else + { + CORE.Window.fullscreen = true; + CORE.Window.flags |= FLAG_FULLSCREEN_MODE; + + glfwSetWindowMonitor(CORE.Window.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + } + + } + else + { + CORE.Window.fullscreen = false; + CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; + + glfwSetWindowMonitor(CORE.Window.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + } + + // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) + // NOTE: V-Sync can be enabled by graphic driver configuration + if (CORE.Window.flags & FLAG_VSYNC_HINT) glfwSwapInterval(1); +} + +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE) + { + glfwMaximizeWindow(CORE.Window.handle); + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; + } +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + // NOTE: Following function launches callback that sets appropriate flag! + glfwIconifyWindow(CORE.Window.handle); +} + +// Set window state: not minimized/maximized +void RestoreWindow(void) +{ + if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE) + { + // Restores the specified window if it was previously iconified (minimized) or maximized + glfwRestoreWindow(CORE.Window.handle); + CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; + CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; + } +} + +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + // Leave fullscreen before attempting to set borderless windowed mode and get screen position from it + bool wasOnFullscreen = false; + if (CORE.Window.fullscreen) + { + CORE.Window.previousPosition = CORE.Window.position; + ToggleFullscreen(); + wasOnFullscreen = true; + } + + const int monitor = GetCurrentMonitor(); + int monitorCount; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + + if (mode) + { + if (!IsWindowState(FLAG_BORDERLESS_WINDOWED_MODE)) + { + // Store screen position and size + // NOTE: If it was on fullscreen, screen position was already stored, so skip setting it here + if (!wasOnFullscreen) glfwGetWindowPos(CORE.Window.handle, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y); + CORE.Window.previousScreen = CORE.Window.screen; + + // Set undecorated and topmost modes and flags + glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE); + CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE); + CORE.Window.flags |= FLAG_WINDOW_TOPMOST; + + // Get monitor position and size + int monitorPosX = 0; + int monitorPosY = 0; + glfwGetMonitorPos(monitors[monitor], &monitorPosX, &monitorPosY); + const int monitorWidth = mode->width; + const int monitorHeight = mode->height; + + // Set screen position and size + glfwSetWindowPos(CORE.Window.handle, monitorPosX, monitorPosY); + glfwSetWindowSize(CORE.Window.handle, monitorWidth, monitorHeight); + + // Refocus window + glfwFocusWindow(CORE.Window.handle); + + CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE; + } + else + { + // Remove topmost and undecorated modes and flags + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE); + CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; + glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); + CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; + + // Return previous screen size and position + // NOTE: The order matters here, it must set size first, then set position, otherwise the screen will be positioned incorrectly + glfwSetWindowSize(CORE.Window.handle, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); + glfwSetWindowPos(CORE.Window.handle, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y); + + // Refocus window + glfwFocusWindow(CORE.Window.handle); + + CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; + } + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); +} + +// Set window configuration state using flags +void SetWindowState(unsigned int flags) +{ + // Check previous state and requested state to apply required changes + // NOTE: In most cases the functions already change the flags internally + + // State change: FLAG_VSYNC_HINT + if (((CORE.Window.flags & FLAG_VSYNC_HINT) != (flags & FLAG_VSYNC_HINT)) && ((flags & FLAG_VSYNC_HINT) > 0)) + { + glfwSwapInterval(1); + CORE.Window.flags |= FLAG_VSYNC_HINT; + } + + // State change: FLAG_BORDERLESS_WINDOWED_MODE + // NOTE: This must be handled before FLAG_FULLSCREEN_MODE because ToggleBorderlessWindowed() needs to get some fullscreen values if fullscreen is running + if (((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) != (flags & FLAG_BORDERLESS_WINDOWED_MODE)) && ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)) + { + ToggleBorderlessWindowed(); // NOTE: Window state flag updated inside function + } + + // State change: FLAG_FULLSCREEN_MODE + if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) != (flags & FLAG_FULLSCREEN_MODE)) + { + ToggleFullscreen(); // NOTE: Window state flag updated inside function + } + + // State change: FLAG_WINDOW_RESIZABLE + if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != (flags & FLAG_WINDOW_RESIZABLE)) && ((flags & FLAG_WINDOW_RESIZABLE) > 0)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_TRUE); + CORE.Window.flags |= FLAG_WINDOW_RESIZABLE; + } + + // State change: FLAG_WINDOW_UNDECORATED + if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) != (flags & FLAG_WINDOW_UNDECORATED)) && (flags & FLAG_WINDOW_UNDECORATED)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE); + CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; + } + + // State change: FLAG_WINDOW_HIDDEN + if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) != (flags & FLAG_WINDOW_HIDDEN)) && ((flags & FLAG_WINDOW_HIDDEN) > 0)) + { + glfwHideWindow(CORE.Window.handle); + CORE.Window.flags |= FLAG_WINDOW_HIDDEN; + } + + // State change: FLAG_WINDOW_MINIMIZED + if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) != (flags & FLAG_WINDOW_MINIMIZED)) && ((flags & FLAG_WINDOW_MINIMIZED) > 0)) + { + //GLFW_ICONIFIED + MinimizeWindow(); // NOTE: Window state flag updated inside function + } + + // State change: FLAG_WINDOW_MAXIMIZED + if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) != (flags & FLAG_WINDOW_MAXIMIZED)) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0)) + { + //GLFW_MAXIMIZED + MaximizeWindow(); // NOTE: Window state flag updated inside function + } + + // State change: FLAG_WINDOW_UNFOCUSED + if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) != (flags & FLAG_WINDOW_UNFOCUSED)) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_FALSE); + CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; + } + + // State change: FLAG_WINDOW_TOPMOST + if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) != (flags & FLAG_WINDOW_TOPMOST)) && ((flags & FLAG_WINDOW_TOPMOST) > 0)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE); + CORE.Window.flags |= FLAG_WINDOW_TOPMOST; + } + + // State change: FLAG_WINDOW_ALWAYS_RUN + if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) != (flags & FLAG_WINDOW_ALWAYS_RUN)) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0)) + { + CORE.Window.flags |= FLAG_WINDOW_ALWAYS_RUN; + } + + // The following states can not be changed after window creation + + // State change: FLAG_WINDOW_TRANSPARENT + if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) != (flags & FLAG_WINDOW_TRANSPARENT)) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0)) + { + TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization"); + } + + // State change: FLAG_WINDOW_HIGHDPI + if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) != (flags & FLAG_WINDOW_HIGHDPI)) && ((flags & FLAG_WINDOW_HIGHDPI) > 0)) + { + TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization"); + } + + // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH + if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) != (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); + CORE.Window.flags |= FLAG_WINDOW_MOUSE_PASSTHROUGH; + } + + // State change: FLAG_MSAA_4X_HINT + if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) != (flags & FLAG_MSAA_4X_HINT)) && ((flags & FLAG_MSAA_4X_HINT) > 0)) + { + TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization"); + } + + // State change: FLAG_INTERLACED_HINT + if (((CORE.Window.flags & FLAG_INTERLACED_HINT) != (flags & FLAG_INTERLACED_HINT)) && ((flags & FLAG_INTERLACED_HINT) > 0)) + { + TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization"); + } +} + +// Clear window configuration state flags +void ClearWindowState(unsigned int flags) +{ + // Check previous state and requested state to apply required changes + // NOTE: In most cases the functions already change the flags internally + + // State change: FLAG_VSYNC_HINT + if (((CORE.Window.flags & FLAG_VSYNC_HINT) > 0) && ((flags & FLAG_VSYNC_HINT) > 0)) + { + glfwSwapInterval(0); + CORE.Window.flags &= ~FLAG_VSYNC_HINT; + } + + // State change: FLAG_BORDERLESS_WINDOWED_MODE + // NOTE: This must be handled before FLAG_FULLSCREEN_MODE because ToggleBorderlessWindowed() needs to get some fullscreen values if fullscreen is running + if (((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0) && ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)) + { + ToggleBorderlessWindowed(); // NOTE: Window state flag updated inside function + } + + // State change: FLAG_FULLSCREEN_MODE + if (((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) && ((flags & FLAG_FULLSCREEN_MODE) > 0)) + { + ToggleFullscreen(); // NOTE: Window state flag updated inside function + } + + // State change: FLAG_WINDOW_RESIZABLE + if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) && ((flags & FLAG_WINDOW_RESIZABLE) > 0)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_FALSE); + CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE; + } + + // State change: FLAG_WINDOW_HIDDEN + if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) && ((flags & FLAG_WINDOW_HIDDEN) > 0)) + { + glfwShowWindow(CORE.Window.handle); + CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; + } + + // State change: FLAG_WINDOW_MINIMIZED + if (((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) && ((flags & FLAG_WINDOW_MINIMIZED) > 0)) + { + RestoreWindow(); // NOTE: Window state flag updated inside function + } + + // State change: FLAG_WINDOW_MAXIMIZED + if (((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) && ((flags & FLAG_WINDOW_MAXIMIZED) > 0)) + { + RestoreWindow(); // NOTE: Window state flag updated inside function + } + + // State change: FLAG_WINDOW_UNDECORATED + if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) && ((flags & FLAG_WINDOW_UNDECORATED) > 0)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); + CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; + } + + // State change: FLAG_WINDOW_UNFOCUSED + if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_TRUE); + CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; + } + + // State change: FLAG_WINDOW_TOPMOST + if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) && ((flags & FLAG_WINDOW_TOPMOST) > 0)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE); + CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; + } + + // State change: FLAG_WINDOW_ALWAYS_RUN + if (((CORE.Window.flags & FLAG_WINDOW_ALWAYS_RUN) > 0) && ((flags & FLAG_WINDOW_ALWAYS_RUN) > 0)) + { + CORE.Window.flags &= ~FLAG_WINDOW_ALWAYS_RUN; + } + + // The following states can not be changed after window creation + + // State change: FLAG_WINDOW_TRANSPARENT + if (((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) && ((flags & FLAG_WINDOW_TRANSPARENT) > 0)) + { + TRACELOG(LOG_WARNING, "WINDOW: Framebuffer transparency can only be configured before window initialization"); + } + + // State change: FLAG_WINDOW_HIGHDPI + if (((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) && ((flags & FLAG_WINDOW_HIGHDPI) > 0)) + { + TRACELOG(LOG_WARNING, "WINDOW: High DPI can only be configured before window initialization"); + } + + // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH + if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); + CORE.Window.flags &= ~FLAG_WINDOW_MOUSE_PASSTHROUGH; + } + + // State change: FLAG_MSAA_4X_HINT + if (((CORE.Window.flags & FLAG_MSAA_4X_HINT) > 0) && ((flags & FLAG_MSAA_4X_HINT) > 0)) + { + TRACELOG(LOG_WARNING, "WINDOW: MSAA can only be configured before window initialization"); + } + + // State change: FLAG_INTERLACED_HINT + if (((CORE.Window.flags & FLAG_INTERLACED_HINT) > 0) && ((flags & FLAG_INTERLACED_HINT) > 0)) + { + TRACELOG(LOG_WARNING, "RPI: Interlaced mode can only be configured before window initialization"); + } +} + +// Set icon for window +// NOTE 1: Image must be in RGBA format, 8bit per channel +// NOTE 2: Image is scaled by the OS for all required sizes +void SetWindowIcon(Image image) +{ + if (image.data == NULL) + { + // Revert to the default window icon, pass in an empty image array + glfwSetWindowIcon(CORE.Window.handle, 0, NULL); + } + else + { + if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) + { + GLFWimage icon[1] = { 0 }; + + icon[0].width = image.width; + icon[0].height = image.height; + icon[0].pixels = (unsigned char *)image.data; + + // NOTE 1: We only support one image icon + // NOTE 2: The specified image data is copied before this function returns + glfwSetWindowIcon(CORE.Window.handle, 1, icon); + } + else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); + } +} + +// Set icon for window, multiple images +// NOTE 1: Images must be in RGBA format, 8bit per channel +// NOTE 2: The multiple images are used depending on provided sizes +// Standard Windows icon sizes: 256, 128, 96, 64, 48, 32, 24, 16 +void SetWindowIcons(Image *images, int count) +{ + if ((images == NULL) || (count <= 0)) + { + // Revert to the default window icon, pass in an empty image array + glfwSetWindowIcon(CORE.Window.handle, 0, NULL); + } + else + { + int valid = 0; + GLFWimage *icons = RL_CALLOC(count, sizeof(GLFWimage)); + + for (int i = 0; i < count; i++) + { + if (images[i].format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) + { + icons[valid].width = images[i].width; + icons[valid].height = images[i].height; + icons[valid].pixels = (unsigned char *)images[i].data; + + valid++; + } + else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); + } + // NOTE: Images data is copied internally before this function returns + glfwSetWindowIcon(CORE.Window.handle, valid, icons); + + RL_FREE(icons); + } +} + +// Set title for window +void SetWindowTitle(const char *title) +{ + CORE.Window.title = title; + glfwSetWindowTitle(CORE.Window.handle, title); +} + +// Set window position on screen (windowed mode) +void SetWindowPosition(int x, int y) +{ + glfwSetWindowPos(CORE.Window.handle, x, y); +} + +// Set monitor for the current window +void SetWindowMonitor(int monitor) +{ + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + if (CORE.Window.fullscreen) + { + TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); + + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + glfwSetWindowMonitor(CORE.Window.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate); + } + else + { + TRACELOG(LOG_INFO, "GLFW: Selected monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); + + const int screenWidth = CORE.Window.screen.width; + const int screenHeight = CORE.Window.screen.height; + int monitorWorkareaX = 0; + int monitorWorkareaY = 0; + int monitorWorkareaWidth = 0; + int monitorWorkareaHeight = 0; + glfwGetMonitorWorkarea(monitors[monitor], &monitorWorkareaX, &monitorWorkareaY, &monitorWorkareaWidth, &monitorWorkareaHeight); + + // If the screen size is larger than the monitor workarea, anchor it on the top left corner, otherwise, center it + if ((screenWidth >= monitorWorkareaWidth) || (screenHeight >= monitorWorkareaHeight)) glfwSetWindowPos(CORE.Window.handle, monitorWorkareaX, monitorWorkareaY); + else + { + const int x = monitorWorkareaX + (monitorWorkareaWidth/2) - (screenWidth/2); + const int y = monitorWorkareaY + (monitorWorkareaHeight/2) - (screenHeight/2); + glfwSetWindowPos(CORE.Window.handle, x, y); + } + } + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); +} + +// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMinSize(int width, int height) +{ + CORE.Window.screenMin.width = width; + CORE.Window.screenMin.height = height; + int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.width; + int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.height; + int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.width; + int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.height; + glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); +} + +// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMaxSize(int width, int height) +{ + CORE.Window.screenMax.width = width; + CORE.Window.screenMax.height = height; + int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.width; + int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.height; + int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.width; + int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.height; + glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); +} + +// Set window dimensions +void SetWindowSize(int width, int height) +{ + glfwSetWindowSize(CORE.Window.handle, width, height); +} + +// Set window opacity, value opacity is between 0.0 and 1.0 +void SetWindowOpacity(float opacity) +{ + if (opacity >= 1.0f) opacity = 1.0f; + else if (opacity <= 0.0f) opacity = 0.0f; + glfwSetWindowOpacity(CORE.Window.handle, opacity); +} + +// Set window focused +void SetWindowFocused(void) +{ + glfwFocusWindow(CORE.Window.handle); +} + +// Get native window handle +void *GetWindowHandle(void) +{ +#if defined(_WIN32) + // NOTE: Returned handle is: void *HWND (windows.h) + return glfwGetWin32Window(CORE.Window.handle); +#endif +#if defined(__linux__) + // NOTE: Returned handle is: unsigned long Window (X.h) + // typedef unsigned long XID; + // typedef XID Window; + //unsigned long id = (unsigned long)glfwGetX11Window(CORE.Window.handle); + //return NULL; // TODO: Find a way to return value... cast to void *? + return (void *)CORE.Window.handle; +#endif +#if defined(__APPLE__) + // NOTE: Returned handle is: (objc_object *) + return (void *)glfwGetCocoaWindow(CORE.Window.handle); +#endif + + return NULL; +} + +// Get number of monitors +int GetMonitorCount(void) +{ + int monitorCount = 0; + + glfwGetMonitors(&monitorCount); + + return monitorCount; +} + +// Get number of monitors +int GetCurrentMonitor(void) +{ + int index = 0; + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + GLFWmonitor *monitor = NULL; + + if (monitorCount >= 1) + { + if (IsWindowFullscreen()) + { + // Get the handle of the monitor that the specified window is in full screen on + monitor = glfwGetWindowMonitor(CORE.Window.handle); + + for (int i = 0; i < monitorCount; i++) + { + if (monitors[i] == monitor) + { + index = i; + break; + } + } + } + else + { + int x = 0; + int y = 0; + + glfwGetWindowPos(CORE.Window.handle, &x, &y); + + for (int i = 0; i < monitorCount; i++) + { + int mx = 0; + int my = 0; + + monitor = monitors[i]; + glfwGetMonitorPos(monitor, &mx, &my); + const GLFWvidmode *mode = glfwGetVideoMode(monitor); + + if (mode) + { + const int width = mode->width; + const int height = mode->height; + + if ((x >= mx) && + (x < (mx + width)) && + (y >= my) && + (y < (my + height))) + { + index = i; + break; + } + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); + } + } + } + + return index; +} + +// Get selected monitor position +Vector2 GetMonitorPosition(int monitor) +{ + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + int x, y; + glfwGetMonitorPos(monitors[monitor], &x, &y); + + return (Vector2){ (float)x, (float)y }; + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); + return (Vector2){ 0, 0 }; +} + +// Get selected monitor width (currently used by monitor) +int GetMonitorWidth(int monitor) +{ + int width = 0; + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + + if (mode) width = mode->width; + else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); + + return width; +} + +// Get selected monitor height (currently used by monitor) +int GetMonitorHeight(int monitor) +{ + int height = 0; + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + + if (mode) height = mode->height; + else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); + + return height; +} + +// Get selected monitor physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ + int width = 0; + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) glfwGetMonitorPhysicalSize(monitors[monitor], &width, NULL); + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); + + return width; +} + +// Get selected monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ + int height = 0; + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &height); + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); + + return height; +} + +// Get selected monitor refresh rate +int GetMonitorRefreshRate(int monitor) +{ + int refresh = 0; + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + const GLFWvidmode *vidmode = glfwGetVideoMode(monitors[monitor]); + refresh = vidmode->refreshRate; + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); + + return refresh; +} + +// Get the human-readable, UTF-8 encoded name of the selected monitor +const char *GetMonitorName(int monitor) +{ + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + return glfwGetMonitorName(monitors[monitor]); + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); + return ""; +} + +// Get window position XY on monitor +Vector2 GetWindowPosition(void) +{ + int x = 0; + int y = 0; + + glfwGetWindowPos(CORE.Window.handle, &x, &y); + + return (Vector2){ (float)x, (float)y }; +} + +// Get window scale DPI factor for current monitor +Vector2 GetWindowScaleDPI(void) +{ + float xdpi = 1.0; + float ydpi = 1.0; + Vector2 scale = { 1.0f, 1.0f }; + Vector2 windowPos = GetWindowPosition(); + + int monitorCount = 0; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + + // Check window monitor + for (int i = 0; i < monitorCount; i++) + { + glfwGetMonitorContentScale(monitors[i], &xdpi, &ydpi); + + int xpos, ypos, width, height; + glfwGetMonitorWorkarea(monitors[i], &xpos, &ypos, &width, &height); + + if ((windowPos.x >= xpos) && (windowPos.x < xpos + width) && + (windowPos.y >= ypos) && (windowPos.y < ypos + height)) + { + scale.x = xdpi; + scale.y = ydpi; + break; + } + } + + return scale; +} + +// Set clipboard text content +void SetClipboardText(const char *text) +{ + glfwSetClipboardString(CORE.Window.handle, text); +} + +// Get clipboard text content +// NOTE: returned string is allocated and freed by GLFW +const char *GetClipboardText(void) +{ + return glfwGetClipboardString(CORE.Window.handle); +} + +// Show mouse cursor +void ShowCursor(void) +{ + glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + CORE.Input.Mouse.cursorHidden = false; +} + +// Hides mouse cursor +void HideCursor(void) +{ + glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + CORE.Input.Mouse.cursorHidden = true; +} + +// Enables cursor (unlock cursor) +void EnableCursor(void) +{ + glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + + CORE.Input.Mouse.cursorHidden = false; +} + +// Disables cursor (lock cursor) +void DisableCursor(void) +{ + glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + + CORE.Input.Mouse.cursorHidden = true; +} + +// Get elapsed time measure in seconds since InitTimer() +double GetTime(void) +{ + double time = glfwGetTime(); // Elapsed time since glfwInit() + return time; +} + +// Takes a screenshot of current screen (saved a .png) +// WARNING: This function requires [rtextures] module functionality +void TakeScreenshot(const char *fileName) +{ +#if defined(SUPPORT_MODULE_RTEXTURES) + // Security check to (partially) avoid malicious code + if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } + + Vector2 scale = GetWindowScaleDPI(); + unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); + Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + + char path[2048] = { 0 }; + strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); + + ExportImage(image, path); // WARNING: Module required: rtextures + RL_FREE(imgData); + + TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); +#else + TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); +#endif +} + +// Open URL with default system browser (if available) +// NOTE: This function is only safe to use if you control the URL given. +// A user could craft a malicious string performing another action. +// Only call this function yourself not with user input or make sure to check the string yourself. +// Ref: https://github.com/raysan5/raylib/issues/686 +void OpenURL(const char *url) +{ + // Security check to (partially) avoid malicious code + if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); + else + { + char *cmd = (char *)RL_CALLOC(strlen(url) + 32, sizeof(char)); +#if defined(_WIN32) + sprintf(cmd, "explorer \"%s\"", url); +#endif +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) + sprintf(cmd, "xdg-open '%s'", url); // Alternatives: firefox, x-www-browser +#endif +#if defined(__APPLE__) + sprintf(cmd, "open '%s'", url); +#endif + int result = system(cmd); + if (result == -1) TRACELOG(LOG_WARNING, "OpenURL() child process could not be created"); + RL_FREE(cmd); + } +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Inputs +//---------------------------------------------------------------------------------- + +// Set a custom key to exit program +// NOTE: default exitKey is ESCAPE +void SetExitKey(int key) +{ + CORE.Input.Keyboard.exitKey = key; +} + +// Get gamepad internal name id +const char *GetGamepadName(int gamepad) +{ + const char *name = NULL; + + if (CORE.Input.Gamepad.ready[gamepad]) name = glfwGetJoystickName(gamepad); + + return name; +} + +// Get gamepad axis count +int GetGamepadAxisCount(int gamepad) +{ + return CORE.Input.Gamepad.axisCount; +} + +// Set internal gamepad mappings +int SetGamepadMappings(const char *mappings) +{ + return glfwUpdateGamepadMappings(mappings); +} + +// Get mouse position X +int GetMouseX(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); +} + +// Get mouse position Y +int GetMouseY(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); +} + +// Get mouse position XY +Vector2 GetMousePosition(void) +{ + Vector2 position = { 0 }; + + position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; + position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; + + return position; +} + +// Set mouse position XY +void SetMousePosition(int x, int y) +{ + CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + + // NOTE: emscripten not implemented + glfwSetCursorPos(CORE.Window.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); +} + +// Get mouse wheel movement Y +float GetMouseWheelMove(void) +{ + float result = 0.0f; + + if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x; + else result = (float)CORE.Input.Mouse.currentWheelMove.y; + + return result; +} + +// Set mouse cursor +void SetMouseCursor(int cursor) +{ + CORE.Input.Mouse.cursor = cursor; + if (cursor == MOUSE_CURSOR_DEFAULT) glfwSetCursor(CORE.Window.handle, NULL); + else + { + // NOTE: We are relating internal GLFW enum values to our MouseCursor enum values + glfwSetCursor(CORE.Window.handle, glfwCreateStandardCursor(0x00036000 + cursor)); + } +} + +// Get touch position X for touch point 0 (relative to screen size) +int GetTouchX(void) +{ + return GetMouseX(); +} + +// Get touch position Y for touch point 0 (relative to screen size) +int GetTouchY(void) +{ + return GetMouseY(); +} + +// Get touch position XY for a touch point index (relative to screen size) +// TODO: Touch position should be scaled depending on display size and render size +Vector2 GetTouchPosition(int index) +{ + Vector2 position = { -1.0f, -1.0f }; + + // TODO: GLFW does not support multi-touch input just yet + // https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch + // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages + if (index == 0) position = GetMousePosition(); + + return position; +} + +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + glfwSwapBuffers(CORE.Window.handle); +} + +// Register all input events +void PollInputEvents(void) +{ +#if defined(SUPPORT_GESTURES_SYSTEM) + // NOTE: Gestures update must be called every frame to reset gestures correctly + // because ProcessGestureEvent() is just called on an event, not every frame + UpdateGestures(); +#endif + + // Reset keys/chars pressed registered + CORE.Input.Keyboard.keyPressedQueueCount = 0; + CORE.Input.Keyboard.charPressedQueueCount = 0; + // Reset key repeats + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + + // Reset last gamepad button/axis registered state + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Input.Gamepad.axisCount = 0; + // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback) + + // Register previous keys states + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) + { + CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } + + // Register previous mouse states + for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; + + // Register previous mouse wheel state + CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; + CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f }; + + // Register previous mouse position + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + + // Register previous touch states + for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; + + // Reset touch positions + // TODO: It resets on PLATFORM_WEB the mouse position and not filled again until a move-event, + // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! + //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; + + // Check if gamepads are ready + // NOTE: We do it here in case of disconnection + for (int i = 0; i < MAX_GAMEPADS; i++) + { + if (glfwJoystickPresent(i)) CORE.Input.Gamepad.ready[i] = true; + else CORE.Input.Gamepad.ready[i] = false; + } + + // Register gamepads buttons events + for (int i = 0; i < MAX_GAMEPADS; i++) + { + if (CORE.Input.Gamepad.ready[i]) // Check if gamepad is available + { + // Register previous gamepad states + for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k]; + + // Get current gamepad state + // NOTE: There is no callback available, so we get it manually + GLFWgamepadstate state = { 0 }; + glfwGetGamepadState(i, &state); // This remapps all gamepads so they have their buttons mapped like an xbox controller + + const unsigned char *buttons = state.buttons; + + for (int k = 0; (buttons != NULL) && (k < GLFW_GAMEPAD_BUTTON_DPAD_LEFT + 1) && (k < MAX_GAMEPAD_BUTTONS); k++) + { + int button = -1; // GamepadButton enum values assigned + + switch (k) + { + case GLFW_GAMEPAD_BUTTON_Y: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; + case GLFW_GAMEPAD_BUTTON_B: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; + case GLFW_GAMEPAD_BUTTON_A: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; + case GLFW_GAMEPAD_BUTTON_X: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; + + case GLFW_GAMEPAD_BUTTON_LEFT_BUMPER: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; + case GLFW_GAMEPAD_BUTTON_RIGHT_BUMPER: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; + + case GLFW_GAMEPAD_BUTTON_BACK: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break; + case GLFW_GAMEPAD_BUTTON_GUIDE: button = GAMEPAD_BUTTON_MIDDLE; break; + case GLFW_GAMEPAD_BUTTON_START: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; + + case GLFW_GAMEPAD_BUTTON_DPAD_UP: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break; + case GLFW_GAMEPAD_BUTTON_DPAD_RIGHT: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; + case GLFW_GAMEPAD_BUTTON_DPAD_DOWN: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; + case GLFW_GAMEPAD_BUTTON_DPAD_LEFT: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; + + case GLFW_GAMEPAD_BUTTON_LEFT_THUMB: button = GAMEPAD_BUTTON_LEFT_THUMB; break; + case GLFW_GAMEPAD_BUTTON_RIGHT_THUMB: button = GAMEPAD_BUTTON_RIGHT_THUMB; break; + default: break; + } + + if (button != -1) // Check for valid button + { + if (buttons[k] == GLFW_PRESS) + { + CORE.Input.Gamepad.currentButtonState[i][button] = 1; + CORE.Input.Gamepad.lastButtonPressed = button; + } + else CORE.Input.Gamepad.currentButtonState[i][button] = 0; + } + } + + // Get current axis state + const float *axes = state.axes; + + for (int k = 0; (axes != NULL) && (k < GLFW_GAMEPAD_AXIS_LAST + 1) && (k < MAX_GAMEPAD_AXIS); k++) + { + CORE.Input.Gamepad.axisState[i][k] = axes[k]; + } + + // Register buttons for 2nd triggers (because GLFW doesn't count these as buttons but rather axis) + CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1f); + CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.1f); + + CORE.Input.Gamepad.axisCount = GLFW_GAMEPAD_AXIS_LAST + 1; + } + } + + CORE.Window.resizedLastFrame = false; + + if (CORE.Window.eventWaiting) glfwWaitEvents(); // Wait for in input events before continue (drawing is paused) + else glfwPollEvents(); // Poll input events: keyboard/mouse/window events (callbacks) +} + + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize display device and framebuffer +// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size +// If width or height are 0, default display size will be used for framebuffer size +// NOTE: returns false in case graphic device could not be created +static bool InitGraphicsDevice(int width, int height) +{ + CORE.Window.screen.width = width; // User desired width + CORE.Window.screen.height = height; // User desired height + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + + // Set the screen minimum and maximum default values to 0 + CORE.Window.screenMin.width = 0; + CORE.Window.screenMin.height = 0; + CORE.Window.screenMax.width = 0; + CORE.Window.screenMax.height = 0; + + // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... + // ...in top-down or left-right to match display aspect ratio (no weird scaling) + + glfwSetErrorCallback(ErrorCallback); +/* + // TODO: Setup GLFW custom allocators to match raylib ones + const GLFWallocator allocator = { + .allocate = MemAlloc, + .deallocate = MemFree, + .reallocate = MemRealloc, + .user = NULL + }; + + glfwInitAllocator(&allocator); +*/ +#if defined(__APPLE__) + glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE); +#endif + + if (!glfwInit()) + { + TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); + return false; + } + + glfwDefaultWindowHints(); // Set default windows hints + //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits + //glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits + //glfwWindowHint(GLFW_BLUE_BITS, 8); // Framebuffer blue color component bits + //glfwWindowHint(GLFW_ALPHA_BITS, 8); // Framebuffer alpha color component bits + //glfwWindowHint(GLFW_DEPTH_BITS, 24); // Depthbuffer bits + //glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window + //glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API + //glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers + + // Check window creation flags + if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true; + + if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window + else glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); // Window initially hidden + + if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window + else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); // Decorated window + + if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window + else glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); // Avoid window being resizable + + // Disable FLAG_WINDOW_MINIMIZED, not supported on initialization + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; + + // Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization + if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; + + if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE); + else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE); + + if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE); + else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE); + + // NOTE: Some GLFW flags are not supported on HTML5 + if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); // Transparent framebuffer + else glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_FALSE); // Opaque framebuffer + + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // Resize window content area based on the monitor content scale. + // NOTE: This hint only has an effect on platforms where screen coordinates and pixels always map 1:1 such as Windows and X11. + // On platforms like macOS the resolution of the framebuffer is changed independently of the window size. + glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_TRUE); // Scale content area based on the monitor content scale where window is placed on +#if defined(__APPLE__) + glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE); +#endif + } + else glfwWindowHint(GLFW_SCALE_TO_MONITOR, GLFW_FALSE); + + // Mouse passthrough + if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); + else glfwWindowHint(GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); + + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + // NOTE: MSAA is only enabled for main framebuffer, not user-created FBOs + TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4"); + glfwWindowHint(GLFW_SAMPLES, 4); // Tries to enable multisampling x4 (MSAA), default is 0 + } + + // NOTE: When asking for an OpenGL context version, most drivers provide the highest supported version + // with backward compatibility to older OpenGL versions. + // For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context. + + // Check selection OpenGL version + if (rlGetVersion() == RL_OPENGL_21) + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); // Choose OpenGL major version (just hint) + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); // Choose OpenGL minor version (just hint) + } + else if (rlGetVersion() == RL_OPENGL_33) + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint) + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above! + // Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE +#if defined(__APPLE__) + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); // OSX Requires forward compatibility +#else + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Forward Compatibility Hint: Only 3.3 and above! +#endif + //glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context + } + else if (rlGetVersion() == RL_OPENGL_43) + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Choose OpenGL major version (just hint) + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); +#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Enable OpenGL Debug Context +#endif + } + else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); + } + else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); + } + + // NOTE: GLFW 3.4+ defers initialization of the Joystick subsystem on the first call to any Joystick related functions. + // Forcing this initialization here avoids doing it on PollInputEvents() called by EndDrawing() after first frame has been just drawn. + // The initialization will still happen and possible delays still occur, but before the window is shown, which is a nicer experience. + // REF: https://github.com/raysan5/raylib/issues/1554 + if (MAX_GAMEPADS > 0) glfwSetJoystickCallback(NULL); + + // Find monitor resolution + GLFWmonitor *monitor = glfwGetPrimaryMonitor(); + if (!monitor) + { + TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor"); + return false; + } + + const GLFWvidmode *mode = glfwGetVideoMode(monitor); + + CORE.Window.display.width = mode->width; + CORE.Window.display.height = mode->height; + + // Set screen width/height to the display width/height if they are 0 + if (CORE.Window.screen.width == 0) CORE.Window.screen.width = CORE.Window.display.width; + if (CORE.Window.screen.height == 0) CORE.Window.screen.height = CORE.Window.display.height; + + if (CORE.Window.fullscreen) + { + // remember center for switchinging from fullscreen to window + if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width)) + { + // If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed. + // Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11. + CORE.Window.position.x = CORE.Window.display.width/4; + CORE.Window.position.y = CORE.Window.display.height/4; + } + else + { + CORE.Window.position.x = CORE.Window.display.width/2 - CORE.Window.screen.width/2; + CORE.Window.position.y = CORE.Window.display.height/2 - CORE.Window.screen.height/2; + } + + if (CORE.Window.position.x < 0) CORE.Window.position.x = 0; + if (CORE.Window.position.y < 0) CORE.Window.position.y = 0; + + // Obtain recommended CORE.Window.display.width/CORE.Window.display.height from a valid videomode for the monitor + int count = 0; + const GLFWvidmode *modes = glfwGetVideoModes(glfwGetPrimaryMonitor(), &count); + + // Get closest video mode to desired CORE.Window.screen.width/CORE.Window.screen.height + for (int i = 0; i < count; i++) + { + if ((unsigned int)modes[i].width >= CORE.Window.screen.width) + { + if ((unsigned int)modes[i].height >= CORE.Window.screen.height) + { + CORE.Window.display.width = modes[i].width; + CORE.Window.display.height = modes[i].height; + break; + } + } + } + TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + + // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example, + // for a desired screen size of 800x450 (16:9), closest supported videomode is 800x600 (4:3), + // framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched + // by the sides to fit all monitor space... + + // Try to setup the most appropriate fullscreen framebuffer for the requested screenWidth/screenHeight + // It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset) + // Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale + // TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed... + // HighDPI monitors are properly considered in a following similar function: SetupViewport() + SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); + + CORE.Window.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL); + + // NOTE: Full-screen change, not working properly... + //glfwSetWindowMonitor(CORE.Window.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + } + else + { + // If we are windowed fullscreen, ensures that window does not minimize when focus is lost + if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width)) + { + glfwWindowHint(GLFW_AUTO_ICONIFY, 0); + } + + // No-fullscreen window creation + CORE.Window.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); + + if (CORE.Window.handle) + { + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + } + } + + if (!CORE.Window.handle) + { + glfwTerminate(); + TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); + return false; + } + + // Set window callback events + glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! + glfwSetWindowMaximizeCallback(CORE.Window.handle, WindowMaximizeCallback); + glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback); + glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback); + glfwSetDropCallback(CORE.Window.handle, WindowDropCallback); + + // Set input callback events + glfwSetKeyCallback(CORE.Window.handle, KeyCallback); + glfwSetCharCallback(CORE.Window.handle, CharCallback); + glfwSetMouseButtonCallback(CORE.Window.handle, MouseButtonCallback); + glfwSetCursorPosCallback(CORE.Window.handle, MouseCursorPosCallback); // Track mouse position changes + glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback); + glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback); + + glfwMakeContextCurrent(CORE.Window.handle); + + glfwSetInputMode(CORE.Window.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) + + glfwSwapInterval(0); // No V-Sync by default + + // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) + // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need + // to be activated on web platforms since VSync is enforced there. + if (CORE.Window.flags & FLAG_VSYNC_HINT) + { + // WARNING: It seems to hit a critical render path in Intel HD Graphics + glfwSwapInterval(1); + TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC"); + } + + int fbWidth = CORE.Window.screen.width; + int fbHeight = CORE.Window.screen.height; + + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling. + // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE); +#if !defined(__APPLE__) + glfwGetFramebufferSize(CORE.Window.handle, &fbWidth, &fbHeight); + + // Screen scaling matrix is required in case desired screen area is different from display area + CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f); + + // Mouse input scaling for the new screen size + SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight); +#endif + } + + CORE.Window.render.width = fbWidth; + CORE.Window.render.height = fbHeight; + CORE.Window.currentFbo.width = fbWidth; + CORE.Window.currentFbo.height = fbHeight; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + + rlLoadExtensions(glfwGetProcAddress); + + // Initialize OpenGL context (states and resources) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + return true; +} + +// GLFW3 Error Callback, runs on GLFW3 error +static void ErrorCallback(int error, const char *description) +{ + TRACELOG(LOG_WARNING, "GLFW: Error: %i Description: %s", error, description); +} + +// GLFW3 WindowSize Callback, runs when window is resizedLastFrame +// NOTE: Window resizing not allowed by default +static void WindowSizeCallback(GLFWwindow *window, int width, int height) +{ + // Reset viewport and projection matrix for new size + SetupViewport(width, height); + + CORE.Window.currentFbo.width = width; + CORE.Window.currentFbo.height = height; + CORE.Window.resizedLastFrame = true; + + if (IsWindowFullscreen()) return; + + // Set current screen size +#if defined(__APPLE__) + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; +#else + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + Vector2 windowScaleDPI = GetWindowScaleDPI(); + + CORE.Window.screen.width = (unsigned int)(width/windowScaleDPI.x); + CORE.Window.screen.height = (unsigned int)(height/windowScaleDPI.y); + } + else + { + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + } +#endif + + // NOTE: Postprocessing texture is not scaled to new size +} + +// GLFW3 WindowIconify Callback, runs when window is minimized/restored +static void WindowIconifyCallback(GLFWwindow *window, int iconified) +{ + if (iconified) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; // The window was iconified + else CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // The window was restored +} + +// GLFW3 WindowMaximize Callback, runs when window is maximized/restored +static void WindowMaximizeCallback(GLFWwindow *window, int maximized) +{ + if (maximized) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // The window was maximized + else CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; // The window was restored +} + +// GLFW3 WindowFocus Callback, runs when window get/lose focus +static void WindowFocusCallback(GLFWwindow *window, int focused) +{ + if (focused) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // The window was focused + else CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; // The window lost focus +} + +// GLFW3 Window Drop Callback, runs when drop files into window +static void WindowDropCallback(GLFWwindow *window, int count, const char **paths) +{ + if (count > 0) + { + // In case previous dropped filepaths have not been freed, we free them + if (CORE.Window.dropFileCount > 0) + { + for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilepaths[i]); + + RL_FREE(CORE.Window.dropFilepaths); + + CORE.Window.dropFileCount = 0; + CORE.Window.dropFilepaths = NULL; + } + + // WARNING: Paths are freed by GLFW when the callback returns, we must keep an internal copy + CORE.Window.dropFileCount = count; + CORE.Window.dropFilepaths = (char **)RL_CALLOC(CORE.Window.dropFileCount, sizeof(char *)); + + for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) + { + CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); + strcpy(CORE.Window.dropFilepaths[i], paths[i]); + } + } +} + +// GLFW3 Keyboard Callback, runs on key pressed +static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) +{ + if (key < 0) return; // Security check, macOS fn key generates -1 + + // WARNING: GLFW could return GLFW_REPEAT, we need to consider it as 1 + // to work properly with our implementation (IsKeyDown/IsKeyUp checks) + if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0; + else if(action == GLFW_PRESS) CORE.Input.Keyboard.currentKeyState[key] = 1; + else if(action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1; + + // WARNING: Check if CAPS/NUM key modifiers are enabled and force down state for those keys + if (((key == KEY_CAPS_LOCK) && ((mods & GLFW_MOD_CAPS_LOCK) > 0)) || + ((key == KEY_NUM_LOCK) && ((mods & GLFW_MOD_NUM_LOCK) > 0))) CORE.Input.Keyboard.currentKeyState[key] = 1; + + // Check if there is space available in the key queue + if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS)) + { + // Add character to the queue + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key; + CORE.Input.Keyboard.keyPressedQueueCount++; + } + + // Check the exit key to set close window + if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(CORE.Window.handle, GLFW_TRUE); + +#if defined(SUPPORT_SCREEN_CAPTURE) + if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) + { +#if defined(SUPPORT_GIF_RECORDING) + if (mods & GLFW_MOD_CONTROL) + { + if (gifRecording) + { + gifRecording = false; + + MsfGifResult result = msf_gif_end(&gifState); + + SaveFileData(TextFormat("%s/screenrec%03i.gif", CORE.Storage.basePath, screenshotCounter), result.data, (unsigned int)result.dataSize); + msf_gif_free(result); + + TRACELOG(LOG_INFO, "SYSTEM: Finish animated GIF recording"); + } + else + { + gifRecording = true; + gifFrameCounter = 0; + + Vector2 scale = GetWindowScaleDPI(); + msf_gif_begin(&gifState, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); + screenshotCounter++; + + TRACELOG(LOG_INFO, "SYSTEM: Start animated GIF recording: %s", TextFormat("screenrec%03i.gif", screenshotCounter)); + } + } + else +#endif // SUPPORT_GIF_RECORDING + { + TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); + screenshotCounter++; + } + } +#endif // SUPPORT_SCREEN_CAPTURE + +#if defined(SUPPORT_EVENTS_AUTOMATION) + if ((key == GLFW_KEY_F11) && (action == GLFW_PRESS)) + { + eventsRecording = !eventsRecording; + + // On finish recording, we export events into a file + if (!eventsRecording) ExportAutomationEvents("eventsrec.rep"); + } + else if ((key == GLFW_KEY_F9) && (action == GLFW_PRESS)) + { + LoadAutomationEvents("eventsrec.rep"); + eventsPlaying = true; + + TRACELOG(LOG_WARNING, "eventsPlaying enabled!"); + } +#endif +} + +// GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value) +static void CharCallback(GLFWwindow *window, unsigned int key) +{ + //TRACELOG(LOG_DEBUG, "Char Callback: KEY:%i(%c)", key, key); + + // NOTE: Registers any key down considering OS keyboard layout but + // does not detect action events, those should be managed by user... + // Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907 + // Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char + + // Check if there is space available in the queue + if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) + { + // Add character to the queue + CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = key; + CORE.Input.Keyboard.charPressedQueueCount++; + } +} + +// GLFW3 Mouse Button Callback, runs on mouse button pressed +static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods) +{ + // WARNING: GLFW could only return GLFW_PRESS (1) or GLFW_RELEASE (0) for now, + // but future releases may add more actions (i.e. GLFW_REPEAT) + CORE.Input.Mouse.currentButtonState[button] = action; + +#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) + // Process mouse events as touches to be able to use mouse-gestures + GestureEvent gestureEvent = { 0 }; + + // Register touch actions + if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) gestureEvent.touchAction = TOUCH_ACTION_DOWN; + else if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) gestureEvent.touchAction = TOUCH_ACTION_UP; + + // NOTE: TOUCH_ACTION_MOVE event is registered in MouseCursorPosCallback() + + // Assign a pointer ID + gestureEvent.pointId[0] = 0; + + // Register touch points count + gestureEvent.pointCount = 1; + + // Register touch points position, only one point registered + gestureEvent.position[0] = GetMousePosition(); + + // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height + gestureEvent.position[0].x /= (float)GetScreenWidth(); + gestureEvent.position[0].y /= (float)GetScreenHeight(); + + // Gesture data is sent to gestures-system for processing + ProcessGestureEvent(gestureEvent); + +#endif +} + +// GLFW3 Cursor Position Callback, runs on mouse move +static void MouseCursorPosCallback(GLFWwindow *window, double x, double y) +{ + CORE.Input.Mouse.currentPosition.x = (float)x; + CORE.Input.Mouse.currentPosition.y = (float)y; + CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; + +#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) + // Process mouse events as touches to be able to use mouse-gestures + GestureEvent gestureEvent = { 0 }; + + gestureEvent.touchAction = TOUCH_ACTION_MOVE; + + // Assign a pointer ID + gestureEvent.pointId[0] = 0; + + // Register touch points count + gestureEvent.pointCount = 1; + + // Register touch points position, only one point registered + gestureEvent.position[0] = CORE.Input.Touch.position[0]; + + // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height + gestureEvent.position[0].x /= (float)GetScreenWidth(); + gestureEvent.position[0].y /= (float)GetScreenHeight(); + + // Gesture data is sent to gestures-system for processing + ProcessGestureEvent(gestureEvent); +#endif +} + +// GLFW3 Scrolling Callback, runs on mouse wheel +static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset) +{ + CORE.Input.Mouse.currentWheelMove = (Vector2){ (float)xoffset, (float)yoffset }; +} + +// GLFW3 CursorEnter Callback, when cursor enters the window +static void CursorEnterCallback(GLFWwindow *window, int enter) +{ + if (enter == true) CORE.Input.Mouse.cursorOnScreen = true; + else CORE.Input.Mouse.cursorOnScreen = false; +} + +// EOF + diff --git a/src/rcore_drm.c b/src/rcore_drm.c new file mode 100644 index 000000000..2c05ab46e --- /dev/null +++ b/src/rcore_drm.c @@ -0,0 +1,2059 @@ +/********************************************************************************************** +* +* rcore_drm - Functions to manage window, graphics device and inputs +* +* PLATFORM: DRM +* - Raspberry Pi 0-5 +* - Linux native mode (KMS driver) +* +* LIMITATIONS: +* - Most of the window/monitor functions are not implemented (not required) +* +* POSSIBLE IMPROVEMENTS: +* - Improvement 01 +* - Improvement 02 +* +* ADDITIONAL NOTES: +* - TRACELOG() function is located in raylib [utils] module +* +* CONFIGURATION: +* #define RCORE_DRM_CUSTOM_FLAG +* Custom flag for rcore on PLATFORM_DRM -not used- +* +* DEPENDENCIES: +* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) and contributors +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#include "rcore.h" + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; // Global CORE state context + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static bool InitGraphicsDevice(int width, int height); // Initialize graphics device + +static void InitKeyboard(void); // Initialize raw keyboard system +static void RestoreKeyboard(void); // Restore keyboard system +#if defined(SUPPORT_SSH_KEYBOARD_RPI) +static void ProcessKeyboard(void); // Process keyboard events +#endif + +static void InitEvdevInput(void); // Initialize evdev inputs +static void ConfigureEvdevDevice(char *device); // Identifies a input device and configures it for use if appropriate +static void PollKeyboardEvents(void); // Process evdev keyboard events +static void *EventThread(void *arg); // Input device events reading thread + +static void InitGamepad(void); // Initialize raw gamepad input +static void *GamepadThread(void *arg); // Mouse reading thread + +static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode); // Search matching DRM mode in connector's mode list +static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced); // Search exactly matching DRM connector mode in connector's list +static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced); // Search the nearest matching DRM connector mode in connector's list + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +// NOTE: Functions declaration is provided by raylib.h + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize window and OpenGL context +// NOTE: data parameter could be used to pass any kind of required data to the initialization +void InitWindow(int width, int height, const char *title) +{ + TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); + + TRACELOG(LOG_INFO, "Supported raylib modules:"); + TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); + TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); +#if defined(SUPPORT_MODULE_RSHAPES) + TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXTURES) + TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXT) + TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RMODELS) + TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RAUDIO) + TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); +#endif + + // NOTE: Keep internal pointer to input title string (no copy) + if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; + + // Initialize global input state + memset(&CORE.Input, 0, sizeof(CORE.Input)); + CORE.Input.Keyboard.exitKey = KEY_ESCAPE; + CORE.Input.Mouse.scale = (Vector2){1.0f, 1.0f}; + CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Window.eventWaiting = false; + + // Initialize graphics device (display device and OpenGL context) + // NOTE: returns true if window and graphic device has been initialized successfully + CORE.Window.ready = InitGraphicsDevice(width, height); + + // If graphic device is no properly initialized, we end program + if (!CORE.Window.ready) + { + TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); + return; + } + else + SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); + + // Initialize hi-res timer + InitTimer(); + + // Initialize random seed + srand((unsigned int)time(NULL)); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + // Load default font + // WARNING: External function: Module required: rtext + LoadFontDefault(); +#if defined(SUPPORT_MODULE_RSHAPES) + // Set font white rectangle for shapes drawing, so shapes and text can be batched together + // WARNING: rshapes module is required, if not available, default internal white rectangle is used + Rectangle rec = GetFontDefault().recs[95]; + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering + SetShapesTexture(GetFontDefault().texture, (Rectangle){rec.x + 2, rec.y + 2, 1, 1}); + } + else + { + // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding + SetShapesTexture(GetFontDefault().texture, (Rectangle){rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2}); + } +#endif +#else +#if defined(SUPPORT_MODULE_RSHAPES) + // Set default texture and rectangle to be used for shapes drawing + // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 + Texture2D texture = {rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8}; + SetShapesTexture(texture, (Rectangle){0.0f, 0.0f, 1.0f, 1.0f}); // WARNING: Module required: rshapes +#endif +#endif +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // Set default font texture filter for HighDPI (blurry) + // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); + } +#endif + + // Initialize raw input system + InitEvdevInput(); // Evdev inputs initialization + InitGamepad(); // Gamepad init + InitKeyboard(); // Keyboard init (stdin) + +#if defined(SUPPORT_EVENTS_AUTOMATION) + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + CORE.Time.frameCounter = 0; +#endif +} + +// Close window and unload OpenGL context +void CloseWindow(void) +{ +#if defined(SUPPORT_GIF_RECORDING) + if (gifRecording) + { + MsfGifResult result = msf_gif_end(&gifState); + msf_gif_free(result); + gifRecording = false; + } +#endif + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + UnloadFontDefault(); // WARNING: Module required: rtext +#endif + + rlglClose(); // De-init rlgl + +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + timeEndPeriod(1); // Restore time period +#endif + + if (CORE.Window.prevFB) + { + drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB); + CORE.Window.prevFB = 0; + } + + if (CORE.Window.prevBO) + { + gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO); + CORE.Window.prevBO = NULL; + } + + if (CORE.Window.gbmSurface) + { + gbm_surface_destroy(CORE.Window.gbmSurface); + CORE.Window.gbmSurface = NULL; + } + + if (CORE.Window.gbmDevice) + { + gbm_device_destroy(CORE.Window.gbmDevice); + CORE.Window.gbmDevice = NULL; + } + + if (CORE.Window.crtc) + { + if (CORE.Window.connector) + { + drmModeSetCrtc(CORE.Window.fd, CORE.Window.crtc->crtc_id, CORE.Window.crtc->buffer_id, + CORE.Window.crtc->x, CORE.Window.crtc->y, &CORE.Window.connector->connector_id, 1, &CORE.Window.crtc->mode); + drmModeFreeConnector(CORE.Window.connector); + CORE.Window.connector = NULL; + } + + drmModeFreeCrtc(CORE.Window.crtc); + CORE.Window.crtc = NULL; + } + + if (CORE.Window.fd != -1) + { + close(CORE.Window.fd); + CORE.Window.fd = -1; + } + + // Close surface, context and display + if (CORE.Window.device != EGL_NO_DISPLAY) + { + if (CORE.Window.surface != EGL_NO_SURFACE) + { + eglDestroySurface(CORE.Window.device, CORE.Window.surface); + CORE.Window.surface = EGL_NO_SURFACE; + } + + if (CORE.Window.context != EGL_NO_CONTEXT) + { + eglDestroyContext(CORE.Window.device, CORE.Window.context); + CORE.Window.context = EGL_NO_CONTEXT; + } + + eglTerminate(CORE.Window.device); + CORE.Window.device = EGL_NO_DISPLAY; + } + + // Wait for mouse and gamepad threads to finish before closing + // NOTE: Those threads should already have finished at this point + // because they are controlled by CORE.Window.shouldClose variable + + CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called + + // Close the evdev keyboard + if (CORE.Input.Keyboard.fd != -1) + { + close(CORE.Input.Keyboard.fd); + CORE.Input.Keyboard.fd = -1; + } + + for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + { + if (CORE.Input.eventWorker[i].threadId) + { + pthread_join(CORE.Input.eventWorker[i].threadId, NULL); + } + } + + if (CORE.Input.Gamepad.threadId) pthread_join(CORE.Input.Gamepad.threadId, NULL); + +#if defined(SUPPORT_EVENTS_AUTOMATION) + RL_FREE(events); +#endif + + CORE.Window.ready = false; + TRACELOG(LOG_INFO, "Window closed successfully"); +} + +// Check if application should close +// NOTE: By default, if KEY_ESCAPE pressed +bool WindowShouldClose(void) +{ + if (CORE.Window.ready) return CORE.Window.shouldClose; + else return true; +} + +// Check if window is currently hidden +bool IsWindowHidden(void) +{ + return false; +} + +// Check if window has been minimized +bool IsWindowMinimized(void) +{ + return false; +} + +// Check if window has been maximized +bool IsWindowMaximized(void) +{ + return false; +} + +// Check if window has the focus +bool IsWindowFocused(void) +{ + return true; +} + +// Check if window has been resizedLastFrame +bool IsWindowResized(void) +{ + return false; +} + +// Toggle fullscreen mode +void ToggleFullscreen(void) +{ + TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on PLATFORM_DRM"); +} + +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_DRM"); +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_DRM"); +} + +// Set window state: not minimized/maximized +void RestoreWindow(void) +{ + TRACELOG(LOG_WARNING, "RestoreWindow() not available on PLATFORM_DRM"); +} + +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_DRM"); +} + +// Set window configuration state using flags +void SetWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_DRM"); +} + +// Clear window configuration state flags +void ClearWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "ClearWindowState() not available on PLATFORM_DRM"); +} + +// Set icon for window +void SetWindowIcon(Image image) +{ + TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_DRM"); +} + +// Set icon for window +void SetWindowIcons(Image *images, int count) +{ + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on PLATFORM_DRM"); +} + +// Set title for window +void SetWindowTitle(const char *title) +{ + CORE.Window.title = title; +} + +// Set window position on screen (windowed mode) +void SetWindowPosition(int x, int y) +{ + TRACELOG(LOG_WARNING, "SetWindowPosition() not available on PLATFORM_DRM"); +} + +// Set monitor for the current window +void SetWindowMonitor(int monitor) +{ + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on PLATFORM_DRM"); +} + +// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMinSize(int width, int height) +{ + CORE.Window.windowMin.width = width; + CORE.Window.windowMin.height = height; +} + +// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMaxSize(int width, int height) +{ + CORE.Window.windowMax.width = width; + CORE.Window.windowMax.height = height; +} + +// Set window dimensions +void SetWindowSize(int width, int height) +{ + TRACELOG(LOG_WARNING, "SetWindowSize() not available on PLATFORM_DRM"); +} + +// Set window opacity, value opacity is between 0.0 and 1.0 +void SetWindowOpacity(float opacity) +{ + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on PLATFORM_DRM"); +} + +// Set window focused +void SetWindowFocused(void) +{ + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_DRM"); +} + +// Get native window handle +void *GetWindowHandle(void) +{ + TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_DRM"); + return NULL; +} + +// Get number of monitors +int GetMonitorCount(void) +{ + TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on PLATFORM_DRM"); + return 1; +} + +// Get number of monitors +int GetCurrentMonitor(void) +{ + TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on PLATFORM_DRM"); + return 0; +} + +// Get selected monitor position +Vector2 GetMonitorPosition(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on PLATFORM_DRM"); + return (Vector2){ 0, 0 }; +} + +// Get selected monitor width (currently used by monitor) +int GetMonitorWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on PLATFORM_DRM"); + return 0; +} + +// Get selected monitor height (currently used by monitor) +int GetMonitorHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on PLATFORM_DRM"); + return 0; +} + +// Get selected monitor physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on PLATFORM_DRM"); + return 0; +} + +// Get selected monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on PLATFORM_DRM"); + return 0; +} + +// Get selected monitor refresh rate +int GetMonitorRefreshRate(int monitor) +{ + int refresh = 0; + + if ((CORE.Window.connector) && (CORE.Window.modeIndex >= 0)) + { + refresh = CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh; + } + + return refresh; +} + +// Get the human-readable, UTF-8 encoded name of the selected monitor +const char *GetMonitorName(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on PLATFORM_DRM"); + return ""; +} + +// Get window position XY on monitor +Vector2 GetWindowPosition(void) +{ + return (Vector2){ 0, 0 }; +} + +// Get window scale DPI factor for current monitor +Vector2 GetWindowScaleDPI(void) +{ + return (Vector2){ 1.0f, 1.0f }; +} + +// Set clipboard text content +void SetClipboardText(const char *text) +{ + TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on PLATFORM_DRM"); +} + +// Get clipboard text content +// NOTE: returned string is allocated and freed by GLFW +const char *GetClipboardText(void) +{ + TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on PLATFORM_DRM"); + return NULL; +} + +// Show mouse cursor +void ShowCursor(void) +{ + CORE.Input.Mouse.cursorHidden = false; +} + +// Hides mouse cursor +void HideCursor(void) +{ + CORE.Input.Mouse.cursorHidden = true; +} + +// Enables cursor (unlock cursor) +void EnableCursor(void) +{ + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + + CORE.Input.Mouse.cursorHidden = false; +} + +// Disables cursor (lock cursor) +void DisableCursor(void) +{ + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + + CORE.Input.Mouse.cursorHidden = true; +} + +// Get elapsed time measure in seconds since InitTimer() +double GetTime(void) +{ + double time = 0.0; + struct timespec ts = { 0 }; + clock_gettime(CLOCK_MONOTONIC, &ts); + unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; + + time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer() + + return time; +} + +// Takes a screenshot of current screen (saved a .png) +void TakeScreenshot(const char *fileName) +{ +#if defined(SUPPORT_MODULE_RTEXTURES) + // Security check to (partially) avoid malicious code on PLATFORM_WEB + if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } + + Vector2 scale = GetWindowScaleDPI(); + unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); + Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + + char path[2048] = { 0 }; + strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); + + ExportImage(image, path); // WARNING: Module required: rtextures + RL_FREE(imgData); + + TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); +#else + TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); +#endif +} + +// Open URL with default system browser (if available) +// NOTE: This function is only safe to use if you control the URL given. +// A user could craft a malicious string performing another action. +// Only call this function yourself not with user input or make sure to check the string yourself. +// Ref: https://github.com/raysan5/raylib/issues/686 +void OpenURL(const char *url) +{ + TRACELOG(LOG_WARNING, "OpenURL() not implemented on PLATFORM_DRM"); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Inputs +//---------------------------------------------------------------------------------- + +// Set a custom key to exit program +// NOTE: default exitKey is ESCAPE +void SetExitKey(int key) +{ + CORE.Input.Keyboard.exitKey = key; +} + +// Get gamepad internal name id +const char *GetGamepadName(int gamepad) +{ + const char *name = NULL; + + if (CORE.Input.Gamepad.ready[gamepad]) + { + ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); + name = CORE.Input.Gamepad.name[gamepad]; + } + + return name; +} + +// Get gamepad axis count +int GetGamepadAxisCount(int gamepad) +{ + int axisCount = 0; + if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGAXES, &axisCount); + CORE.Input.Gamepad.axisCount = axisCount; + + return CORE.Input.Gamepad.axisCount; +} + +// Set internal gamepad mappings +int SetGamepadMappings(const char *mappings) +{ + TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on PLATFORM_DRM"); + return 0; +} + +// Get mouse position X +int GetMouseX(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); +} + +// Get mouse position Y +int GetMouseY(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); +} + +// Get mouse position XY +Vector2 GetMousePosition(void) +{ + Vector2 position = { 0 }; + + position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; + position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; + + return position; +} + +// Set mouse position XY +void SetMousePosition(int x, int y) +{ + CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; +} + +// Get mouse wheel movement Y +float GetMouseWheelMove(void) +{ + float result = 0.0f; + + if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x; + else result = (float)CORE.Input.Mouse.currentWheelMove.y; + + return result; +} + +// Set mouse cursor +void SetMouseCursor(int cursor) +{ + TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on PLATFORM_DRM"); +} + +// Get touch position X for touch point 0 (relative to screen size) +int GetTouchX(void) +{ + return GetMouseX(); +} + +// Get touch position Y for touch point 0 (relative to screen size) +int GetTouchY(void) +{ + return GetMouseY(); +} + +// Get touch position XY for a touch point index (relative to screen size) +Vector2 GetTouchPosition(int index) +{ + Vector2 position = { -1.0f, -1.0f }; + + if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index]; + else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS); + + return position; +} + +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + eglSwapBuffers(CORE.Window.device, CORE.Window.surface); + + if (!CORE.Window.gbmSurface || (-1 == CORE.Window.fd) || !CORE.Window.connector || !CORE.Window.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); + + struct gbm_bo *bo = gbm_surface_lock_front_buffer(CORE.Window.gbmSurface); + if (!bo) TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer"); + + uint32_t fb = 0; + int result = drmModeAddFB(CORE.Window.fd, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, 24, 32, gbm_bo_get_stride(bo), gbm_bo_get_handle(bo).u32, &fb); + if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d", result); + + result = drmModeSetCrtc(CORE.Window.fd, CORE.Window.crtc->crtc_id, fb, 0, 0, &CORE.Window.connector->connector_id, 1, &CORE.Window.connector->modes[CORE.Window.modeIndex]); + if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result); + + if (CORE.Window.prevFB) + { + result = drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB); + if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result); + } + + CORE.Window.prevFB = fb; + + if (CORE.Window.prevBO) gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO); + + CORE.Window.prevBO = bo; +} + +// Register all input events +void PollInputEvents(void) +{ +#if defined(SUPPORT_GESTURES_SYSTEM) + // NOTE: Gestures update must be called every frame to reset gestures correctly + // because ProcessGestureEvent() is just called on an event, not every frame + UpdateGestures(); +#endif + + // Reset keys/chars pressed registered + CORE.Input.Keyboard.keyPressedQueueCount = 0; + CORE.Input.Keyboard.charPressedQueueCount = 0; + + // Reset key repeats + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + + // Reset last gamepad button/axis registered state + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Input.Gamepad.axisCount = 0; + + // Register previous keys states + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) + { + CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } + + PollKeyboardEvents(); + + // Register previous mouse states + CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; + CORE.Input.Mouse.currentWheelMove = CORE.Input.Mouse.eventWheelMove; + CORE.Input.Mouse.eventWheelMove = (Vector2){ 0.0f, 0.0f }; + for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) + { + CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; + CORE.Input.Mouse.currentButtonState[i] = CORE.Input.Mouse.currentButtonStateEvdev[i]; + } + + // Register gamepads buttons events + for (int i = 0; i < MAX_GAMEPADS; i++) + { + if (CORE.Input.Gamepad.ready[i]) + { + // Register previous gamepad states + for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k]; + } + } + + // Register previous touch states + for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; + + // Reset touch positions + // TODO: It resets on PLATFORM_WEB the mouse position and not filled again until a move-event, + // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! + //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; + +#if defined(SUPPORT_SSH_KEYBOARD_RPI) + // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here. + // stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console + + if (!CORE.Input.Keyboard.evtMode) ProcessKeyboard(); + + // NOTE: Mouse input events polling is done asynchronously in another pthread - EventThread() + // NOTE: Gamepad (Joystick) input events polling is done asynchonously in another pthread - GamepadThread() +#endif +} + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize display device and framebuffer +// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size +// If width or height are 0, default display size will be used for framebuffer size +// NOTE: returns false in case graphic device could not be created +static bool InitGraphicsDevice(int width, int height) +{ + CORE.Window.screen.width = width; // User desired width + CORE.Window.screen.height = height; // User desired height + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + + // Set the window minimum and maximum default values to 0 + CORE.Window.windowMin.width = 0; + CORE.Window.windowMin.height = 0; + CORE.Window.windowMax.width = 0; + CORE.Window.windowMax.height = 0; + + // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... + // ...in top-down or left-right to match display aspect ratio (no weird scaling) + + CORE.Window.fullscreen = true; + CORE.Window.flags |= FLAG_FULLSCREEN_MODE; + + CORE.Window.fd = -1; + CORE.Window.connector = NULL; + CORE.Window.modeIndex = -1; + CORE.Window.crtc = NULL; + CORE.Window.gbmDevice = NULL; + CORE.Window.gbmSurface = NULL; + CORE.Window.prevBO = NULL; + CORE.Window.prevFB = 0; + +#if defined(DEFAULT_GRAPHIC_DEVICE_DRM) + CORE.Window.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR); +#else + TRACELOG(LOG_INFO, "DISPLAY: No graphic card set, trying platform-gpu-card"); + CORE.Window.fd = open("/dev/dri/by-path/platform-gpu-card", O_RDWR); // VideoCore VI (Raspberry Pi 4) + + if ((CORE.Window.fd == -1) || (drmModeGetResources(CORE.Window.fd) == NULL)) + { + TRACELOG(LOG_INFO, "DISPLAY: Failed to open platform-gpu-card, trying card1"); + CORE.Window.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded + } + + if ((CORE.Window.fd == -1) || (drmModeGetResources(CORE.Window.fd) == NULL)) + { + TRACELOG(LOG_INFO, "DISPLAY: Failed to open graphic card1, trying card0"); + CORE.Window.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3) + } +#endif + + if (CORE.Window.fd == -1) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card"); + return false; + } + + drmModeRes *res = drmModeGetResources(CORE.Window.fd); + if (!res) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed get DRM resources"); + return false; + } + + TRACELOG(LOG_TRACE, "DISPLAY: Connectors found: %i", res->count_connectors); + + for (size_t i = 0; i < res->count_connectors; i++) + { + TRACELOG(LOG_TRACE, "DISPLAY: Connector index %i", i); + + drmModeConnector *con = drmModeGetConnector(CORE.Window.fd, res->connectors[i]); + TRACELOG(LOG_TRACE, "DISPLAY: Connector modes detected: %i", con->count_modes); + + if ((con->connection == DRM_MODE_CONNECTED) && (con->encoder_id)) + { + TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected"); + CORE.Window.connector = con; + break; + } + else + { + TRACELOG(LOG_TRACE, "DISPLAY: DRM mode NOT connected (deleting)"); + drmModeFreeConnector(con); + } + } + + if (!CORE.Window.connector) + { + TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found"); + drmModeFreeResources(res); + return false; + } + + drmModeEncoder *enc = drmModeGetEncoder(CORE.Window.fd, CORE.Window.connector->encoder_id); + if (!enc) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder"); + drmModeFreeResources(res); + return false; + } + + CORE.Window.crtc = drmModeGetCrtc(CORE.Window.fd, enc->crtc_id); + if (!CORE.Window.crtc) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc"); + drmModeFreeEncoder(enc); + drmModeFreeResources(res); + return false; + } + + // If InitWindow should use the current mode find it in the connector's mode list + if ((CORE.Window.screen.width <= 0) || (CORE.Window.screen.height <= 0)) + { + TRACELOG(LOG_TRACE, "DISPLAY: Selecting DRM connector mode for current used mode..."); + + CORE.Window.modeIndex = FindMatchingConnectorMode(CORE.Window.connector, &CORE.Window.crtc->mode); + + if (CORE.Window.modeIndex < 0) + { + TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found"); + drmModeFreeEncoder(enc); + drmModeFreeResources(res); + return false; + } + + CORE.Window.screen.width = CORE.Window.display.width; + CORE.Window.screen.height = CORE.Window.display.height; + } + + const bool allowInterlaced = CORE.Window.flags & FLAG_INTERLACED_HINT; + const int fps = (CORE.Time.target > 0)? (1.0/CORE.Time.target) : 60; + + // Try to find an exact matching mode + CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced); + + // If nothing found, try to find a nearly matching mode + if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced); + + // If nothing found, try to find an exactly matching mode including interlaced + if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true); + + // If nothing found, try to find a nearly matching mode including interlaced + if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true); + + // If nothing found, there is no suitable mode + if (CORE.Window.modeIndex < 0) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode"); + drmModeFreeEncoder(enc); + drmModeFreeResources(res); + return false; + } + + CORE.Window.display.width = CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay; + CORE.Window.display.height = CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay; + + TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u)", CORE.Window.connector->modes[CORE.Window.modeIndex].name, + CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, + (CORE.Window.connector->modes[CORE.Window.modeIndex].flags & DRM_MODE_FLAG_INTERLACE)? 'i' : 'p', + CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh); + + // Use the width and height of the surface for render + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + + drmModeFreeEncoder(enc); + enc = NULL; + + drmModeFreeResources(res); + res = NULL; + + CORE.Window.gbmDevice = gbm_create_device(CORE.Window.fd); + if (!CORE.Window.gbmDevice) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM device"); + return false; + } + + CORE.Window.gbmSurface = gbm_surface_create(CORE.Window.gbmDevice, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, + CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); + if (!CORE.Window.gbmSurface) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM surface"); + return false; + } + + EGLint samples = 0; + EGLint sampleBuffer = 0; + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + samples = 4; + sampleBuffer = 1; + TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4"); + } + + const EGLint framebufferAttribs[] = + { + EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, // Type of context support + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, // Don't use it on Android! + EGL_RED_SIZE, 8, // RED color bit depth (alternative: 5) + EGL_GREEN_SIZE, 8, // GREEN color bit depth (alternative: 6) + EGL_BLUE_SIZE, 8, // BLUE color bit depth (alternative: 5) + EGL_ALPHA_SIZE, 8, // ALPHA bit depth (required for transparent framebuffer) + //EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI) + EGL_DEPTH_SIZE, 16, // Depth buffer size (Required to use Depth testing!) + //EGL_STENCIL_SIZE, 8, // Stencil buffer size + EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA + EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs) + EGL_NONE + }; + + const EGLint contextAttribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + EGLint numConfigs = 0; + + // Get an EGL device connection + CORE.Window.device = eglGetDisplay((EGLNativeDisplayType)CORE.Window.gbmDevice); + if (CORE.Window.device == EGL_NO_DISPLAY) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); + return false; + } + + // Initialize the EGL device connection + if (eglInitialize(CORE.Window.device, NULL, NULL) == EGL_FALSE) + { + // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred. + TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); + return false; + } + + if (!eglChooseConfig(CORE.Window.device, NULL, NULL, 0, &numConfigs)) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError()); + return false; + } + + TRACELOG(LOG_TRACE, "DISPLAY: EGL configs available: %d", numConfigs); + + EGLConfig *configs = RL_CALLOC(numConfigs, sizeof(*configs)); + if (!configs) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to get memory for EGL configs"); + return false; + } + + EGLint matchingNumConfigs = 0; + if (!eglChooseConfig(CORE.Window.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs)) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError()); + free(configs); + return false; + } + + TRACELOG(LOG_TRACE, "DISPLAY: EGL matching configs available: %d", matchingNumConfigs); + + // find the EGL config that matches the previously setup GBM format + int found = 0; + for (EGLint i = 0; i < matchingNumConfigs; ++i) + { + EGLint id = 0; + if (!eglGetConfigAttrib(CORE.Window.device, configs[i], EGL_NATIVE_VISUAL_ID, &id)) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config attribute: 0x%x", eglGetError()); + continue; + } + + if (GBM_FORMAT_ARGB8888 == id) + { + TRACELOG(LOG_TRACE, "DISPLAY: Using EGL config: %d", i); + CORE.Window.config = configs[i]; + found = 1; + break; + } + } + + RL_FREE(configs); + + if (!found) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable EGL config"); + return false; + } + + // Set rendering API + eglBindAPI(EGL_OPENGL_ES_API); + + // Create an EGL rendering context + CORE.Window.context = eglCreateContext(CORE.Window.device, CORE.Window.config, EGL_NO_CONTEXT, contextAttribs); + if (CORE.Window.context == EGL_NO_CONTEXT) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); + return false; + } + + // Create an EGL window surface + //--------------------------------------------------------------------------------- + CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, (EGLNativeWindowType)CORE.Window.gbmSurface, NULL); + if (EGL_NO_SURFACE == CORE.Window.surface) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL window surface: 0x%04x", eglGetError()); + return false; + } + + // At this point we need to manage render size vs screen size + // NOTE: This function use and modify global module variables: + // -> CORE.Window.screen.width/CORE.Window.screen.height + // -> CORE.Window.render.width/CORE.Window.render.height + // -> CORE.Window.screenScale + SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); + + // There must be at least one frame displayed before the buffers are swapped + //eglSwapInterval(CORE.Window.device, 1); + + if (eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context) == EGL_FALSE) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); + return false; + } + else + { + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + } + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(eglGetProcAddress); + + // Initialize OpenGL context (states and resources) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + return true; +} + +// Initialize Keyboard system (using standard input) +static void InitKeyboard(void) +{ + // NOTE: We read directly from Standard Input (stdin) - STDIN_FILENO file descriptor, + // Reading directly from stdin will give chars already key-mapped by kernel to ASCII or UNICODE + + // Save terminal keyboard settings + tcgetattr(STDIN_FILENO, &CORE.Input.Keyboard.defaultSettings); + + // Reconfigure terminal with new settings + struct termios keyboardNewSettings = { 0 }; + keyboardNewSettings = CORE.Input.Keyboard.defaultSettings; + + // New terminal settings for keyboard: turn off buffering (non-canonical mode), echo and key processing + // NOTE: ISIG controls if ^C and ^Z generate break signals or not + keyboardNewSettings.c_lflag &= ~(ICANON | ECHO | ISIG); + //keyboardNewSettings.c_iflag &= ~(ISTRIP | INLCR | ICRNL | IGNCR | IXON | IXOFF); + keyboardNewSettings.c_cc[VMIN] = 1; + keyboardNewSettings.c_cc[VTIME] = 0; + + // Set new keyboard settings (change occurs immediately) + tcsetattr(STDIN_FILENO, TCSANOW, &keyboardNewSettings); + + // Save old keyboard mode to restore it at the end + CORE.Input.Keyboard.defaultFileFlags = fcntl(STDIN_FILENO, F_GETFL, 0); // F_GETFL: Get the file access mode and the file status flags + fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags | O_NONBLOCK); // F_SETFL: Set the file status flags to the value specified + + // NOTE: If ioctl() returns -1, it means the call failed for some reason (error code set in errno) + int result = ioctl(STDIN_FILENO, KDGKBMODE, &CORE.Input.Keyboard.defaultMode); + + // In case of failure, it could mean a remote keyboard is used (SSH) + if (result < 0) TRACELOG(LOG_WARNING, "RPI: Failed to change keyboard mode, an SSH keyboard is probably used"); + else + { + // Reconfigure keyboard mode to get: + // - scancodes (K_RAW) + // - keycodes (K_MEDIUMRAW) + // - ASCII chars (K_XLATE) + // - UNICODE chars (K_UNICODE) + ioctl(STDIN_FILENO, KDSKBMODE, K_XLATE); // ASCII chars + } + + // Register keyboard restore when program finishes + atexit(RestoreKeyboard); +} + +// Restore default keyboard input +static void RestoreKeyboard(void) +{ + // Reset to default keyboard settings + tcsetattr(STDIN_FILENO, TCSANOW, &CORE.Input.Keyboard.defaultSettings); + + // Reconfigure keyboard to default mode + fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags); + ioctl(STDIN_FILENO, KDSKBMODE, CORE.Input.Keyboard.defaultMode); +} + +#if defined(SUPPORT_SSH_KEYBOARD_RPI) +// Process keyboard inputs +static void ProcessKeyboard(void) +{ + #define MAX_KEYBUFFER_SIZE 32 // Max size in bytes to read + + // Keyboard input polling (fill keys[256] array with status) + int bufferByteCount = 0; // Bytes available on the buffer + char keysBuffer[MAX_KEYBUFFER_SIZE] = { 0 }; // Max keys to be read at a time + + // Read availables keycodes from stdin + bufferByteCount = read(STDIN_FILENO, keysBuffer, MAX_KEYBUFFER_SIZE); // POSIX system call + + // Reset pressed keys array (it will be filled below) + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) + { + CORE.Input.Keyboard.currentKeyState[i] = 0; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } + + // Fill all read bytes (looking for keys) + for (int i = 0; i < bufferByteCount; i++) + { + // NOTE: If (key == 0x1b), depending on next key, it could be a special keymap code! + // Up -> 1b 5b 41 / Left -> 1b 5b 44 / Right -> 1b 5b 43 / Down -> 1b 5b 42 + if (keysBuffer[i] == 0x1b) + { + // Check if ESCAPE key has been pressed to stop program + if (bufferByteCount == 1) CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] = 1; + else + { + if (keysBuffer[i + 1] == 0x5b) // Special function key + { + if ((keysBuffer[i + 2] == 0x5b) || (keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32)) + { + // Process special function keys (F1 - F12) + switch (keysBuffer[i + 3]) + { + case 0x41: CORE.Input.Keyboard.currentKeyState[290] = 1; break; // raylib KEY_F1 + case 0x42: CORE.Input.Keyboard.currentKeyState[291] = 1; break; // raylib KEY_F2 + case 0x43: CORE.Input.Keyboard.currentKeyState[292] = 1; break; // raylib KEY_F3 + case 0x44: CORE.Input.Keyboard.currentKeyState[293] = 1; break; // raylib KEY_F4 + case 0x45: CORE.Input.Keyboard.currentKeyState[294] = 1; break; // raylib KEY_F5 + case 0x37: CORE.Input.Keyboard.currentKeyState[295] = 1; break; // raylib KEY_F6 + case 0x38: CORE.Input.Keyboard.currentKeyState[296] = 1; break; // raylib KEY_F7 + case 0x39: CORE.Input.Keyboard.currentKeyState[297] = 1; break; // raylib KEY_F8 + case 0x30: CORE.Input.Keyboard.currentKeyState[298] = 1; break; // raylib KEY_F9 + case 0x31: CORE.Input.Keyboard.currentKeyState[299] = 1; break; // raylib KEY_F10 + case 0x33: CORE.Input.Keyboard.currentKeyState[300] = 1; break; // raylib KEY_F11 + case 0x34: CORE.Input.Keyboard.currentKeyState[301] = 1; break; // raylib KEY_F12 + default: break; + } + + if (keysBuffer[i + 2] == 0x5b) i += 4; + else if ((keysBuffer[i + 2] == 0x31) || (keysBuffer[i + 2] == 0x32)) i += 5; + } + else + { + switch (keysBuffer[i + 2]) + { + case 0x41: CORE.Input.Keyboard.currentKeyState[265] = 1; break; // raylib KEY_UP + case 0x42: CORE.Input.Keyboard.currentKeyState[264] = 1; break; // raylib KEY_DOWN + case 0x43: CORE.Input.Keyboard.currentKeyState[262] = 1; break; // raylib KEY_RIGHT + case 0x44: CORE.Input.Keyboard.currentKeyState[263] = 1; break; // raylib KEY_LEFT + default: break; + } + + i += 3; // Jump to next key + } + + // NOTE: Some keys are not directly keymapped (CTRL, ALT, SHIFT) + } + } + } + else if (keysBuffer[i] == 0x0a) // raylib KEY_ENTER (don't mix with KEY_*) + { + CORE.Input.Keyboard.currentKeyState[257] = 1; + + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257; // Add keys pressed into queue + CORE.Input.Keyboard.keyPressedQueueCount++; + } + else if (keysBuffer[i] == 0x7f) // raylib KEY_BACKSPACE + { + CORE.Input.Keyboard.currentKeyState[259] = 1; + + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = 257; // Add keys pressed into queue + CORE.Input.Keyboard.keyPressedQueueCount++; + } + else + { + // Translate lowercase a-z letters to A-Z + if ((keysBuffer[i] >= 97) && (keysBuffer[i] <= 122)) + { + CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i] - 32] = 1; + } + else CORE.Input.Keyboard.currentKeyState[(int)keysBuffer[i]] = 1; + + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keysBuffer[i]; // Add keys pressed into queue + CORE.Input.Keyboard.keyPressedQueueCount++; + } + } + + // Check exit key (same functionality as GLFW3 KeyCallback()) + if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true; + +#if defined(SUPPORT_SCREEN_CAPTURE) + // Check screen capture key (raylib key: KEY_F12) + if (CORE.Input.Keyboard.currentKeyState[301] == 1) + { + TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); + screenshotCounter++; + } +#endif +} +#endif // SUPPORT_SSH_KEYBOARD_RPI + +// Initialise user input from evdev(/dev/input/event) +// this means mouse, keyboard or gamepad devices +static void InitEvdevInput(void) +{ + char path[MAX_FILEPATH_LENGTH] = { 0 }; + DIR *directory = NULL; + struct dirent *entity = NULL; + + // Initialise keyboard file descriptor + CORE.Input.Keyboard.fd = -1; + + // Reset variables + for (int i = 0; i < MAX_TOUCH_POINTS; ++i) + { + CORE.Input.Touch.position[i].x = -1; + CORE.Input.Touch.position[i].y = -1; + } + + // Reset keyboard key state + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) + { + CORE.Input.Keyboard.currentKeyState[i] = 0; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } + + // Open the linux directory of "/dev/input" + directory = opendir(DEFAULT_EVDEV_PATH); + + if (directory) + { + while ((entity = readdir(directory)) != NULL) + { + if ((strncmp("event", entity->d_name, strlen("event")) == 0) || // Search for devices named "event*" + (strncmp("mouse", entity->d_name, strlen("mouse")) == 0)) // Search for devices named "mouse*" + { + sprintf(path, "%s%s", DEFAULT_EVDEV_PATH, entity->d_name); + ConfigureEvdevDevice(path); // Configure the device if appropriate + } + } + + closedir(directory); + } + else TRACELOG(LOG_WARNING, "RPI: Failed to open linux event directory: %s", DEFAULT_EVDEV_PATH); +} + +// Identifies a input device and configures it for use if appropriate +static void ConfigureEvdevDevice(char *device) +{ + #define BITS_PER_LONG (8*sizeof(long)) + #define NBITS(x) ((((x) - 1)/BITS_PER_LONG) + 1) + #define OFF(x) ((x)%BITS_PER_LONG) + #define BIT(x) (1UL<> OFF(bit)) & 1) + + struct input_absinfo absinfo = { 0 }; + unsigned long evBits[NBITS(EV_MAX)] = { 0 }; + unsigned long absBits[NBITS(ABS_MAX)] = { 0 }; + unsigned long relBits[NBITS(REL_MAX)] = { 0 }; + unsigned long keyBits[NBITS(KEY_MAX)] = { 0 }; + bool hasAbs = false; + bool hasRel = false; + bool hasAbsMulti = false; + int freeWorkerId = -1; + int fd = -1; + + InputEventWorker *worker = NULL; + + // Open the device and allocate worker + //------------------------------------------------------------------------------------------------------- + // Find a free spot in the workers array + for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + { + if (CORE.Input.eventWorker[i].threadId == 0) + { + freeWorkerId = i; + break; + } + } + + // Select the free worker from array + if (freeWorkerId >= 0) + { + worker = &(CORE.Input.eventWorker[freeWorkerId]); // Grab a pointer to the worker + memset(worker, 0, sizeof(InputEventWorker)); // Clear the worker + } + else + { + TRACELOG(LOG_WARNING, "RPI: Failed to create input device thread for %s, out of worker slots", device); + return; + } + + // Open the device + fd = open(device, O_RDONLY | O_NONBLOCK); + if (fd < 0) + { + TRACELOG(LOG_WARNING, "RPI: Failed to open input device: %s", device); + return; + } + worker->fd = fd; + + // Grab number on the end of the devices name "event" + int devNum = 0; + char *ptrDevName = strrchr(device, 't'); + worker->eventNum = -1; + + if (ptrDevName != NULL) + { + if (sscanf(ptrDevName, "t%d", &devNum) == 1) worker->eventNum = devNum; + } + else worker->eventNum = 0; // TODO: HACK: Grab number for mouse0 device! + + // At this point we have a connection to the device, but we don't yet know what the device is. + // It could be many things, even as simple as a power button... + //------------------------------------------------------------------------------------------------------- + + // Identify the device + //------------------------------------------------------------------------------------------------------- + ioctl(fd, EVIOCGBIT(0, sizeof(evBits)), evBits); // Read a bitfield of the available device properties + + // Check for absolute input devices + if (TEST_BIT(evBits, EV_ABS)) + { + ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits); + + // Check for absolute movement support (usually touchscreens, but also joysticks) + if (TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y)) + { + hasAbs = true; + + // Get the scaling values + ioctl(fd, EVIOCGABS(ABS_X), &absinfo); + worker->absRange.x = absinfo.minimum; + worker->absRange.width = absinfo.maximum - absinfo.minimum; + ioctl(fd, EVIOCGABS(ABS_Y), &absinfo); + worker->absRange.y = absinfo.minimum; + worker->absRange.height = absinfo.maximum - absinfo.minimum; + } + + // Check for multiple absolute movement support (usually multitouch touchscreens) + if (TEST_BIT(absBits, ABS_MT_POSITION_X) && TEST_BIT(absBits, ABS_MT_POSITION_Y)) + { + hasAbsMulti = true; + + // Get the scaling values + ioctl(fd, EVIOCGABS(ABS_X), &absinfo); + worker->absRange.x = absinfo.minimum; + worker->absRange.width = absinfo.maximum - absinfo.minimum; + ioctl(fd, EVIOCGABS(ABS_Y), &absinfo); + worker->absRange.y = absinfo.minimum; + worker->absRange.height = absinfo.maximum - absinfo.minimum; + } + } + + // Check for relative movement support (usually mouse) + if (TEST_BIT(evBits, EV_REL)) + { + ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits); + + if (TEST_BIT(relBits, REL_X) && TEST_BIT(relBits, REL_Y)) hasRel = true; + } + + // Check for button support to determine the device type(usually on all input devices) + if (TEST_BIT(evBits, EV_KEY)) + { + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits); + + if (hasAbs || hasAbsMulti) + { + if (TEST_BIT(keyBits, BTN_TOUCH)) worker->isTouch = true; // This is a touchscreen + if (TEST_BIT(keyBits, BTN_TOOL_FINGER)) worker->isTouch = true; // This is a drawing tablet + if (TEST_BIT(keyBits, BTN_TOOL_PEN)) worker->isTouch = true; // This is a drawing tablet + if (TEST_BIT(keyBits, BTN_STYLUS)) worker->isTouch = true; // This is a drawing tablet + if (worker->isTouch || hasAbsMulti) worker->isMultitouch = true; // This is a multitouch capable device + } + + if (hasRel) + { + if (TEST_BIT(keyBits, BTN_LEFT)) worker->isMouse = true; // This is a mouse + if (TEST_BIT(keyBits, BTN_RIGHT)) worker->isMouse = true; // This is a mouse + } + + if (TEST_BIT(keyBits, BTN_A)) worker->isGamepad = true; // This is a gamepad + if (TEST_BIT(keyBits, BTN_TRIGGER)) worker->isGamepad = true; // This is a gamepad + if (TEST_BIT(keyBits, BTN_START)) worker->isGamepad = true; // This is a gamepad + if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad + if (TEST_BIT(keyBits, BTN_TL)) worker->isGamepad = true; // This is a gamepad + + if (TEST_BIT(keyBits, KEY_SPACE)) worker->isKeyboard = true; // This is a keyboard + } + //------------------------------------------------------------------------------------------------------- + + // Decide what to do with the device + //------------------------------------------------------------------------------------------------------- + if (worker->isKeyboard && (CORE.Input.Keyboard.fd == -1)) + { + // Use the first keyboard encountered. This assumes that a device that says it's a keyboard is just a + // keyboard. The keyboard is polled synchronously, whereas other input devices are polled in separate + // threads so that they don't drop events when the frame rate is slow. + TRACELOG(LOG_INFO, "RPI: Opening keyboard device: %s", device); + CORE.Input.Keyboard.fd = worker->fd; + } + else if (worker->isTouch || worker->isMouse) + { + // Looks like an interesting device + TRACELOG(LOG_INFO, "RPI: Opening input device: %s (%s%s%s%s)", device, + worker->isMouse? "mouse " : "", + worker->isMultitouch? "multitouch " : "", + worker->isTouch? "touchscreen " : "", + worker->isGamepad? "gamepad " : ""); + + // Create a thread for this device + int error = pthread_create(&worker->threadId, NULL, &EventThread, (void *)worker); + if (error != 0) + { + TRACELOG(LOG_WARNING, "RPI: Failed to create input device thread: %s (error: %d)", device, error); + worker->threadId = 0; + close(fd); + } + +#if defined(USE_LAST_TOUCH_DEVICE) + // Find touchscreen with the highest index + int maxTouchNumber = -1; + + for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + { + if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum > maxTouchNumber)) maxTouchNumber = CORE.Input.eventWorker[i].eventNum; + } + + // Find touchscreens with lower indexes + for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + { + if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum < maxTouchNumber)) + { + if (CORE.Input.eventWorker[i].threadId != 0) + { + TRACELOG(LOG_WARNING, "RPI: Found duplicate touchscreen, killing touchscreen on event: %d", i); + pthread_cancel(CORE.Input.eventWorker[i].threadId); + close(CORE.Input.eventWorker[i].fd); + } + } + } +#endif + } + else close(fd); // We are not interested in this device + //------------------------------------------------------------------------------------------------------- +} + +// Poll and process evdev keyboard events +static void PollKeyboardEvents(void) +{ + // Scancode to keycode mapping for US keyboards + // TODO: Replace this with a keymap from the X11 to get the correct regional map for the keyboard: + // Currently non US keyboards will have the wrong mapping for some keys + static const int keymapUS[] = { + 0, 256, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 259, 258, 81, 87, 69, 82, 84, + 89, 85, 73, 79, 80, 91, 93, 257, 341, 65, 83, 68, 70, 71, 72, 74, 75, 76, 59, 39, 96, + 340, 92, 90, 88, 67, 86, 66, 78, 77, 44, 46, 47, 344, 332, 342, 32, 280, 290, 291, + 292, 293, 294, 295, 296, 297, 298, 299, 282, 281, 327, 328, 329, 333, 324, 325, + 326, 334, 321, 322, 323, 320, 330, 0, 85, 86, 300, 301, 89, 90, 91, 92, 93, 94, 95, + 335, 345, 331, 283, 346, 101, 268, 265, 266, 263, 262, 269, 264, 267, 260, 261, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 347, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 0, 0, 0, 0, 0, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, + 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 0, 0, 0, 0, 0, 0, 0 + }; + + int fd = CORE.Input.Keyboard.fd; + if (fd == -1) return; + + struct input_event event = { 0 }; + int keycode = -1; + + // Try to read data from the keyboard and only continue if successful + while (read(fd, &event, sizeof(event)) == (int)sizeof(event)) + { + // Button parsing + if (event.type == EV_KEY) + { +#if defined(SUPPORT_SSH_KEYBOARD_RPI) + // Change keyboard mode to events + CORE.Input.Keyboard.evtMode = true; +#endif + // Keyboard button parsing + if ((event.code >= 1) && (event.code <= 255)) //Keyboard keys appear for codes 1 to 255 + { + keycode = keymapUS[event.code & 0xFF]; // The code we get is a scancode so we look up the appropriate keycode + + // Make sure we got a valid keycode + if ((keycode > 0) && (keycode < sizeof(CORE.Input.Keyboard.currentKeyState))) + { + // WARNING: https://www.kernel.org/doc/Documentation/input/input.txt + // Event interface: 'value' is the value the event carries. Either a relative change for EV_REL, + // absolute new value for EV_ABS (joysticks ...), or 0 for EV_KEY for release, 1 for keypress and 2 for autorepeat + CORE.Input.Keyboard.currentKeyState[keycode] = (event.value >= 1)? 1 : 0; + if (event.value >= 1) + { + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; // Register last key pressed + CORE.Input.Keyboard.keyPressedQueueCount++; + } + + #if defined(SUPPORT_SCREEN_CAPTURE) + // Check screen capture key (raylib key: KEY_F12) + if (CORE.Input.Keyboard.currentKeyState[301] == 1) + { + TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); + screenshotCounter++; + } + #endif + + if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true; + + TRACELOGD("RPI: KEY_%s ScanCode: %4i KeyCode: %4i", (event.value == 0)? "UP" : "DOWN", event.code, keycode); + } + } + } + } +} + +// Input device events reading thread +static void *EventThread(void *arg) +{ + struct input_event event = { 0 }; + InputEventWorker *worker = (InputEventWorker *)arg; + + int touchAction = -1; // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE + bool gestureUpdate = false; // Flag to note gestures require to update + + while (!CORE.Window.shouldClose) + { + // Try to read data from the device and only continue if successful + while (read(worker->fd, &event, sizeof(event)) == (int)sizeof(event)) + { + // Relative movement parsing + if (event.type == EV_REL) + { + if (event.code == REL_X) + { + CORE.Input.Mouse.currentPosition.x += event.value; + CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x; + + touchAction = 2; // TOUCH_ACTION_MOVE + gestureUpdate = true; + } + + if (event.code == REL_Y) + { + CORE.Input.Mouse.currentPosition.y += event.value; + CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y; + + touchAction = 2; // TOUCH_ACTION_MOVE + gestureUpdate = true; + } + + if (event.code == REL_WHEEL) CORE.Input.Mouse.eventWheelMove.y += event.value; + } + + // Absolute movement parsing + if (event.type == EV_ABS) + { + // Basic movement + if (event.code == ABS_X) + { + CORE.Input.Mouse.currentPosition.x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange + CORE.Input.Touch.position[0].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange + + touchAction = 2; // TOUCH_ACTION_MOVE + gestureUpdate = true; + } + + if (event.code == ABS_Y) + { + CORE.Input.Mouse.currentPosition.y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange + CORE.Input.Touch.position[0].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange + + touchAction = 2; // TOUCH_ACTION_MOVE + gestureUpdate = true; + } + + // Multitouch movement + if (event.code == ABS_MT_SLOT) worker->touchSlot = event.value; // Remember the slot number for the folowing events + + if (event.code == ABS_MT_POSITION_X) + { + if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange + } + + if (event.code == ABS_MT_POSITION_Y) + { + if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange + } + + if (event.code == ABS_MT_TRACKING_ID) + { + if ((event.value < 0) && (worker->touchSlot < MAX_TOUCH_POINTS)) + { + // Touch has ended for this point + CORE.Input.Touch.position[worker->touchSlot].x = -1; + CORE.Input.Touch.position[worker->touchSlot].y = -1; + } + } + + // Touchscreen tap + if (event.code == ABS_PRESSURE) + { + int previousMouseLeftButtonState = CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT]; + + if (!event.value && previousMouseLeftButtonState) + { + CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0; + + touchAction = 0; // TOUCH_ACTION_UP + gestureUpdate = true; + } + + if (event.value && !previousMouseLeftButtonState) + { + CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1; + + touchAction = 1; // TOUCH_ACTION_DOWN + gestureUpdate = true; + } + } + + } + + // Button parsing + if (event.type == EV_KEY) + { + // Mouse button parsing + if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT)) + { + CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value; + + if (event.value > 0) touchAction = 1; // TOUCH_ACTION_DOWN + else touchAction = 0; // TOUCH_ACTION_UP + gestureUpdate = true; + } + + if (event.code == BTN_RIGHT) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value; + if (event.code == BTN_MIDDLE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_MIDDLE] = event.value; + if (event.code == BTN_SIDE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_SIDE] = event.value; + if (event.code == BTN_EXTRA) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_EXTRA] = event.value; + if (event.code == BTN_FORWARD) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_FORWARD] = event.value; + if (event.code == BTN_BACK) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_BACK] = event.value; + } + + // Screen confinement + if (!CORE.Input.Mouse.cursorHidden) + { + if (CORE.Input.Mouse.currentPosition.x < 0) CORE.Input.Mouse.currentPosition.x = 0; + if (CORE.Input.Mouse.currentPosition.x > CORE.Window.screen.width/CORE.Input.Mouse.scale.x) CORE.Input.Mouse.currentPosition.x = CORE.Window.screen.width/CORE.Input.Mouse.scale.x; + + if (CORE.Input.Mouse.currentPosition.y < 0) CORE.Input.Mouse.currentPosition.y = 0; + if (CORE.Input.Mouse.currentPosition.y > CORE.Window.screen.height/CORE.Input.Mouse.scale.y) CORE.Input.Mouse.currentPosition.y = CORE.Window.screen.height/CORE.Input.Mouse.scale.y; + } + + // Update touch point count + CORE.Input.Touch.pointCount = 0; + for (int i = 0; i < MAX_TOUCH_POINTS; i++) + { + if (CORE.Input.Touch.position[i].x >= 0) CORE.Input.Touch.pointCount++; + } + +#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_DRM + if (gestureUpdate) + { + GestureEvent gestureEvent = { 0 }; + + gestureEvent.touchAction = touchAction; + gestureEvent.pointCount = CORE.Input.Touch.pointCount; + + for (int i = 0; i < MAX_TOUCH_POINTS; i++) + { + gestureEvent.pointId[i] = i; + gestureEvent.position[i] = CORE.Input.Touch.position[i]; + } + + ProcessGestureEvent(gestureEvent); + } +#endif + } + + WaitTime(0.005); // Sleep for 5ms to avoid hogging CPU time + } + + close(worker->fd); + + return NULL; +} + +// Initialize gamepad system +static void InitGamepad(void) +{ + char gamepadDev[128] = { 0 }; + + for (int i = 0; i < MAX_GAMEPADS; i++) + { + sprintf(gamepadDev, "%s%i", DEFAULT_GAMEPAD_DEV, i); + + if ((CORE.Input.Gamepad.streamId[i] = open(gamepadDev, O_RDONLY | O_NONBLOCK)) < 0) + { + // NOTE: Only show message for first gamepad + if (i == 0) TRACELOG(LOG_WARNING, "RPI: Failed to open Gamepad device, no gamepad available"); + } + else + { + CORE.Input.Gamepad.ready[i] = true; + + // NOTE: Only create one thread + if (i == 0) + { + int error = pthread_create(&CORE.Input.Gamepad.threadId, NULL, &GamepadThread, NULL); + + if (error != 0) TRACELOG(LOG_WARNING, "RPI: Failed to create gamepad input event thread"); + else TRACELOG(LOG_INFO, "RPI: Gamepad device initialized successfully"); + } + } + } +} + +// Process Gamepad (/dev/input/js0) +static void *GamepadThread(void *arg) +{ + #define JS_EVENT_BUTTON 0x01 // Button pressed/released + #define JS_EVENT_AXIS 0x02 // Joystick axis moved + #define JS_EVENT_INIT 0x80 // Initial state of device + + struct js_event { + unsigned int time; // event timestamp in milliseconds + short value; // event value + unsigned char type; // event type + unsigned char number; // event axis/button number + }; + + // Read gamepad event + struct js_event gamepadEvent = { 0 }; + + while (!CORE.Window.shouldClose) + { + for (int i = 0; i < MAX_GAMEPADS; i++) + { + if (read(CORE.Input.Gamepad.streamId[i], &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event)) + { + gamepadEvent.type &= ~JS_EVENT_INIT; // Ignore synthetic events + + // Process gamepad events by type + if (gamepadEvent.type == JS_EVENT_BUTTON) + { + //TRACELOG(LOG_WARNING, "RPI: Gamepad button: %i, value: %i", gamepadEvent.number, gamepadEvent.value); + + if (gamepadEvent.number < MAX_GAMEPAD_BUTTONS) + { + // 1 - button pressed, 0 - button released + CORE.Input.Gamepad.currentButtonState[i][gamepadEvent.number] = (int)gamepadEvent.value; + + if ((int)gamepadEvent.value == 1) CORE.Input.Gamepad.lastButtonPressed = gamepadEvent.number; + else CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + } + } + else if (gamepadEvent.type == JS_EVENT_AXIS) + { + //TRACELOG(LOG_WARNING, "RPI: Gamepad axis: %i, value: %i", gamepadEvent.number, gamepadEvent.value); + + if (gamepadEvent.number < MAX_GAMEPAD_AXIS) + { + // NOTE: Scaling of gamepadEvent.value to get values between -1..1 + CORE.Input.Gamepad.axisState[i][gamepadEvent.number] = (float)gamepadEvent.value/32768; + } + } + } + else WaitTime(0.001); // Sleep for 1 ms to avoid hogging CPU time + } + } + + return NULL; +} + +// Search matching DRM mode in connector's mode list +static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode) +{ + if (NULL == connector) return -1; + if (NULL == mode) return -1; + + // safe bitwise comparison of two modes + #define BINCMP(a, b) memcmp((a), (b), (sizeof(a) < sizeof(b))? sizeof(a) : sizeof(b)) + + for (size_t i = 0; i < connector->count_modes; i++) + { + TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, connector->modes[i].hdisplay, connector->modes[i].vdisplay, + connector->modes[i].vrefresh, (connector->modes[i].flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive"); + + if (0 == BINCMP(&CORE.Window.crtc->mode, &CORE.Window.connector->modes[i])) return i; + } + + return -1; + + #undef BINCMP +} + +// Search exactly matching DRM connector mode in connector's list +static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced) +{ + TRACELOG(LOG_TRACE, "DISPLAY: Searching exact connector mode for %ux%u@%u, selecting an interlaced mode is allowed: %s", width, height, fps, allowInterlaced? "yes" : "no"); + + if (NULL == connector) return -1; + + for (int i = 0; i < CORE.Window.connector->count_modes; i++) + { + const drmModeModeInfo *const mode = &CORE.Window.connector->modes[i]; + + TRACELOG(LOG_TRACE, "DISPLAY: DRM Mode %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive"); + + if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced)) continue; + + if ((mode->hdisplay == width) && (mode->vdisplay == height) && (mode->vrefresh == fps)) return i; + } + + TRACELOG(LOG_TRACE, "DISPLAY: No DRM exact matching mode found"); + return -1; +} + +// Search the nearest matching DRM connector mode in connector's list +static int FindNearestConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced) +{ + TRACELOG(LOG_TRACE, "DISPLAY: Searching nearest connector mode for %ux%u@%u, selecting an interlaced mode is allowed: %s", width, height, fps, allowInterlaced? "yes" : "no"); + + if (NULL == connector) return -1; + + int nearestIndex = -1; + for (int i = 0; i < CORE.Window.connector->count_modes; i++) + { + const drmModeModeInfo *const mode = &CORE.Window.connector->modes[i]; + + TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh, + (mode->flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive"); + + if ((mode->hdisplay < width) || (mode->vdisplay < height)) + { + TRACELOG(LOG_TRACE, "DISPLAY: DRM mode is too small"); + continue; + } + + if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && (!allowInterlaced)) + { + TRACELOG(LOG_TRACE, "DISPLAY: DRM shouldn't choose an interlaced mode"); + continue; + } + + if (nearestIndex < 0) + { + nearestIndex = i; + continue; + } + + const int widthDiff = abs(mode->hdisplay - width); + const int heightDiff = abs(mode->vdisplay - height); + const int fpsDiff = abs(mode->vrefresh - fps); + + const int nearestWidthDiff = abs(CORE.Window.connector->modes[nearestIndex].hdisplay - width); + const int nearestHeightDiff = abs(CORE.Window.connector->modes[nearestIndex].vdisplay - height); + const int nearestFpsDiff = abs(CORE.Window.connector->modes[nearestIndex].vrefresh - fps); + + if ((widthDiff < nearestWidthDiff) || (heightDiff < nearestHeightDiff) || (fpsDiff < nearestFpsDiff)) { + nearestIndex = i; + } + } + + return nearestIndex; +} + +// EOF diff --git a/src/rcore_web.c b/src/rcore_web.c new file mode 100644 index 000000000..baafd60d6 --- /dev/null +++ b/src/rcore_web.c @@ -0,0 +1,1604 @@ +/********************************************************************************************** +* +* rcore_web - Functions to manage window, graphics device and inputs +* +* PLATFORM: WEB +* - HTML5 (WebAssembly) +* +* LIMITATIONS: +* - Limitation 01 +* - Limitation 02 +* +* POSSIBLE IMPROVEMENTS: +* - Replace glfw3 dependency by direct browser API calls (same as library_glfw3.js) +* +* ADDITIONAL NOTES: +* - TRACELOG() function is located in raylib [utils] module +* +* CONFIGURATION: +* #define RCORE_WEB_CUSTOM_FLAG +* Custom flag for rcore on PLATFORM_WEB -not used- +* +* DEPENDENCIES: +* emscripten - Allow interaction between browser API and C +* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) and contributors +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#include "rcore.h" + +#define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) +// #define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?) +#include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management +#include // Required for: timespec, nanosleep(), select() - POSIX + +#include // Emscripten functionality for C +#include // Emscripten HTML5 library + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; // Global CORE state context + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static bool InitGraphicsDevice(int width, int height); // Initialize graphics device + +// Error callback event +static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error + +// Window callbacks events +static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized +static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored +static void WindowMaximizeCallback(GLFWwindow *window, int maximized); // GLFW3 Window Maximize Callback, runs when window is maximized +static void WindowFocusCallback(GLFWwindow *window, int focused); // GLFW3 WindowFocus Callback, runs when window get/lose focus +static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window + +// Input callbacks events +static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed +static void CharCallback(GLFWwindow *window, unsigned int key); // GLFW3 Char Key Callback, runs on key pressed (get char value) +static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods); // GLFW3 Mouse Button Callback, runs on mouse button pressed +static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); // GLFW3 Cursor Position Callback, runs on mouse move +static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel +static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area + +// Emscripten callback events +static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData); +static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData); +static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData); + +static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); +static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData); +static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData); + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +// NOTE: Functions declaration is provided by raylib.h + +//---------------------------------------------------------------------------------- +// Module Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize window and OpenGL context +// NOTE: data parameter could be used to pass any kind of required data to the initialization +void InitWindow(int width, int height, const char *title) +{ + TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); + + TRACELOG(LOG_INFO, "Supported raylib modules:"); + TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); + TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); +#if defined(SUPPORT_MODULE_RSHAPES) + TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXTURES) + TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXT) + TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RMODELS) + TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RAUDIO) + TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); +#endif + + // NOTE: Keep internal pointer to input title string (no copy) + if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; + + // Initialize global input state + memset(&CORE.Input, 0, sizeof(CORE.Input)); + CORE.Input.Keyboard.exitKey = KEY_ESCAPE; + CORE.Input.Mouse.scale = (Vector2){1.0f, 1.0f}; + CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Window.eventWaiting = false; + + // Initialize graphics device (display device and OpenGL context) + // NOTE: returns true if window and graphic device has been initialized successfully + CORE.Window.ready = InitGraphicsDevice(width, height); + + // If graphic device is no properly initialized, we end program + if (!CORE.Window.ready) + { + TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); + return; + } + else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); + + // Initialize hi-res timer + InitTimer(); + + // Initialize random seed + srand((unsigned int)time(NULL)); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + // Load default font + // WARNING: External function: Module required: rtext + LoadFontDefault(); +#if defined(SUPPORT_MODULE_RSHAPES) + // Set font white rectangle for shapes drawing, so shapes and text can be batched together + // WARNING: rshapes module is required, if not available, default internal white rectangle is used + Rectangle rec = GetFontDefault().recs[95]; + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering + SetShapesTexture(GetFontDefault().texture, (Rectangle){rec.x + 2, rec.y + 2, 1, 1}); + } + else + { + // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding + SetShapesTexture(GetFontDefault().texture, (Rectangle){rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2}); + } +#endif +#else +#if defined(SUPPORT_MODULE_RSHAPES) + // Set default texture and rectangle to be used for shapes drawing + // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 + Texture2D texture = {rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8}; + SetShapesTexture(texture, (Rectangle){0.0f, 0.0f, 1.0f, 1.0f}); // WARNING: Module required: rshapes +#endif +#endif +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // Set default font texture filter for HighDPI (blurry) + // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); + } +#endif + + // Setup callback functions for the DOM events + emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); + + // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review + // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas) + // emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); + // Check Resize event (note this is done on the window since most browsers don't support this on #canvas) + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); + + // Trigger this once to get initial window sizing + EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); + + // Support keyboard events -> Not used, GLFW.JS takes care of that + // emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); + // emscripten_set_keydown_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); + + // Support mouse events + emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback); + + // Support touch events + emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + + // Support gamepad events (not provided by GLFW3 on emscripten) + emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); + emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); + +#if defined(SUPPORT_EVENTS_AUTOMATION) + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + CORE.Time.frameCounter = 0; +#endif +} + +// Close window and unload OpenGL context +void CloseWindow(void) +{ +#if defined(SUPPORT_GIF_RECORDING) + if (gifRecording) + { + MsfGifResult result = msf_gif_end(&gifState); + msf_gif_free(result); + gifRecording = false; + } +#endif + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + UnloadFontDefault(); // WARNING: Module required: rtext +#endif + + rlglClose(); // De-init rlgl + + glfwDestroyWindow(CORE.Window.handle); + glfwTerminate(); + +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + timeEndPeriod(1); // Restore time period +#endif + +#if defined(SUPPORT_EVENTS_AUTOMATION) + RL_FREE(events); +#endif + + CORE.Window.ready = false; + TRACELOG(LOG_INFO, "Window closed successfully"); +} + +// Check if application should close +bool WindowShouldClose(void) +{ + // Emterpreter-Async required to run sync code + // https://github.com/emscripten-core/emscripten/wiki/Emterpreter#emterpreter-async-run-synchronous-code + // By default, this function is never called on a web-ready raylib example because we encapsulate + // frame code in a UpdateDrawFrame() function, to allow browser manage execution asynchronously + // but now emscripten allows sync code to be executed in an interpreted way, using emterpreter! + emscripten_sleep(16); + return false; +} + +// Check if window is currently hidden +bool IsWindowHidden(void) +{ + return false; +} + +// Check if window has been minimized +bool IsWindowMinimized(void) +{ + return false; +} + +// Check if window has been maximized +bool IsWindowMaximized(void) +{ + return false; +} + +// Check if window has the focus +bool IsWindowFocused(void) +{ + return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0); +} + +// Check if window has been resizedLastFrame +bool IsWindowResized(void) +{ + return CORE.Window.resizedLastFrame; +} + +// Toggle fullscreen mode +void ToggleFullscreen(void) +{ + /* + EM_ASM + ( + // This strategy works well while using raylib minimal web shell for emscripten, + // it re-scales the canvas to fullscreen using monitor resolution, for tools this + // is a good strategy but maybe games prefer to keep current canvas resolution and + // display it in fullscreen, adjusting monitor resolution if possible + if (document.fullscreenElement) document.exitFullscreen(); + else Module.requestFullscreen(true, true); //false, true); + ); + */ + // EM_ASM(Module.requestFullscreen(false, false);); + /* + if (!CORE.Window.fullscreen) + { + // Option 1: Request fullscreen for the canvas element + // This option does not seem to work at all: + // emscripten_request_pointerlock() and emscripten_request_fullscreen() are affected by web security, + // the user must click once on the canvas to hide the pointer or transition to full screen + //emscripten_request_fullscreen("#canvas", false); + + // Option 2: Request fullscreen for the canvas element with strategy + // This option does not seem to work at all + // Ref: https://github.com/emscripten-core/emscripten/issues/5124 + // EmscriptenFullscreenStrategy strategy = { + // .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_STRETCH, //EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT, + // .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF, + // .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT, + // .canvasResizedCallback = EmscriptenWindowResizedCallback, + // .canvasResizedCallbackUserData = NULL + // }; + //emscripten_request_fullscreen_strategy("#canvas", EM_FALSE, &strategy); + + // Option 3: Request fullscreen for the canvas element with strategy + // It works as expected but only inside the browser (client area) + EmscriptenFullscreenStrategy strategy = { + .scaleMode = EMSCRIPTEN_FULLSCREEN_SCALE_ASPECT, + .canvasResolutionScaleMode = EMSCRIPTEN_FULLSCREEN_CANVAS_SCALE_STDDEF, + .filteringMode = EMSCRIPTEN_FULLSCREEN_FILTERING_DEFAULT, + .canvasResizedCallback = EmscriptenWindowResizedCallback, + .canvasResizedCallbackUserData = NULL + }; + emscripten_enter_soft_fullscreen("#canvas", &strategy); + + int width, height; + emscripten_get_canvas_element_size("#canvas", &width, &height); + TRACELOG(LOG_WARNING, "Emscripten: Enter fullscreen: Canvas size: %i x %i", width, height); + + CORE.Window.fullscreen = true; // Toggle fullscreen flag + CORE.Window.flags |= FLAG_FULLSCREEN_MODE; + } + else + { + //emscripten_exit_fullscreen(); + //emscripten_exit_soft_fullscreen(); + + int width, height; + emscripten_get_canvas_element_size("#canvas", &width, &height); + TRACELOG(LOG_WARNING, "Emscripten: Exit fullscreen: Canvas size: %i x %i", width, height); + + CORE.Window.fullscreen = false; // Toggle fullscreen flag + CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; + } + */ + + CORE.Window.fullscreen = !CORE.Window.fullscreen; // Toggle fullscreen flag +} + +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_WEB"); +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_WEB"); +} + +// Set window state: not minimized/maximized +void RestoreWindow(void) +{ + TRACELOG(LOG_WARNING, "RestoreWindow() not available on PLATFORM_WEB"); +} + +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_WEB"); +} + +// Set window configuration state using flags +void SetWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_WEB"); +} + +// Clear window configuration state flags +void ClearWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "ClearWindowState() not available on PLATFORM_WEB"); +} + +// Set icon for window +void SetWindowIcon(Image image) +{ + TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_WEB"); +} + +// Set icon for window, multiple images +void SetWindowIcons(Image *images, int count) +{ + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on PLATFORM_WEB"); +} + +// Set title for window +void SetWindowTitle(const char *title) +{ + CORE.Window.title = title; + emscripten_set_window_title(title); +} + +// Set window position on screen (windowed mode) +void SetWindowPosition(int x, int y) +{ + TRACELOG(LOG_WARNING, "SetWindowPosition() not available on PLATFORM_WEB"); +} + +// Set monitor for the current window +void SetWindowMonitor(int monitor) +{ + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on PLATFORM_WEB"); +} + +// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMinSize(int width, int height) +{ + CORE.Window.screenMin.width = width; + CORE.Window.screenMin.height = height; + + // Trigger the resize event once to update the window minimum width and height + if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); +} + +// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMaxSize(int width, int height) +{ + CORE.Window.screenMax.width = width; + CORE.Window.screenMax.height = height; + + // Trigger the resize event once to update the window maximum width and height + if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); +} + +// Set window dimensions +void SetWindowSize(int width, int height) +{ + glfwSetWindowSize(CORE.Window.handle, width, height); +} + +// Set window opacity, value opacity is between 0.0 and 1.0 +void SetWindowOpacity(float opacity) +{ + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on PLATFORM_WEB"); +} + +// Set window focused +void SetWindowFocused(void) +{ + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_WEB"); +} + +// Get native window handle +void *GetWindowHandle(void) +{ + TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_WEB"); + return NULL; +} + +// Get number of monitors +int GetMonitorCount(void) +{ + TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on PLATFORM_WEB"); + return 1; +} + +// Get number of monitors +int GetCurrentMonitor(void) +{ + TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on PLATFORM_WEB"); + return 0; +} + +// Get selected monitor position +Vector2 GetMonitorPosition(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on PLATFORM_WEB"); + return (Vector2){ 0, 0 }; +} + +// Get selected monitor width (currently used by monitor) +int GetMonitorWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on PLATFORM_WEB"); + return 0; +} + +// Get selected monitor height (currently used by monitor) +int GetMonitorHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on PLATFORM_WEB"); + return 0; +} + +// Get selected monitor physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on PLATFORM_WEB"); + return 0; +} + +// Get selected monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on PLATFORM_WEB"); + return 0; +} + +// Get selected monitor refresh rate +int GetMonitorRefreshRate(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on PLATFORM_WEB"); + return 0; +} + +// Get the human-readable, UTF-8 encoded name of the selected monitor +const char *GetMonitorName(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on PLATFORM_WEB"); + return ""; +} + +// Get window position XY on monitor +Vector2 GetWindowPosition(void) +{ + TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on PLATFORM_WEB"); + return (Vector2){ 0, 0 }; +} + +// Get window scale DPI factor for current monitor +Vector2 GetWindowScaleDPI(void) +{ + TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on PLATFORM_WEB"); + return (Vector2){ 1.0f, 1.0f }; +} + +// Set clipboard text content +void SetClipboardText(const char *text) +{ + // Security check to (partially) avoid malicious code + if (strchr(text, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided Clipboard could be potentially malicious, avoid [\'] character"); + else EM_ASM({ navigator.clipboard.writeText(UTF8ToString($0)); }, text); +} + +// Get clipboard text content +// NOTE: returned string is allocated and freed by GLFW +const char *GetClipboardText(void) +{ +/* + // Accessing clipboard data from browser is tricky due to security reasons + // The method to use is navigator.clipboard.readText() but this is an asynchronous method + // that will return at some moment after the function is called with the required data + emscripten_run_script_string("navigator.clipboard.readText() \ + .then(text => { document.getElementById('clipboard').innerText = text; console.log('Pasted content: ', text); }) \ + .catch(err => { console.error('Failed to read clipboard contents: ', err); });" + ); + + // The main issue is getting that data, one approach could be using ASYNCIFY and wait + // for the data but it requires adding Asyncify emscripten library on compilation + + // Another approach could be just copy the data in a HTML text field and try to retrieve it + // later on if available... and clean it for future accesses +*/ + return NULL; +} + +// Show mouse cursor +void ShowCursor(void) +{ + CORE.Input.Mouse.cursorHidden = false; +} + +// Hides mouse cursor +void HideCursor(void) +{ + CORE.Input.Mouse.cursorHidden = true; +} + +// Enables cursor (unlock cursor) +void EnableCursor(void) +{ + emscripten_exit_pointerlock(); + + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + + CORE.Input.Mouse.cursorHidden = false; +} + +// Disables cursor (lock cursor) +void DisableCursor(void) +{ + // TODO: figure out how not to hard code the canvas ID here. + emscripten_request_pointerlock("#canvas", 1); + + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + + CORE.Input.Mouse.cursorHidden = true; +} + +// Get elapsed time measure in seconds since InitTimer() +double GetTime(void) +{ + double time = glfwGetTime(); // Elapsed time since glfwInit() + return time; +} + +// Takes a screenshot of current screen (saved a .png) +void TakeScreenshot(const char *fileName) +{ +#if defined(SUPPORT_MODULE_RTEXTURES) + // Security check to (partially) avoid malicious code on PLATFORM_WEB + if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } + + Vector2 scale = GetWindowScaleDPI(); + unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); + Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + + char path[2048] = { 0 }; + strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); + + ExportImage(image, path); // WARNING: Module required: rtextures + RL_FREE(imgData); + + // Download file from MEMFS (emscripten memory filesystem) + // saveFileFromMEMFSToDisk() function is defined in raylib/src/shell.html + emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", GetFileName(path), GetFileName(path))); + + TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); +#else + TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); +#endif +} + +// Open URL with default system browser (if available) +// NOTE: This function is only safe to use if you control the URL given. +// A user could craft a malicious string performing another action. +// Only call this function yourself not with user input or make sure to check the string yourself. +// Ref: https://github.com/raysan5/raylib/issues/686 +void OpenURL(const char *url) +{ + // Security check to (partially) avoid malicious code on PLATFORM_WEB + if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); + else emscripten_run_script(TextFormat("window.open('%s', '_blank')", url)); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Inputs +//---------------------------------------------------------------------------------- + +// Set a custom key to exit program +// NOTE: default exitKey is ESCAPE +void SetExitKey(int key) +{ + CORE.Input.Keyboard.exitKey = key; +} + +// Get gamepad internal name id +const char *GetGamepadName(int gamepad) +{ + const char *name = NULL; + + name = CORE.Input.Gamepad.name[gamepad]; + + return name; +} + +// Get gamepad axis count +int GetGamepadAxisCount(int gamepad) +{ + return CORE.Input.Gamepad.axisCount; +} + +// Set internal gamepad mappings +int SetGamepadMappings(const char *mappings) +{ + TRACELOG(LOG_INFO, "SetGamepadMappings not implemented in rcore_web.c"); + + return 0; +} + +// Get mouse position X +int GetMouseX(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); +} + +// Get mouse position Y +int GetMouseY(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); +} + +// Get mouse position XY +Vector2 GetMousePosition(void) +{ + Vector2 position = { 0 }; + + // TODO: Review touch position + + // NOTE: On PLATFORM_WEB, even on canvas scaling, mouse position is proportionally returned + position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; + position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; + + return position; +} + +// Set mouse position XY +void SetMousePosition(int x, int y) +{ + CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + + // NOTE: emscripten not implemented + glfwSetCursorPos(CORE.Window.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); +} + +// Get mouse wheel movement Y +float GetMouseWheelMove(void) +{ + float result = 0.0f; + + if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x; + else result = (float)CORE.Input.Mouse.currentWheelMove.y; + + return result; +} + +// Set mouse cursor +void SetMouseCursor(int cursor) +{ + TRACELOG(LOG_INFO, "SetMouseCursor not implemented in rcore_web.c"); +} + +// Get touch position X for touch point 0 (relative to screen size) +int GetTouchX(void) +{ + return (int)CORE.Input.Touch.position[0].x; +} + +// Get touch position Y for touch point 0 (relative to screen size) +int GetTouchY(void) +{ + return (int)CORE.Input.Touch.position[0].y; +} + +// Get touch position XY for a touch point index (relative to screen size) +// TODO: Touch position should be scaled depending on display size and render size +Vector2 GetTouchPosition(int index) +{ + Vector2 position = { -1.0f, -1.0f }; + + if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index]; + else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS); + + return position; +} + +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + glfwSwapBuffers(CORE.Window.handle); +} + +// Register all input events +void PollInputEvents(void) +{ +#if defined(SUPPORT_GESTURES_SYSTEM) + // NOTE: Gestures update must be called every frame to reset gestures correctly + // because ProcessGestureEvent() is just called on an event, not every frame + UpdateGestures(); +#endif + + // Reset keys/chars pressed registered + CORE.Input.Keyboard.keyPressedQueueCount = 0; + CORE.Input.Keyboard.charPressedQueueCount = 0; + // Reset key repeats + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + + // Reset last gamepad button/axis registered state + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Input.Gamepad.axisCount = 0; + // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback) + + // Register previous keys states + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) + { + CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } + + // Register previous mouse states + for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; + + // Register previous mouse wheel state + CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; + CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f }; + + // Register previous mouse position + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + + // Register previous touch states + for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; + + // Reset touch positions + // TODO: It resets on PLATFORM_WEB the mouse position and not filled again until a move-event, + // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! + //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; + + CORE.Window.resizedLastFrame = false; + + // Gamepad support using emscripten API + // NOTE: GLFW3 joystick functionality not available in web + + // Get number of gamepads connected + int numGamepads = 0; + if (emscripten_sample_gamepad_data() == EMSCRIPTEN_RESULT_SUCCESS) numGamepads = emscripten_get_num_gamepads(); + + for (int i = 0; (i < numGamepads) && (i < MAX_GAMEPADS); i++) + { + // Register previous gamepad button states + for (int k = 0; k < MAX_GAMEPAD_BUTTONS; k++) CORE.Input.Gamepad.previousButtonState[i][k] = CORE.Input.Gamepad.currentButtonState[i][k]; + + EmscriptenGamepadEvent gamepadState; + + int result = emscripten_get_gamepad_status(i, &gamepadState); + + if (result == EMSCRIPTEN_RESULT_SUCCESS) + { + // Register buttons data for every connected gamepad + for (int j = 0; (j < gamepadState.numButtons) && (j < MAX_GAMEPAD_BUTTONS); j++) + { + GamepadButton button = -1; + + // Gamepad Buttons reference: https://www.w3.org/TR/gamepad/#gamepad-interface + switch (j) + { + case 0: button = GAMEPAD_BUTTON_RIGHT_FACE_DOWN; break; + case 1: button = GAMEPAD_BUTTON_RIGHT_FACE_RIGHT; break; + case 2: button = GAMEPAD_BUTTON_RIGHT_FACE_LEFT; break; + case 3: button = GAMEPAD_BUTTON_RIGHT_FACE_UP; break; + case 4: button = GAMEPAD_BUTTON_LEFT_TRIGGER_1; break; + case 5: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_1; break; + case 6: button = GAMEPAD_BUTTON_LEFT_TRIGGER_2; break; + case 7: button = GAMEPAD_BUTTON_RIGHT_TRIGGER_2; break; + case 8: button = GAMEPAD_BUTTON_MIDDLE_LEFT; break; + case 9: button = GAMEPAD_BUTTON_MIDDLE_RIGHT; break; + case 10: button = GAMEPAD_BUTTON_LEFT_THUMB; break; + case 11: button = GAMEPAD_BUTTON_RIGHT_THUMB; break; + case 12: button = GAMEPAD_BUTTON_LEFT_FACE_UP; break; + case 13: button = GAMEPAD_BUTTON_LEFT_FACE_DOWN; break; + case 14: button = GAMEPAD_BUTTON_LEFT_FACE_LEFT; break; + case 15: button = GAMEPAD_BUTTON_LEFT_FACE_RIGHT; break; + default: break; + } + + if (button != -1) // Check for valid button + { + if (gamepadState.digitalButton[j] == 1) + { + CORE.Input.Gamepad.currentButtonState[i][button] = 1; + CORE.Input.Gamepad.lastButtonPressed = button; + } + else CORE.Input.Gamepad.currentButtonState[i][button] = 0; + } + + //TRACELOGD("INPUT: Gamepad %d, button %d: Digital: %d, Analog: %g", gamepadState.index, j, gamepadState.digitalButton[j], gamepadState.analogButton[j]); + } + + // Register axis data for every connected gamepad + for (int j = 0; (j < gamepadState.numAxes) && (j < MAX_GAMEPAD_AXIS); j++) + { + CORE.Input.Gamepad.axisState[i][j] = gamepadState.axis[j]; + } + + CORE.Input.Gamepad.axisCount = gamepadState.numAxes; + } + } +} + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize display device and framebuffer +// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size +// If width or height are 0, default display size will be used for framebuffer size +// NOTE: returns false in case graphic device could not be created +static bool InitGraphicsDevice(int width, int height) +{ + CORE.Window.screen.width = width; // User desired width + CORE.Window.screen.height = height; // User desired height + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + + // Set the screen minimum and maximum default values to 0 + CORE.Window.screenMin.width = 0; + CORE.Window.screenMin.height = 0; + CORE.Window.screenMax.width = 0; + CORE.Window.screenMax.height = 0; + + // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... + // ...in top-down or left-right to match display aspect ratio (no weird scaling) + + glfwSetErrorCallback(ErrorCallback); +/* + // TODO: Setup GLFW custom allocators to match raylib ones + const GLFWallocator allocator = { + .allocate = MemAlloc, + .deallocate = MemFree, + .reallocate = MemRealloc, + .user = NULL + }; + + glfwInitAllocator(&allocator); +*/ + + if (!glfwInit()) + { + TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); + return false; + } + + glfwDefaultWindowHints(); // Set default windows hints + // glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits + // glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits + // glfwWindowHint(GLFW_BLUE_BITS, 8); // Framebuffer blue color component bits + // glfwWindowHint(GLFW_ALPHA_BITS, 8); // Framebuffer alpha color component bits + // glfwWindowHint(GLFW_DEPTH_BITS, 24); // Depthbuffer bits + // glfwWindowHint(GLFW_REFRESH_RATE, 0); // Refresh rate for fullscreen window + // glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); // OpenGL API to use. Alternative: GLFW_OPENGL_ES_API + // glfwWindowHint(GLFW_AUX_BUFFERS, 0); // Number of auxiliar buffers + + // Check window creation flags + if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) CORE.Window.fullscreen = true; + + if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // Visible window + else glfwWindowHint(GLFW_VISIBLE, GLFW_TRUE); // Window initially hidden + + if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) glfwWindowHint(GLFW_DECORATED, GLFW_FALSE); // Border and buttons on Window + else glfwWindowHint(GLFW_DECORATED, GLFW_TRUE); // Decorated window + + if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); // Resizable window + else glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); // Avoid window being resizable + + // Disable FLAG_WINDOW_MINIMIZED, not supported on initialization + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; + + // Disable FLAG_WINDOW_MAXIMIZED, not supported on initialization + if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; + + if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) glfwWindowHint(GLFW_FOCUSED, GLFW_FALSE); + else glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE); + + if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) glfwWindowHint(GLFW_FLOATING, GLFW_TRUE); + else glfwWindowHint(GLFW_FLOATING, GLFW_FALSE); + + // NOTE: Some GLFW flags are not supported on HTML5 + // e.g.: GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_SCALE_TO_MONITOR, GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_MOUSE_PASSTHROUGH + + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + // NOTE: MSAA is only enabled for main framebuffer, not user-created FBOs + TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4"); + glfwWindowHint(GLFW_SAMPLES, 4); // Tries to enable multisampling x4 (MSAA), default is 0 + } + + // NOTE: When asking for an OpenGL context version, most drivers provide the highest supported version + // with backward compatibility to older OpenGL versions. + // For example, if using OpenGL 1.1, driver can provide a 4.3 backwards compatible context. + + // Check selection OpenGL version + if (rlGetVersion() == RL_OPENGL_21) + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); // Choose OpenGL major version (just hint) + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1); // Choose OpenGL minor version (just hint) + } + else if (rlGetVersion() == RL_OPENGL_33) + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Choose OpenGL major version (just hint) + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above! + // Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Forward Compatibility Hint: Only 3.3 and above! + // glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context + } + else if (rlGetVersion() == RL_OPENGL_43) + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); // Choose OpenGL major version (just hint) + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); // Choose OpenGL minor version (just hint) + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); +#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Enable OpenGL Debug Context +#endif + } + else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); + } + else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); + } + + // NOTE: Getting video modes is not implemented in emscripten GLFW3 version + CORE.Window.display.width = CORE.Window.screen.width; + CORE.Window.display.height = CORE.Window.screen.height; + + if (CORE.Window.fullscreen) + { + // remember center for switchinging from fullscreen to window + if ((CORE.Window.screen.height == CORE.Window.display.height) && (CORE.Window.screen.width == CORE.Window.display.width)) + { + // If screen width/height equal to the display, we can't calculate the window pos for toggling full-screened/windowed. + // Toggling full-screened/windowed with pos(0, 0) can cause problems in some platforms, such as X11. + CORE.Window.position.x = CORE.Window.display.width/4; + CORE.Window.position.y = CORE.Window.display.height/4; + } + else + { + CORE.Window.position.x = CORE.Window.display.width/2 - CORE.Window.screen.width/2; + CORE.Window.position.y = CORE.Window.display.height/2 - CORE.Window.screen.height/2; + } + + if (CORE.Window.position.x < 0) CORE.Window.position.x = 0; + if (CORE.Window.position.y < 0) CORE.Window.position.y = 0; + + // Obtain recommended CORE.Window.display.width/CORE.Window.display.height from a valid videomode for the monitor + int count = 0; + const GLFWvidmode *modes = glfwGetVideoModes(glfwGetPrimaryMonitor(), &count); + + // Get closest video mode to desired CORE.Window.screen.width/CORE.Window.screen.height + for (int i = 0; i < count; i++) + { + if ((unsigned int)modes[i].width >= CORE.Window.screen.width) + { + if ((unsigned int)modes[i].height >= CORE.Window.screen.height) + { + CORE.Window.display.width = modes[i].width; + CORE.Window.display.height = modes[i].height; + break; + } + } + } + + TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + + // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example, + // for a desired screen size of 800x450 (16:9), closest supported videomode is 800x600 (4:3), + // framebuffer is rendered correctly but once displayed on a 16:9 monitor, it gets stretched + // by the sides to fit all monitor space... + + // Try to setup the most appropriate fullscreen framebuffer for the requested screenWidth/screenHeight + // It considers device display resolution mode and setups a framebuffer with black bars if required (render size/offset) + // Modified global variables: CORE.Window.screen.width/CORE.Window.screen.height - CORE.Window.render.width/CORE.Window.render.height - CORE.Window.renderOffset.x/CORE.Window.renderOffset.y - CORE.Window.screenScale + // TODO: It is a quite cumbersome solution to display size vs requested size, it should be reviewed or removed... + // HighDPI monitors are properly considered in a following similar function: SetupViewport() + SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); + + CORE.Window.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL); + + // NOTE: Full-screen change, not working properly... + // glfwSetWindowMonitor(CORE.Window.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + } + else + { + // No-fullscreen window creation + CORE.Window.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); + + if (CORE.Window.handle) + { + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + } + } + + if (!CORE.Window.handle) + { + glfwTerminate(); + TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); + return false; + } + + // WARNING: glfwCreateWindow() title doesn't work with emscripten + emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " "); + + // Set window callback events + glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! + glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback); + glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback); + glfwSetDropCallback(CORE.Window.handle, WindowDropCallback); + + // Set input callback events + glfwSetKeyCallback(CORE.Window.handle, KeyCallback); + glfwSetCharCallback(CORE.Window.handle, CharCallback); + glfwSetMouseButtonCallback(CORE.Window.handle, MouseButtonCallback); + glfwSetCursorPosCallback(CORE.Window.handle, MouseCursorPosCallback); // Track mouse position changes + glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback); + glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback); + + glfwMakeContextCurrent(CORE.Window.handle); + + // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) + // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need + // to be activated on web platforms since VSync is enforced there. + + int fbWidth = CORE.Window.screen.width; + int fbHeight = CORE.Window.screen.height; + + CORE.Window.render.width = fbWidth; + CORE.Window.render.height = fbHeight; + CORE.Window.currentFbo.width = fbWidth; + CORE.Window.currentFbo.height = fbHeight; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(glfwGetProcAddress); + + // Initialize OpenGL context (states and resources) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + return true; +} + +// GLFW3 Error Callback, runs on GLFW3 error +static void ErrorCallback(int error, const char *description) +{ + TRACELOG(LOG_WARNING, "GLFW: Error: %i Description: %s", error, description); +} + +// GLFW3 WindowSize Callback, runs when window is resizedLastFrame +// NOTE: Window resizing not allowed by default +static void WindowSizeCallback(GLFWwindow *window, int width, int height) +{ + // Reset viewport and projection matrix for new size + SetupViewport(width, height); + + CORE.Window.currentFbo.width = width; + CORE.Window.currentFbo.height = height; + CORE.Window.resizedLastFrame = true; + + if (IsWindowFullscreen()) return; + + // Set current screen size + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + Vector2 windowScaleDPI = GetWindowScaleDPI(); + + CORE.Window.screen.width = (unsigned int)(width/windowScaleDPI.x); + CORE.Window.screen.height = (unsigned int)(height/windowScaleDPI.y); + } + else + { + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + } + + // NOTE: Postprocessing texture is not scaled to new size +} + +// GLFW3 WindowIconify Callback, runs when window is minimized/restored +static void WindowIconifyCallback(GLFWwindow *window, int iconified) +{ + if (iconified) CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; // The window was iconified + else CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // The window was restored +} + +// GLFW3 Window Maximize Callback, runs when window is maximized +static void WindowMaximizeCallback(GLFWwindow *window, int maximized) +{ + +} + +// GLFW3 WindowFocus Callback, runs when window get/lose focus +static void WindowFocusCallback(GLFWwindow *window, int focused) +{ + if (focused) CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // The window was focused + else CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; // The window lost focus +} + +// GLFW3 Window Drop Callback, runs when drop files into window +static void WindowDropCallback(GLFWwindow *window, int count, const char **paths) +{ + if (count > 0) + { + // In case previous dropped filepaths have not been freed, we free them + if (CORE.Window.dropFileCount > 0) + { + for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) RL_FREE(CORE.Window.dropFilepaths[i]); + + RL_FREE(CORE.Window.dropFilepaths); + + CORE.Window.dropFileCount = 0; + CORE.Window.dropFilepaths = NULL; + } + + // WARNING: Paths are freed by GLFW when the callback returns, we must keep an internal copy + CORE.Window.dropFileCount = count; + CORE.Window.dropFilepaths = (char **)RL_CALLOC(CORE.Window.dropFileCount, sizeof(char *)); + + for (unsigned int i = 0; i < CORE.Window.dropFileCount; i++) + { + CORE.Window.dropFilepaths[i] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); + strcpy(CORE.Window.dropFilepaths[i], paths[i]); + } + } +} + + +// GLFW3 Keyboard Callback, runs on key pressed +static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) +{ + if (key < 0) return; // Security check, macOS fn key generates -1 + + // WARNING: GLFW could return GLFW_REPEAT, we need to consider it as 1 + // to work properly with our implementation (IsKeyDown/IsKeyUp checks) + if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0; + else if(action == GLFW_PRESS) CORE.Input.Keyboard.currentKeyState[key] = 1; + else if(action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1; + + // Check if there is space available in the key queue + if ((CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) && (action == GLFW_PRESS)) + { + // Add character to the queue + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = key; + CORE.Input.Keyboard.keyPressedQueueCount++; + } + + // Check the exit key to set close window + if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(CORE.Window.handle, GLFW_TRUE); + +#if defined(SUPPORT_SCREEN_CAPTURE) + if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) + { +#if defined(SUPPORT_GIF_RECORDING) + if (mods & GLFW_MOD_CONTROL) + { + if (gifRecording) + { + gifRecording = false; + + MsfGifResult result = msf_gif_end(&gifState); + + SaveFileData(TextFormat("%s/screenrec%03i.gif", CORE.Storage.basePath, screenshotCounter), result.data, (unsigned int)result.dataSize); + msf_gif_free(result); + + // Download file from MEMFS (emscripten memory filesystem) + // saveFileFromMEMFSToDisk() function is defined in raylib/templates/web_shel/shell.html + emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", TextFormat("screenrec%03i.gif", screenshotCounter - 1), TextFormat("screenrec%03i.gif", screenshotCounter - 1))); + + TRACELOG(LOG_INFO, "SYSTEM: Finish animated GIF recording"); + } + else + { + gifRecording = true; + gifFrameCounter = 0; + + Vector2 scale = GetWindowScaleDPI(); + msf_gif_begin(&gifState, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); + screenshotCounter++; + + TRACELOG(LOG_INFO, "SYSTEM: Start animated GIF recording: %s", TextFormat("screenrec%03i.gif", screenshotCounter)); + } + } + else +#endif // SUPPORT_GIF_RECORDING + { + TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); + screenshotCounter++; + } + } +#endif // SUPPORT_SCREEN_CAPTURE + +#if defined(SUPPORT_EVENTS_AUTOMATION) + if ((key == GLFW_KEY_F11) && (action == GLFW_PRESS)) + { + eventsRecording = !eventsRecording; + + // On finish recording, we export events into a file + if (!eventsRecording) ExportAutomationEvents("eventsrec.rep"); + } + else if ((key == GLFW_KEY_F9) && (action == GLFW_PRESS)) + { + LoadAutomationEvents("eventsrec.rep"); + eventsPlaying = true; + + TRACELOG(LOG_WARNING, "eventsPlaying enabled!"); + } +#endif +} + +// GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value) +static void CharCallback(GLFWwindow *window, unsigned int key) +{ + //TRACELOG(LOG_DEBUG, "Char Callback: KEY:%i(%c)", key, key); + + // NOTE: Registers any key down considering OS keyboard layout but + // does not detect action events, those should be managed by user... + // Ref: https://github.com/glfw/glfw/issues/668#issuecomment-166794907 + // Ref: https://www.glfw.org/docs/latest/input_guide.html#input_char + + // Check if there is space available in the queue + if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) + { + // Add character to the queue + CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = key; + CORE.Input.Keyboard.charPressedQueueCount++; + } +} + +// GLFW3 Mouse Button Callback, runs on mouse button pressed +static void MouseButtonCallback(GLFWwindow *window, int button, int action, int mods) +{ + // WARNING: GLFW could only return GLFW_PRESS (1) or GLFW_RELEASE (0) for now, + // but future releases may add more actions (i.e. GLFW_REPEAT) + CORE.Input.Mouse.currentButtonState[button] = action; + +#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) + // Process mouse events as touches to be able to use mouse-gestures + GestureEvent gestureEvent = { 0 }; + + // Register touch actions + if ((CORE.Input.Mouse.currentButtonState[button] == 1) && (CORE.Input.Mouse.previousButtonState[button] == 0)) gestureEvent.touchAction = TOUCH_ACTION_DOWN; + else if ((CORE.Input.Mouse.currentButtonState[button] == 0) && (CORE.Input.Mouse.previousButtonState[button] == 1)) gestureEvent.touchAction = TOUCH_ACTION_UP; + + // NOTE: TOUCH_ACTION_MOVE event is registered in MouseCursorPosCallback() + + // Assign a pointer ID + gestureEvent.pointId[0] = 0; + + // Register touch points count + gestureEvent.pointCount = 1; + + // Register touch points position, only one point registered + gestureEvent.position[0] = GetMousePosition(); + + // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height + gestureEvent.position[0].x /= (float)GetScreenWidth(); + gestureEvent.position[0].y /= (float)GetScreenHeight(); + + // Gesture data is sent to gestures-system for processing + // Prevent calling ProcessGestureEvent() when Emscripten is present and there's a touch gesture, so EmscriptenTouchCallback() can handle it itself + if (GetMouseX() != 0 || GetMouseY() != 0) ProcessGestureEvent(gestureEvent); + +#endif +} + +// GLFW3 Cursor Position Callback, runs on mouse move +static void MouseCursorPosCallback(GLFWwindow *window, double x, double y) +{ + CORE.Input.Mouse.currentPosition.x = (float)x; + CORE.Input.Mouse.currentPosition.y = (float)y; + CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; + +#if defined(SUPPORT_GESTURES_SYSTEM) && defined(SUPPORT_MOUSE_GESTURES) + // Process mouse events as touches to be able to use mouse-gestures + GestureEvent gestureEvent = { 0 }; + + gestureEvent.touchAction = TOUCH_ACTION_MOVE; + + // Assign a pointer ID + gestureEvent.pointId[0] = 0; + + // Register touch points count + gestureEvent.pointCount = 1; + + // Register touch points position, only one point registered + gestureEvent.position[0] = CORE.Input.Touch.position[0]; + + // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height + gestureEvent.position[0].x /= (float)GetScreenWidth(); + gestureEvent.position[0].y /= (float)GetScreenHeight(); + + // Gesture data is sent to gestures-system for processing + ProcessGestureEvent(gestureEvent); +#endif +} + +// GLFW3 Scrolling Callback, runs on mouse wheel +static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset) +{ + CORE.Input.Mouse.currentWheelMove = (Vector2){ (float)xoffset, (float)yoffset }; +} + +// GLFW3 CursorEnter Callback, when cursor enters the window +static void CursorEnterCallback(GLFWwindow *window, int enter) +{ + if (enter == true) CORE.Input.Mouse.cursorOnScreen = true; + else CORE.Input.Mouse.cursorOnScreen = false; +} + + +// Register fullscreen change events +static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData) +{ + // TODO: Implement EmscriptenFullscreenChangeCallback()? + + return 1; // The event was consumed by the callback handler +} + +// Register window resize event +static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData) +{ + // TODO: Implement EmscriptenWindowResizedCallback()? + + return 1; // The event was consumed by the callback handler +} + +EM_JS(int, GetWindowInnerWidth, (), { return window.innerWidth; }); +EM_JS(int, GetWindowInnerHeight, (), { return window.innerHeight; }); + +// Register DOM element resize event +static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData) +{ + // Don't resize non-resizeable windows + if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) == 0) return 1; + + // This event is called whenever the window changes sizes, + // so the size of the canvas object is explicitly retrieved below + int width = GetWindowInnerWidth(); + int height = GetWindowInnerHeight(); + + if (width < CORE.Window.screenMin.width) width = CORE.Window.screenMin.width; + else if (width > CORE.Window.screenMax.width && CORE.Window.screenMax.width > 0) width = CORE.Window.screenMax.width; + + if (height < CORE.Window.screenMin.height) height = CORE.Window.screenMin.height; + else if (height > CORE.Window.screenMax.height && CORE.Window.screenMax.height > 0) height = CORE.Window.screenMax.height; + + emscripten_set_canvas_element_size("#canvas", width, height); + + SetupViewport(width, height); // Reset viewport and projection matrix for new size + + CORE.Window.currentFbo.width = width; + CORE.Window.currentFbo.height = height; + CORE.Window.resizedLastFrame = true; + + if (IsWindowFullscreen()) return 1; + + // Set current screen size + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + + // NOTE: Postprocessing texture is not scaled to new size + + return 0; +} + +// Register mouse input events +static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData) +{ + // This is only for registering mouse click events with emscripten and doesn't need to do anything + + return 1; // The event was consumed by the callback handler +} + +// Register connected/disconnected gamepads events +static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData) +{ + /* + TRACELOGD("%s: timeStamp: %g, connected: %d, index: %ld, numAxes: %d, numButtons: %d, id: \"%s\", mapping: \"%s\"", + eventType != 0? emscripten_event_type_to_string(eventType) : "Gamepad state", + gamepadEvent->timestamp, gamepadEvent->connected, gamepadEvent->index, gamepadEvent->numAxes, gamepadEvent->numButtons, gamepadEvent->id, gamepadEvent->mapping); + + for (int i = 0; i < gamepadEvent->numAxes; ++i) TRACELOGD("Axis %d: %g", i, gamepadEvent->axis[i]); + for (int i = 0; i < gamepadEvent->numButtons; ++i) TRACELOGD("Button %d: Digital: %d, Analog: %g", i, gamepadEvent->digitalButton[i], gamepadEvent->analogButton[i]); + */ + + if ((gamepadEvent->connected) && (gamepadEvent->index < MAX_GAMEPADS)) + { + CORE.Input.Gamepad.ready[gamepadEvent->index] = true; + sprintf(CORE.Input.Gamepad.name[gamepadEvent->index], "%s", gamepadEvent->id); + } + else CORE.Input.Gamepad.ready[gamepadEvent->index] = false; + + return 1; // The event was consumed by the callback handler +} + +// Register touch input events +static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData) +{ + // Register touch points count + CORE.Input.Touch.pointCount = touchEvent->numTouches; + + double canvasWidth = 0.0; + double canvasHeight = 0.0; + // NOTE: emscripten_get_canvas_element_size() returns canvas.width and canvas.height but + // we are looking for actual CSS size: canvas.style.width and canvas.style.height + // EMSCRIPTEN_RESULT res = emscripten_get_canvas_element_size("#canvas", &canvasWidth, &canvasHeight); + emscripten_get_element_css_size("#canvas", &canvasWidth, &canvasHeight); + + for (int i = 0; (i < CORE.Input.Touch.pointCount) && (i < MAX_TOUCH_POINTS); i++) + { + // Register touch points id + CORE.Input.Touch.pointId[i] = touchEvent->touches[i].identifier; + + // Register touch points position + CORE.Input.Touch.position[i] = (Vector2){touchEvent->touches[i].targetX, touchEvent->touches[i].targetY}; + + // Normalize gestureEvent.position[x] for CORE.Window.screen.width and CORE.Window.screen.height + CORE.Input.Touch.position[i].x *= ((float)GetScreenWidth()/(float)canvasWidth); + CORE.Input.Touch.position[i].y *= ((float)GetScreenHeight()/(float)canvasHeight); + + if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) CORE.Input.Touch.currentTouchState[i] = 1; + else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) CORE.Input.Touch.currentTouchState[i] = 0; + } + +#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_WEB + GestureEvent gestureEvent = {0}; + + gestureEvent.pointCount = CORE.Input.Touch.pointCount; + + // Register touch actions + if (eventType == EMSCRIPTEN_EVENT_TOUCHSTART) gestureEvent.touchAction = TOUCH_ACTION_DOWN; + else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) gestureEvent.touchAction = TOUCH_ACTION_UP; + else if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) gestureEvent.touchAction = TOUCH_ACTION_MOVE; + else if (eventType == EMSCRIPTEN_EVENT_TOUCHCANCEL) gestureEvent.touchAction = TOUCH_ACTION_CANCEL; + + for (int i = 0; (i < gestureEvent.pointCount) && (i < MAX_TOUCH_POINTS); i++) + { + gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i]; + gestureEvent.position[i] = CORE.Input.Touch.position[i]; + + // Normalize gestureEvent.position[i] + gestureEvent.position[i].x /= (float)GetScreenWidth(); + gestureEvent.position[i].y /= (float)GetScreenHeight(); + } + + // Gesture data is sent to gestures system for processing + ProcessGestureEvent(gestureEvent); + + // Reset the pointCount for web, if it was the last Touch End event + if (eventType == EMSCRIPTEN_EVENT_TOUCHEND && CORE.Input.Touch.pointCount == 1) CORE.Input.Touch.pointCount = 0; +#endif + + return 1; // The event was consumed by the callback handler +} + +// EOF From bbbaae55621a854a6e69bcb9cfe1c6a5a8697e2c Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 8 Oct 2023 23:38:52 +0200 Subject: [PATCH 0315/1350] Reviewed #3313 --- src/rcore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 44b7ee787..ff7558dc3 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1050,7 +1050,7 @@ Ray GetMouseRay(Vector2 mouse, Camera camera) } else if (camera.projection == CAMERA_ORTHOGRAPHIC) { - float aspect = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height; + double aspect = (double)CORE.Window.screen.width/(double)CORE.Window.screen.height; double top = camera.fovy/2.0; double right = top*aspect; @@ -1134,7 +1134,7 @@ Vector2 GetWorldToScreenEx(Vector3 position, Camera camera, int width, int heigh } else if (camera.projection == CAMERA_ORTHOGRAPHIC) { - float aspect = (float)CORE.Window.screen.width/(float)CORE.Window.screen.height; + double aspect = (double)width/(double)height; double top = camera.fovy/2.0; double right = top*aspect; From d445fdaa199eae6540621b5a35f2d498f5ffab5c Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 00:41:06 +0200 Subject: [PATCH 0316/1350] WARNING: REDESIGN: Move platform specific data to platform submodules #3313 REVIEWED: Defines, macros, types and tweaks --- src/rcore.c | 70 +++----- src/rcore.h | 149 +++-------------- src/rcore_android.c | 156 +++++++++-------- src/rcore_desktop.c | 189 +++++++++++---------- src/rcore_drm.c | 398 ++++++++++++++++++++++++++------------------ src/rcore_web.c | 75 +++++---- src/utils.h | 2 +- 7 files changed, 519 insertions(+), 520 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index ff7558dc3..f28aff74f 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1,33 +1,18 @@ /********************************************************************************************** * -* rcore - Basic functions to manage windows, OpenGL context and input on multiple platforms +* rcore - Window/display management, Graphic device/context management and input management * * PLATFORMS SUPPORTED: * - PLATFORM_DESKTOP: Windows (Win32, Win64) * - PLATFORM_DESKTOP: Linux (X11 desktop mode) * - PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) * - PLATFORM_DESKTOP: OSX/macOS +* - PLATFORM_WEB: HTML5 (WebAssembly) +* - PLATFORM_DRM: Raspberry Pi 0-5 +* - PLATFORM_DRM: Linux native mode (KMS driver) * - PLATFORM_ANDROID: Android (ARM, ARM64) -* - PLATFORM_DRM: Linux native mode, including Raspberry Pi 4 with V3D fkms driver -* - PLATFORM_WEB: HTML5 with WebAssembly * * CONFIGURATION: -* #define PLATFORM_DESKTOP -* Windowing and input system configured for desktop platforms: -* Windows, Linux, OSX, FreeBSD, OpenBSD, NetBSD, DragonFly -* -* #define PLATFORM_ANDROID -* Windowing and input system configured for Android device, app activity managed internally in this module. -* NOTE: OpenGL ES 2.0 is required and graphic device is managed by EGL -* -* #define PLATFORM_DRM -* Windowing and input system configured for DRM native mode (RPI4 and other devices) -* graphic device is managed by EGL and inputs are processed is raw mode, reading from /dev/input/ -* -* #define PLATFORM_WEB -* Windowing and input system configured for HTML5 (run on browser), code converted from C to asm.js -* using emscripten compiler. OpenGL ES 2.0 required for direct translation to WebGL equivalent code. -* * #define SUPPORT_DEFAULT_FONT (default) * Default font is loaded on window initialization to be available for the user to render simple text. * NOTE: If enabled, uses external module functions to load default raylib font (module: text) @@ -42,11 +27,6 @@ * #define SUPPORT_MOUSE_GESTURES * Mouse gestures are directly mapped like touches and processed by gestures system. * -* #define SUPPORT_SSH_KEYBOARD_RPI (Raspberry Pi only) -* Reconfigure standard input to receive key inputs, works with SSH connection. -* WARNING: Reconfiguring standard input could lead to undesired effects, like breaking other -* running processes orblocking the device if not restored properly. Use with care. -* * #define SUPPORT_BUSY_WAIT_LOOP * Use busy wait loop for timing sync, if not defined, a high-resolution timer is setup and used * @@ -68,7 +48,6 @@ * Support automatic generated events, loading and recording of those events when required * * DEPENDENCIES: -* rglfw - Manage graphic device, OpenGL context and inputs on PLATFORM_DESKTOP (Windows, Linux, OSX, FreeBSD...) * raymath - 3D math functionality (Vector2, Vector3, Matrix, Quaternion) * camera - Multiple 3D camera modes (free, orbital, 1st person, 3rd person) * gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) @@ -76,7 +55,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) +* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) and contributors * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -102,7 +81,7 @@ #include "config.h" // Defines module configuration flags #endif -#include "rcore.h" +#include "rcore.h" // Defines types and globals #define RLGL_IMPLEMENTATION #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 @@ -142,21 +121,19 @@ #endif // Platform specific defines to handle GetApplicationDirectory() -#if defined (PLATFORM_DESKTOP) - #if defined(_WIN32) - #ifndef MAX_PATH - #define MAX_PATH 1025 - #endif - __declspec(dllimport) unsigned long __stdcall GetModuleFileNameA(void *hModule, void *lpFilename, unsigned long nSize); - __declspec(dllimport) unsigned long __stdcall GetModuleFileNameW(void *hModule, void *lpFilename, unsigned long nSize); - __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, void *widestr, int cchwide, void *str, int cbmb, void *defchar, int *used_default); - #elif defined(__linux__) - #include - #elif defined(__APPLE__) - #include - #include - #endif // OSs -#endif // PLATFORM_DESKTOP +#if defined(_WIN32) + #ifndef MAX_PATH + #define MAX_PATH 1025 + #endif +__declspec(dllimport) unsigned long __stdcall GetModuleFileNameA(void *hModule, void *lpFilename, unsigned long nSize); +__declspec(dllimport) unsigned long __stdcall GetModuleFileNameW(void *hModule, void *lpFilename, unsigned long nSize); +__declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, void *widestr, int cchwide, void *str, int cbmb, void *defchar, int *used_default); +#elif defined(__linux__) + #include +#elif defined(__APPLE__) + #include + #include +#endif // OSs #define _CRT_INTERNAL_NONSTDC_NAMES 1 #include // Required for: stat(), S_ISREG [Used in GetFileModTime(), IsFilePath()] @@ -165,7 +142,7 @@ #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #endif -#if defined(PLATFORM_DESKTOP) && defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__)) +#if defined(_WIN32) && (defined(_MSC_VER) || defined(__TINYC__)) #define DIRENT_MALLOC RL_MALLOC #define DIRENT_FREE RL_FREE @@ -333,7 +310,8 @@ const char *TextFormat(const char *text, ...); // Formatting of text with #elif defined(PLATFORM_ANDROID) #include "rcore_android.c" #else - // Software rendering backend, user needs to provide buffer ;) + // TODO: Include your custom platform backend! + // i.e software rendering backend or console backend! #endif //---------------------------------------------------------------------------------- @@ -1897,7 +1875,7 @@ bool IsKeyPressed(int key) return pressed; } -// Check if a key has been pressed again (only PLATFORM_DESKTOP) +// Check if a key has been pressed again bool IsKeyPressedRepeat(int key) { bool repeat = false; @@ -2291,7 +2269,7 @@ void InitTimer(void) timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms) #endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__) struct timespec now = { 0 }; if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success diff --git a/src/rcore.h b/src/rcore.h index 5d9e3bd74..132c8e007 100644 --- a/src/rcore.h +++ b/src/rcore.h @@ -35,64 +35,6 @@ #ifndef RCORE_H #define RCORE_H -#include // Required for: srand(), rand(), atexit() -#include // Required for: sprintf() [Used in OpenURL()] -#include // Required for: strrchr(), strcmp(), strlen(), memset() -#include // Required for: time() [Used in InitTimer()] -#include // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()] - -#include "utils.h" // Required for: TRACELOG() macros - -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - #define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 - // NOTE: Already provided by rlgl implementation (on glad.h) - #include "GLFW/glfw3.h" // GLFW3 library: Windows, OpenGL context and Input management - // NOTE: GLFW3 already includes gl.h (OpenGL) headers -#endif - -#if defined(PLATFORM_ANDROID) - #include // Native platform windowing system interface - //#include // OpenGL ES 2.0 library (not required in this module, only in rlgl) -#endif - -#if defined(PLATFORM_DRM) - - #include // POSIX file control definitions - open(), creat(), fcntl() - #include // POSIX standard function definitions - read(), close(), STDIN_FILENO - #include // POSIX terminal control definitions - tcgetattr(), tcsetattr() - #include // POSIX threads management (inputs reading) - #include // POSIX directory browsing - - #include // Required for: ioctl() - UNIX System call for device-specific input/output operations - #include // Linux: KDSKBMODE, K_MEDIUMRAM constants definition - #include // Linux: Keycodes constants definition (KEY_A, ...) - #include // Linux: Joystick support library - - #include // Generic Buffer Management (native platform for EGL on DRM) - #include // Direct Rendering Manager user-level library interface - #include // Direct Rendering Manager mode setting (KMS) interface - - #include "EGL/egl.h" // Native platform windowing system interface - #include "EGL/eglext.h" // EGL extensions - - typedef struct - { - pthread_t threadId; // Event reading thread id - int fd; // File descriptor to the device it is assigned to - int eventNum; // Number of 'event' device - Rectangle absRange; // Range of values for absolute pointing devices (touchscreens) - int touchSlot; // Hold the touch slot number of the currently being sent multitouch block - bool isMouse; // True if device supports relative X Y movements - bool isTouch; // True if device supports absolute X Y movements and has BTN_TOUCH - bool isMultitouch; // True if device supports multiple absolute movevents and has BTN_TOUCH - bool isKeyboard; // True if device has letter keycodes - bool isGamepad; // True if device has gamepad buttons - } InputEventWorker; - -#endif - -// TODO: PROVIDE A HEADER TO BE USED BY ALL THE rcore_* IMPLEMENTATIONS - #include "raylib.h" #include "rlgl.h" @@ -100,16 +42,17 @@ #define RAYMATH_IMPLEMENTATION #include "raymath.h" +#include "utils.h" // Required for: TRACELOG() macros + +#include // Required for: srand(), rand(), atexit() +#include // Required for: sprintf() [Used in OpenURL()] +#include // Required for: strrchr(), strcmp(), strlen(), memset() +#include // Required for: time() [Used in InitTimer()] +#include // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()] + //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#if defined(PLATFORM_DRM) - #define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event number - - #define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...) - #define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events -#endif - #ifndef MAX_FILEPATH_CAPACITY #define MAX_FILEPATH_CAPACITY 8192 // Maximum capacity for filepath #endif @@ -152,12 +95,6 @@ #define FLAG_TOGGLE(n, f) ((n) ^= (f)) #define FLAG_CHECK(n, f) ((n) & (f)) -// TODO: HACK: Added flag if not provided by GLFW when using external library -// Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev -#if !defined(GLFW_MOUSE_PASSTHROUGH) - #define GLFW_MOUSE_PASSTHROUGH 0x0002000D -#endif - #if (defined(__linux__) || defined(PLATFORM_WEB)) && (_POSIX_C_SOURCE < 199309L) #undef _POSIX_C_SOURCE #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext. @@ -172,25 +109,6 @@ typedef struct { unsigned int width; unsigned int height; } Size; // Core global state context data typedef struct CoreData { struct { -#if defined(PLATFORM_DESKTOP) || defined(PLATFORM_WEB) - GLFWwindow *handle; // GLFW window handle (graphic device) -#endif -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) -#if defined(PLATFORM_DRM) - int fd; // File descriptor for /dev/dri/... - drmModeConnector *connector; // Direct Rendering Manager (DRM) mode connector - drmModeCrtc *crtc; // CRT Controller - int modeIndex; // Index of the used mode of connector->modes - struct gbm_device *gbmDevice; // GBM device - struct gbm_surface *gbmSurface; // GBM surface - struct gbm_bo *prevBO; // Previous GBM buffer object (during frame swapping) - uint32_t prevFB; // Previous GBM framebufer (during frame swapping) -#endif // PLATFORM_DRM - EGLDisplay device; // Native display device (physical screen connection) - EGLSurface surface; // Surface to draw on, framebuffers (connected to context) - EGLContext context; // Graphic context, mode in which drawing can be done - EGLConfig config; // Graphic config -#endif const char *title; // Window text title const pointer unsigned int flags; // Configuration flags (bit based), keeps window state bool ready; // Check if window has been initialized successfully @@ -215,45 +133,27 @@ typedef struct CoreData { char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) unsigned int dropFileCount; // Count dropped files strings - + } Window; -#if defined(PLATFORM_ANDROID) - struct { - bool appEnabled; // Flag to detect if app is active ** = true - struct android_app *app; // Android activity - struct android_poll_source *source; // Android events polling source - bool contextRebindRequired; // Used to know context rebind required - } Android; -#endif struct { const char *basePath; // Base path for data storage + } Storage; struct { -#if defined(PLATFORM_DRM) - InputEventWorker eventWorker[10]; // List of worker threads for every monitored "/dev/input/event" -#endif struct { int exitKey; // Default exit key - char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state - char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state + char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state + char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state + // NOTE: Since key press logic involves comparing prev vs cur key state, we need to handle key repeats specially - char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame. + char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame. - int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue + int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue int keyPressedQueueCount; // Input keys queue count - int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode) + int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode) int charPressedQueueCount; // Input characters queue count -#if defined(PLATFORM_DRM) - int defaultMode; // Default keyboard mode -#if defined(SUPPORT_SSH_KEYBOARD_RPI) - bool evtMode; // Keyboard in event mode -#endif - int defaultFileFlags; // Default IO file flags - struct termios defaultSettings; // Default keyboard settings - int fd; // File descriptor for the evdev keyboard -#endif } Keyboard; struct { Vector2 offset; // Mouse offset @@ -269,11 +169,7 @@ typedef struct CoreData { char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state Vector2 currentWheelMove; // Registers current mouse wheel variation Vector2 previousWheelMove; // Registers previous mouse wheel variation -#if defined(PLATFORM_DRM) - Vector2 eventWheelMove; // Registers the event mouse wheel variation - // NOTE: currentButtonState[] can't be written directly due to multithreading, app could miss the update - char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab -#endif + } Mouse; struct { int pointCount; // Number of touch points active @@ -281,6 +177,7 @@ typedef struct CoreData { Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen char currentTouchState[MAX_TOUCH_POINTS]; // Registers current touch state char previousTouchState[MAX_TOUCH_POINTS]; // Registers previous touch state + } Touch; struct { int lastButtonPressed; // Register last gamepad button pressed @@ -290,10 +187,7 @@ typedef struct CoreData { char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state -#if defined(PLATFORM_DRM) - pthread_t threadId; // Gamepad reading thread id - int streamId[MAX_GAMEPADS]; // Gamepad device file descriptor -#endif + } Gamepad; } Input; struct { @@ -303,10 +197,9 @@ typedef struct CoreData { double draw; // Time measure for frame draw double frame; // Time measure for one frame double target; // Desired time for one frame, if 0 not applied -#if defined(PLATFORM_ANDROID) || defined(PLATFORM_DRM) - unsigned long long int base; // Base time measure for hi-res timer -#endif + unsigned long long int base; // Base time measure for hi-res timer (PLATFORM_ANDROID, PLATFORM_DRM) unsigned int frameCounter; // Frame counter + } Time; } CoreData; diff --git a/src/rcore_android.c b/src/rcore_android.c index 61d729a1c..ad7a905c1 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -53,15 +53,31 @@ #include // Required for: android_app struct and activity management #include // Required for: JNIEnv and JavaVM [Used in OpenURL()] +#include // Native platform windowing system interface + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -//... +typedef struct { + // Application data + struct android_app *app; // Android activity + struct android_poll_source *source; // Android events polling source + bool appEnabled; // Flag to detect if app is active ** = true + bool contextRebindRequired; // Used to know context rebind required + + // Display data + EGLDisplay device; // Native display device (physical screen connection) + EGLSurface surface; // Surface to draw on, framebuffers (connected to context) + EGLContext context; // Graphic context, mode in which drawing can be done + EGLConfig config; // Graphic config +} PlatformData; //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -extern CoreData CORE; // Global CORE state context +extern CoreData CORE; // Global CORE state context + +static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration @@ -89,7 +105,7 @@ extern int main(int argc, char *argv[]); void android_main(struct android_app *app) { char arg0[] = "raylib"; // NOTE: argv[] are mutable - CORE.Android.app = app; + platform.app = app; // NOTE: Return from main is ignored (void)main(1, (char *[]) { arg0, NULL }); @@ -104,9 +120,9 @@ void android_main(struct android_app *app) // Waiting for application events before complete finishing while (!app->destroyRequested) { - while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void **)&CORE.Android.source)) >= 0) + while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void **)&platform.source)) >= 0) { - if (CORE.Android.source != NULL) CORE.Android.source->process(app, CORE.Android.source); + if (platform.source != NULL) platform.source->process(app, platform.source); } } } @@ -114,7 +130,7 @@ void android_main(struct android_app *app) // NOTE: Add this to header (if apps really need it) struct android_app *GetAndroidApp(void) { - return CORE.Android.app; + return platform.app; } // Initialize window and OpenGL context @@ -169,9 +185,9 @@ void InitWindow(int width, int height, const char *title) CORE.Window.currentFbo.height = height; // Set desired windows flags before initializing anything - ANativeActivity_setWindowFlags(CORE.Android.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER + ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER - int orientation = AConfiguration_getOrientation(CORE.Android.app->config); + int orientation = AConfiguration_getOrientation(platform.app->config); if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait"); else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape"); @@ -179,32 +195,32 @@ void InitWindow(int width, int height, const char *title) // TODO: Automatic orientation doesn't seem to work if (width <= height) { - AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_PORT); + AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_PORT); TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait"); } else { - AConfiguration_setOrientation(CORE.Android.app->config, ACONFIGURATION_ORIENTATION_LAND); + AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_LAND); TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape"); } - //AConfiguration_getDensity(CORE.Android.app->config); - //AConfiguration_getKeyboard(CORE.Android.app->config); - //AConfiguration_getScreenSize(CORE.Android.app->config); - //AConfiguration_getScreenLong(CORE.Android.app->config); + //AConfiguration_getDensity(platform.app->config); + //AConfiguration_getKeyboard(platform.app->config); + //AConfiguration_getScreenSize(platform.app->config); + //AConfiguration_getScreenLong(platform.app->config); // Initialize App command system // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()... - CORE.Android.app->onAppCmd = AndroidCommandCallback; + platform.app->onAppCmd = AndroidCommandCallback; // Initialize input events system - CORE.Android.app->onInputEvent = AndroidInputCallback; + platform.app->onInputEvent = AndroidInputCallback; // Initialize assets manager - InitAssetManager(CORE.Android.app->activity->assetManager, CORE.Android.app->activity->internalDataPath); + InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath); // Initialize base path for storage - CORE.Storage.basePath = CORE.Android.app->activity->internalDataPath; + CORE.Storage.basePath = platform.app->activity->internalDataPath; TRACELOG(LOG_INFO, "ANDROID: App initialized successfully"); @@ -216,13 +232,13 @@ void InitWindow(int width, int height, const char *title) while (!CORE.Window.ready) { // Process events loop - while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) + while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&platform.source)) >= 0) { // Process this event - if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); + if (platform.source != NULL) platform.source->process(platform.app, platform.source); // NOTE: Never close window, native activity is controlled by the system! - //if (CORE.Android.app->destroyRequested != 0) CORE.Window.shouldClose = true; + //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true; } } } @@ -250,24 +266,24 @@ void CloseWindow(void) #endif // Close surface, context and display - if (CORE.Window.device != EGL_NO_DISPLAY) + if (platform.device != EGL_NO_DISPLAY) { - eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - if (CORE.Window.surface != EGL_NO_SURFACE) + if (platform.surface != EGL_NO_SURFACE) { - eglDestroySurface(CORE.Window.device, CORE.Window.surface); - CORE.Window.surface = EGL_NO_SURFACE; + eglDestroySurface(platform.device, platform.surface); + platform.surface = EGL_NO_SURFACE; } - if (CORE.Window.context != EGL_NO_CONTEXT) + if (platform.context != EGL_NO_CONTEXT) { - eglDestroyContext(CORE.Window.device, CORE.Window.context); - CORE.Window.context = EGL_NO_CONTEXT; + eglDestroyContext(platform.device, platform.context); + platform.context = EGL_NO_CONTEXT; } - eglTerminate(CORE.Window.device); - CORE.Window.device = EGL_NO_DISPLAY; + eglTerminate(platform.device); + platform.device = EGL_NO_DISPLAY; } #if defined(SUPPORT_EVENTS_AUTOMATION) @@ -306,7 +322,7 @@ bool IsWindowMaximized(void) // Check if window has the focus bool IsWindowFocused(void) { - return CORE.Android.appEnabled; + return platform.appEnabled; } // Check if window has been resizedLastFrame @@ -595,7 +611,7 @@ void OpenURL(const char *url) else { JNIEnv *env = NULL; - JavaVM *vm = CORE.Android.app->activity->vm; + JavaVM *vm = platform.app->activity->vm; (*vm)->AttachCurrentThread(vm, &env, NULL); jstring urlString = (*env)->NewStringUTF(env, url); @@ -612,7 +628,7 @@ void OpenURL(const char *url) (*env)->CallVoidMethod(env, intent, newIntent, actionView, uri); jclass activityClass = (*env)->FindClass(env, "android/app/Activity"); jmethodID startActivity = (*env)->GetMethodID(env, activityClass, "startActivity", "(Landroid/content/Intent;)V"); - (*env)->CallVoidMethod(env, CORE.Android.app->activity->clazz, startActivity, intent); + (*env)->CallVoidMethod(env, platform.app->activity->clazz, startActivity, intent); (*vm)->DetachCurrentThread(vm); } @@ -713,7 +729,7 @@ Vector2 GetTouchPosition(int index) // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { - eglSwapBuffers(CORE.Window.device, CORE.Window.surface); + eglSwapBuffers(platform.device, platform.surface); } // Register all input events @@ -756,17 +772,17 @@ void PollInputEvents(void) int pollEvents = 0; // Poll Events (registered events) - // NOTE: Activity is paused if not enabled (CORE.Android.appEnabled) - while ((pollResult = ALooper_pollAll(CORE.Android.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) + // NOTE: Activity is paused if not enabled (platform.appEnabled) + while ((pollResult = ALooper_pollAll(platform.appEnabled? 0 : -1, NULL, &pollEvents, (void**)&platform.source)) >= 0) { // Process this event - if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); + if (platform.source != NULL) platform.source->process(platform.app, platform.source); // NOTE: Never close window, native activity is controlled by the system! - if (CORE.Android.app->destroyRequested != 0) + if (platform.app->destroyRequested != 0) { //CORE.Window.shouldClose = true; - //ANativeActivity_finish(CORE.Android.app->activity); + //ANativeActivity_finish(platform.app->activity); } } } @@ -829,15 +845,15 @@ static bool InitGraphicsDevice(int width, int height) EGLint numConfigs = 0; // Get an EGL device connection - CORE.Window.device = eglGetDisplay(EGL_DEFAULT_DISPLAY); - if (CORE.Window.device == EGL_NO_DISPLAY) + platform.device = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (platform.device == EGL_NO_DISPLAY) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); return false; } // Initialize the EGL device connection - if (eglInitialize(CORE.Window.device, NULL, NULL) == EGL_FALSE) + if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE) { // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred. TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); @@ -845,14 +861,14 @@ static bool InitGraphicsDevice(int width, int height) } // Get an appropriate EGL framebuffer configuration - eglChooseConfig(CORE.Window.device, framebufferAttribs, &CORE.Window.config, 1, &numConfigs); + eglChooseConfig(platform.device, framebufferAttribs, &platform.config, 1, &numConfigs); // Set rendering API eglBindAPI(EGL_OPENGL_ES_API); // Create an EGL rendering context - CORE.Window.context = eglCreateContext(CORE.Window.device, CORE.Window.config, EGL_NO_CONTEXT, contextAttribs); - if (CORE.Window.context == EGL_NO_CONTEXT) + platform.context = eglCreateContext(platform.device, platform.config, EGL_NO_CONTEXT, contextAttribs); + if (platform.context == EGL_NO_CONTEXT) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); return false; @@ -864,7 +880,7 @@ static bool InitGraphicsDevice(int width, int height) // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry() // As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID - eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat); + eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat); // At this point we need to manage render size vs screen size // NOTE: This function use and modify global module variables: @@ -873,15 +889,15 @@ static bool InitGraphicsDevice(int width, int height) // -> CORE.Window.screenScale SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); - ANativeWindow_setBuffersGeometry(CORE.Android.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat); - //ANativeWindow_setBuffersGeometry(CORE.Android.app->window, 0, 0, displayFormat); // Force use of native display size + ANativeWindow_setBuffersGeometry(platform.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat); + //ANativeWindow_setBuffersGeometry(platform.app->window, 0, 0, displayFormat); // Force use of native display size - CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, CORE.Android.app->window, NULL); + platform.surface = eglCreateWindowSurface(platform.device, platform.config, platform.app->window, NULL); // There must be at least one frame displayed before the buffers are swapped - //eglSwapInterval(CORE.Window.device, 1); + //eglSwapInterval(platform.device, 1); - if (eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context) == EGL_FALSE) + if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); return false; @@ -933,11 +949,11 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) { if (app->window != NULL) { - if (CORE.Android.contextRebindRequired) + if (platform.contextRebindRequired) { // Reset screen scaling to full display size EGLint displayFormat = 0; - eglGetConfigAttrib(CORE.Window.device, CORE.Window.config, EGL_NATIVE_VISUAL_ID, &displayFormat); + eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat); // Adding renderOffset here feels rather hackish, but the viewport scaling is wrong after the // context rebinding if the screen is scaled unless offsets are added. There's probably a more @@ -948,15 +964,15 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) displayFormat); // Recreate display surface and re-attach OpenGL context - CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, app->window, NULL); - eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context); + platform.surface = eglCreateWindowSurface(platform.device, platform.config, app->window, NULL); + eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context); - CORE.Android.contextRebindRequired = false; + platform.contextRebindRequired = false; } else { - CORE.Window.display.width = ANativeWindow_getWidth(CORE.Android.app->window); - CORE.Window.display.height = ANativeWindow_getHeight(CORE.Android.app->window); + CORE.Window.display.width = ANativeWindow_getWidth(platform.app->window); + CORE.Window.display.height = ANativeWindow_getHeight(platform.app->window); // Initialize graphics device (display device and OpenGL context) InitGraphicsDevice(CORE.Window.screen.width, CORE.Window.screen.height); @@ -997,13 +1013,13 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) } break; case APP_CMD_GAINED_FOCUS: { - CORE.Android.appEnabled = true; + platform.appEnabled = true; //ResumeMusicStream(); } break; case APP_CMD_PAUSE: break; case APP_CMD_LOST_FOCUS: { - CORE.Android.appEnabled = false; + platform.appEnabled = false; //PauseMusicStream(); } break; case APP_CMD_TERM_WINDOW: @@ -1012,19 +1028,19 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) // NOTE 1: This case is used when the user exits the app without closing it. We detach the context to ensure everything is recoverable upon resuming. // NOTE 2: Detaching context before destroying display surface avoids losing our resources (textures, shaders, VBOs...) // NOTE 3: In some cases (too many context loaded), OS could unload context automatically... :( - if (CORE.Window.device != EGL_NO_DISPLAY) + if (platform.device != EGL_NO_DISPLAY) { - eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - if (CORE.Window.surface != EGL_NO_SURFACE) + if (platform.surface != EGL_NO_SURFACE) { - eglDestroySurface(CORE.Window.device, CORE.Window.surface); - CORE.Window.surface = EGL_NO_SURFACE; + eglDestroySurface(platform.device, platform.surface); + platform.surface = EGL_NO_SURFACE; } - CORE.Android.contextRebindRequired = true; + platform.contextRebindRequired = true; } - // If 'CORE.Window.device' is already set to 'EGL_NO_DISPLAY' + // If 'platform.device' is already set to 'EGL_NO_DISPLAY' // this means that the user has already called 'CloseWindow()' } break; @@ -1033,8 +1049,8 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) case APP_CMD_DESTROY: break; case APP_CMD_CONFIG_CHANGED: { - //AConfiguration_fromAssetManager(CORE.Android.app->config, CORE.Android.app->activity->assetManager); - //print_cur_config(CORE.Android.app); + //AConfiguration_fromAssetManager(platform.app->config, platform.app->activity->assetManager); + //print_cur_config(platform.app); // Check screen orientation here! } break; diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index f8c6b0e2c..33a4aab0e 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -85,15 +85,28 @@ #include "GLFW/glfw3native.h" // Required for: glfwGetCocoaWindow() #endif +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// TODO: HACK: Added flag if not provided by GLFW when using external library +// Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev +#if !defined(GLFW_MOUSE_PASSTHROUGH) + #define GLFW_MOUSE_PASSTHROUGH 0x0002000D +#endif + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -//... +typedef struct { + GLFWwindow *handle; // GLFW window handle (graphic device) +} PlatformData; //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -extern CoreData CORE; // Global CORE state context +extern CoreData CORE; // Global CORE state context + +static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration @@ -255,7 +268,7 @@ void CloseWindow(void) rlglClose(); // De-init rlgl - glfwDestroyWindow(CORE.Window.handle); + glfwDestroyWindow(platform.handle); glfwTerminate(); #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) @@ -279,10 +292,10 @@ bool WindowShouldClose(void) // While window minimized, stop loop execution while (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)) glfwWaitEvents(); - CORE.Window.shouldClose = glfwWindowShouldClose(CORE.Window.handle); + CORE.Window.shouldClose = glfwWindowShouldClose(platform.handle); // Reset close status for next frame - glfwSetWindowShouldClose(CORE.Window.handle, GLFW_FALSE); + glfwSetWindowShouldClose(platform.handle, GLFW_FALSE); return CORE.Window.shouldClose; } @@ -325,7 +338,7 @@ void ToggleFullscreen(void) if (!CORE.Window.fullscreen) { // Store previous window position (in case we exit fullscreen) - glfwGetWindowPos(CORE.Window.handle, &CORE.Window.position.x, &CORE.Window.position.y); + glfwGetWindowPos(platform.handle, &CORE.Window.position.x, &CORE.Window.position.y); int monitorCount = 0; int monitorIndex = GetCurrentMonitor(); @@ -341,14 +354,14 @@ void ToggleFullscreen(void) CORE.Window.fullscreen = false; CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; - glfwSetWindowMonitor(CORE.Window.handle, NULL, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + glfwSetWindowMonitor(platform.handle, NULL, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } else { CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; - glfwSetWindowMonitor(CORE.Window.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + glfwSetWindowMonitor(platform.handle, monitor, 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } } @@ -357,7 +370,7 @@ void ToggleFullscreen(void) CORE.Window.fullscreen = false; CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; - glfwSetWindowMonitor(CORE.Window.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + glfwSetWindowMonitor(platform.handle, NULL, CORE.Window.position.x, CORE.Window.position.y, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) @@ -368,9 +381,9 @@ void ToggleFullscreen(void) // Set window state: maximized, if resizable void MaximizeWindow(void) { - if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE) + if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) { - glfwMaximizeWindow(CORE.Window.handle); + glfwMaximizeWindow(platform.handle); CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; } } @@ -379,16 +392,16 @@ void MaximizeWindow(void) void MinimizeWindow(void) { // NOTE: Following function launches callback that sets appropriate flag! - glfwIconifyWindow(CORE.Window.handle); + glfwIconifyWindow(platform.handle); } // Set window state: not minimized/maximized void RestoreWindow(void) { - if (glfwGetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE) == GLFW_TRUE) + if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) { // Restores the specified window if it was previously iconified (minimized) or maximized - glfwRestoreWindow(CORE.Window.handle); + glfwRestoreWindow(platform.handle); CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; } @@ -420,13 +433,13 @@ void ToggleBorderlessWindowed(void) { // Store screen position and size // NOTE: If it was on fullscreen, screen position was already stored, so skip setting it here - if (!wasOnFullscreen) glfwGetWindowPos(CORE.Window.handle, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y); + if (!wasOnFullscreen) glfwGetWindowPos(platform.handle, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y); CORE.Window.previousScreen = CORE.Window.screen; // Set undecorated and topmost modes and flags - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE); + glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE); CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE); + glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_TRUE); CORE.Window.flags |= FLAG_WINDOW_TOPMOST; // Get monitor position and size @@ -437,29 +450,29 @@ void ToggleBorderlessWindowed(void) const int monitorHeight = mode->height; // Set screen position and size - glfwSetWindowPos(CORE.Window.handle, monitorPosX, monitorPosY); - glfwSetWindowSize(CORE.Window.handle, monitorWidth, monitorHeight); + glfwSetWindowPos(platform.handle, monitorPosX, monitorPosY); + glfwSetWindowSize(platform.handle, monitorWidth, monitorHeight); // Refocus window - glfwFocusWindow(CORE.Window.handle); + glfwFocusWindow(platform.handle); CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE; } else { // Remove topmost and undecorated modes and flags - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE); + glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_FALSE); CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); + glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE); CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; // Return previous screen size and position // NOTE: The order matters here, it must set size first, then set position, otherwise the screen will be positioned incorrectly - glfwSetWindowSize(CORE.Window.handle, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); - glfwSetWindowPos(CORE.Window.handle, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y); + glfwSetWindowSize(platform.handle, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); + glfwSetWindowPos(platform.handle, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y); // Refocus window - glfwFocusWindow(CORE.Window.handle); + glfwFocusWindow(platform.handle); CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; } @@ -498,21 +511,21 @@ void SetWindowState(unsigned int flags) // State change: FLAG_WINDOW_RESIZABLE if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != (flags & FLAG_WINDOW_RESIZABLE)) && ((flags & FLAG_WINDOW_RESIZABLE) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_TRUE); + glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_TRUE); CORE.Window.flags |= FLAG_WINDOW_RESIZABLE; } // State change: FLAG_WINDOW_UNDECORATED if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) != (flags & FLAG_WINDOW_UNDECORATED)) && (flags & FLAG_WINDOW_UNDECORATED)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE); + glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_FALSE); CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; } // State change: FLAG_WINDOW_HIDDEN if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) != (flags & FLAG_WINDOW_HIDDEN)) && ((flags & FLAG_WINDOW_HIDDEN) > 0)) { - glfwHideWindow(CORE.Window.handle); + glfwHideWindow(platform.handle); CORE.Window.flags |= FLAG_WINDOW_HIDDEN; } @@ -533,14 +546,14 @@ void SetWindowState(unsigned int flags) // State change: FLAG_WINDOW_UNFOCUSED if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) != (flags & FLAG_WINDOW_UNFOCUSED)) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_FALSE); + glfwSetWindowAttrib(platform.handle, GLFW_FOCUS_ON_SHOW, GLFW_FALSE); CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; } // State change: FLAG_WINDOW_TOPMOST if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) != (flags & FLAG_WINDOW_TOPMOST)) && ((flags & FLAG_WINDOW_TOPMOST) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE); + glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_TRUE); CORE.Window.flags |= FLAG_WINDOW_TOPMOST; } @@ -567,7 +580,7 @@ void SetWindowState(unsigned int flags) // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) != (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH)) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); + glfwSetWindowAttrib(platform.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_TRUE); CORE.Window.flags |= FLAG_WINDOW_MOUSE_PASSTHROUGH; } @@ -613,14 +626,14 @@ void ClearWindowState(unsigned int flags) // State change: FLAG_WINDOW_RESIZABLE if (((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) && ((flags & FLAG_WINDOW_RESIZABLE) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_RESIZABLE, GLFW_FALSE); + glfwSetWindowAttrib(platform.handle, GLFW_RESIZABLE, GLFW_FALSE); CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE; } // State change: FLAG_WINDOW_HIDDEN if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) && ((flags & FLAG_WINDOW_HIDDEN) > 0)) { - glfwShowWindow(CORE.Window.handle); + glfwShowWindow(platform.handle); CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; } @@ -639,21 +652,21 @@ void ClearWindowState(unsigned int flags) // State change: FLAG_WINDOW_UNDECORATED if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) && ((flags & FLAG_WINDOW_UNDECORATED) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); + glfwSetWindowAttrib(platform.handle, GLFW_DECORATED, GLFW_TRUE); CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; } // State change: FLAG_WINDOW_UNFOCUSED if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FOCUS_ON_SHOW, GLFW_TRUE); + glfwSetWindowAttrib(platform.handle, GLFW_FOCUS_ON_SHOW, GLFW_TRUE); CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; } // State change: FLAG_WINDOW_TOPMOST if (((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) && ((flags & FLAG_WINDOW_TOPMOST) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE); + glfwSetWindowAttrib(platform.handle, GLFW_FLOATING, GLFW_FALSE); CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; } @@ -680,7 +693,7 @@ void ClearWindowState(unsigned int flags) // State change: FLAG_WINDOW_MOUSE_PASSTHROUGH if (((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) && ((flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0)) { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); + glfwSetWindowAttrib(platform.handle, GLFW_MOUSE_PASSTHROUGH, GLFW_FALSE); CORE.Window.flags &= ~FLAG_WINDOW_MOUSE_PASSTHROUGH; } @@ -705,7 +718,7 @@ void SetWindowIcon(Image image) if (image.data == NULL) { // Revert to the default window icon, pass in an empty image array - glfwSetWindowIcon(CORE.Window.handle, 0, NULL); + glfwSetWindowIcon(platform.handle, 0, NULL); } else { @@ -719,7 +732,7 @@ void SetWindowIcon(Image image) // NOTE 1: We only support one image icon // NOTE 2: The specified image data is copied before this function returns - glfwSetWindowIcon(CORE.Window.handle, 1, icon); + glfwSetWindowIcon(platform.handle, 1, icon); } else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); } @@ -734,7 +747,7 @@ void SetWindowIcons(Image *images, int count) if ((images == NULL) || (count <= 0)) { // Revert to the default window icon, pass in an empty image array - glfwSetWindowIcon(CORE.Window.handle, 0, NULL); + glfwSetWindowIcon(platform.handle, 0, NULL); } else { @@ -754,7 +767,7 @@ void SetWindowIcons(Image *images, int count) else TRACELOG(LOG_WARNING, "GLFW: Window icon image must be in R8G8B8A8 pixel format"); } // NOTE: Images data is copied internally before this function returns - glfwSetWindowIcon(CORE.Window.handle, valid, icons); + glfwSetWindowIcon(platform.handle, valid, icons); RL_FREE(icons); } @@ -764,13 +777,13 @@ void SetWindowIcons(Image *images, int count) void SetWindowTitle(const char *title) { CORE.Window.title = title; - glfwSetWindowTitle(CORE.Window.handle, title); + glfwSetWindowTitle(platform.handle, title); } // Set window position on screen (windowed mode) void SetWindowPosition(int x, int y) { - glfwSetWindowPos(CORE.Window.handle, x, y); + glfwSetWindowPos(platform.handle, x, y); } // Set monitor for the current window @@ -786,7 +799,7 @@ void SetWindowMonitor(int monitor) TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - glfwSetWindowMonitor(CORE.Window.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate); + glfwSetWindowMonitor(platform.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate); } else { @@ -801,12 +814,12 @@ void SetWindowMonitor(int monitor) glfwGetMonitorWorkarea(monitors[monitor], &monitorWorkareaX, &monitorWorkareaY, &monitorWorkareaWidth, &monitorWorkareaHeight); // If the screen size is larger than the monitor workarea, anchor it on the top left corner, otherwise, center it - if ((screenWidth >= monitorWorkareaWidth) || (screenHeight >= monitorWorkareaHeight)) glfwSetWindowPos(CORE.Window.handle, monitorWorkareaX, monitorWorkareaY); + if ((screenWidth >= monitorWorkareaWidth) || (screenHeight >= monitorWorkareaHeight)) glfwSetWindowPos(platform.handle, monitorWorkareaX, monitorWorkareaY); else { const int x = monitorWorkareaX + (monitorWorkareaWidth/2) - (screenWidth/2); const int y = monitorWorkareaY + (monitorWorkareaHeight/2) - (screenHeight/2); - glfwSetWindowPos(CORE.Window.handle, x, y); + glfwSetWindowPos(platform.handle, x, y); } } } @@ -822,7 +835,7 @@ void SetWindowMinSize(int width, int height) int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.height; int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.width; int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.height; - glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); + glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight); } // Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) @@ -834,13 +847,13 @@ void SetWindowMaxSize(int width, int height) int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.height; int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.width; int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.height; - glfwSetWindowSizeLimits(CORE.Window.handle, minWidth, minHeight, maxWidth, maxHeight); + glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight); } // Set window dimensions void SetWindowSize(int width, int height) { - glfwSetWindowSize(CORE.Window.handle, width, height); + glfwSetWindowSize(platform.handle, width, height); } // Set window opacity, value opacity is between 0.0 and 1.0 @@ -848,13 +861,13 @@ void SetWindowOpacity(float opacity) { if (opacity >= 1.0f) opacity = 1.0f; else if (opacity <= 0.0f) opacity = 0.0f; - glfwSetWindowOpacity(CORE.Window.handle, opacity); + glfwSetWindowOpacity(platform.handle, opacity); } // Set window focused void SetWindowFocused(void) { - glfwFocusWindow(CORE.Window.handle); + glfwFocusWindow(platform.handle); } // Get native window handle @@ -862,19 +875,19 @@ void *GetWindowHandle(void) { #if defined(_WIN32) // NOTE: Returned handle is: void *HWND (windows.h) - return glfwGetWin32Window(CORE.Window.handle); + return glfwGetWin32Window(platform.handle); #endif #if defined(__linux__) // NOTE: Returned handle is: unsigned long Window (X.h) // typedef unsigned long XID; // typedef XID Window; - //unsigned long id = (unsigned long)glfwGetX11Window(CORE.Window.handle); + //unsigned long id = (unsigned long)glfwGetX11Window(platform.handle); //return NULL; // TODO: Find a way to return value... cast to void *? - return (void *)CORE.Window.handle; + return (void *)platform.handle; #endif #if defined(__APPLE__) // NOTE: Returned handle is: (objc_object *) - return (void *)glfwGetCocoaWindow(CORE.Window.handle); + return (void *)glfwGetCocoaWindow(platform.handle); #endif return NULL; @@ -903,7 +916,7 @@ int GetCurrentMonitor(void) if (IsWindowFullscreen()) { // Get the handle of the monitor that the specified window is in full screen on - monitor = glfwGetWindowMonitor(CORE.Window.handle); + monitor = glfwGetWindowMonitor(platform.handle); for (int i = 0; i < monitorCount; i++) { @@ -919,7 +932,7 @@ int GetCurrentMonitor(void) int x = 0; int y = 0; - glfwGetWindowPos(CORE.Window.handle, &x, &y); + glfwGetWindowPos(platform.handle, &x, &y); for (int i = 0; i < monitorCount; i++) { @@ -1070,7 +1083,7 @@ Vector2 GetWindowPosition(void) int x = 0; int y = 0; - glfwGetWindowPos(CORE.Window.handle, &x, &y); + glfwGetWindowPos(platform.handle, &x, &y); return (Vector2){ (float)x, (float)y }; } @@ -1109,34 +1122,34 @@ Vector2 GetWindowScaleDPI(void) // Set clipboard text content void SetClipboardText(const char *text) { - glfwSetClipboardString(CORE.Window.handle, text); + glfwSetClipboardString(platform.handle, text); } // Get clipboard text content // NOTE: returned string is allocated and freed by GLFW const char *GetClipboardText(void) { - return glfwGetClipboardString(CORE.Window.handle); + return glfwGetClipboardString(platform.handle); } // Show mouse cursor void ShowCursor(void) { - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); CORE.Input.Mouse.cursorHidden = false; } // Hides mouse cursor void HideCursor(void) { - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); CORE.Input.Mouse.cursorHidden = true; } // Enables cursor (unlock cursor) void EnableCursor(void) { - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_NORMAL); // Set cursor position in the middle SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); @@ -1147,7 +1160,7 @@ void EnableCursor(void) // Disables cursor (lock cursor) void DisableCursor(void) { - glfwSetInputMode(CORE.Window.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + glfwSetInputMode(platform.handle, GLFW_CURSOR, GLFW_CURSOR_DISABLED); // Set cursor position in the middle SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); @@ -1276,7 +1289,7 @@ void SetMousePosition(int x, int y) CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; // NOTE: emscripten not implemented - glfwSetCursorPos(CORE.Window.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); + glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); } // Get mouse wheel movement Y @@ -1294,11 +1307,11 @@ float GetMouseWheelMove(void) void SetMouseCursor(int cursor) { CORE.Input.Mouse.cursor = cursor; - if (cursor == MOUSE_CURSOR_DEFAULT) glfwSetCursor(CORE.Window.handle, NULL); + if (cursor == MOUSE_CURSOR_DEFAULT) glfwSetCursor(platform.handle, NULL); else { // NOTE: We are relating internal GLFW enum values to our MouseCursor enum values - glfwSetCursor(CORE.Window.handle, glfwCreateStandardCursor(0x00036000 + cursor)); + glfwSetCursor(platform.handle, glfwCreateStandardCursor(0x00036000 + cursor)); } } @@ -1331,7 +1344,7 @@ Vector2 GetTouchPosition(int index) // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { - glfwSwapBuffers(CORE.Window.handle); + glfwSwapBuffers(platform.handle); } // Register all input events @@ -1691,10 +1704,10 @@ static bool InitGraphicsDevice(int width, int height) // HighDPI monitors are properly considered in a following similar function: SetupViewport() SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); - CORE.Window.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL); + platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL); // NOTE: Full-screen change, not working properly... - //glfwSetWindowMonitor(CORE.Window.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + //glfwSetWindowMonitor(platform.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } else { @@ -1705,16 +1718,16 @@ static bool InitGraphicsDevice(int width, int height) } // No-fullscreen window creation - CORE.Window.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); + platform.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); - if (CORE.Window.handle) + if (platform.handle) { CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; } } - if (!CORE.Window.handle) + if (!platform.handle) { glfwTerminate(); TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); @@ -1722,23 +1735,23 @@ static bool InitGraphicsDevice(int width, int height) } // Set window callback events - glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! - glfwSetWindowMaximizeCallback(CORE.Window.handle, WindowMaximizeCallback); - glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback); - glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback); - glfwSetDropCallback(CORE.Window.handle, WindowDropCallback); + glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! + glfwSetWindowMaximizeCallback(platform.handle, WindowMaximizeCallback); + glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback); + glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback); + glfwSetDropCallback(platform.handle, WindowDropCallback); // Set input callback events - glfwSetKeyCallback(CORE.Window.handle, KeyCallback); - glfwSetCharCallback(CORE.Window.handle, CharCallback); - glfwSetMouseButtonCallback(CORE.Window.handle, MouseButtonCallback); - glfwSetCursorPosCallback(CORE.Window.handle, MouseCursorPosCallback); // Track mouse position changes - glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback); - glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback); + glfwSetKeyCallback(platform.handle, KeyCallback); + glfwSetCharCallback(platform.handle, CharCallback); + glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback); + glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes + glfwSetScrollCallback(platform.handle, MouseScrollCallback); + glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); - glfwMakeContextCurrent(CORE.Window.handle); + glfwMakeContextCurrent(platform.handle); - glfwSetInputMode(CORE.Window.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) + glfwSetInputMode(platform.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) glfwSwapInterval(0); // No V-Sync by default @@ -1760,7 +1773,7 @@ static bool InitGraphicsDevice(int width, int height) // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling. // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE); #if !defined(__APPLE__) - glfwGetFramebufferSize(CORE.Window.handle, &fbWidth, &fbHeight); + glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight); // Screen scaling matrix is required in case desired screen area is different from display area CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f); @@ -1913,7 +1926,7 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i } // Check the exit key to set close window - if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(CORE.Window.handle, GLFW_TRUE); + if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE); #if defined(SUPPORT_SCREEN_CAPTURE) if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 2c05ab46e..8c63b63da 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -17,8 +17,10 @@ * - TRACELOG() function is located in raylib [utils] module * * CONFIGURATION: -* #define RCORE_DRM_CUSTOM_FLAG -* Custom flag for rcore on PLATFORM_DRM -not used- +* #define SUPPORT_SSH_KEYBOARD_RPI (Raspberry Pi only) +* Reconfigure standard input to receive key inputs, works with SSH connection. +* WARNING: Reconfiguring standard input could lead to undesired effects, like breaking other +* running processes orblocking the device if not restored properly. Use with care. * * DEPENDENCIES: * gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) @@ -47,15 +49,93 @@ #include "rcore.h" +#include // POSIX file control definitions - open(), creat(), fcntl() +#include // POSIX standard function definitions - read(), close(), STDIN_FILENO +#include // POSIX terminal control definitions - tcgetattr(), tcsetattr() +#include // POSIX threads management (inputs reading) +#include // POSIX directory browsing + +#include // Required for: ioctl() - UNIX System call for device-specific input/output operations +#include // Linux: KDSKBMODE, K_MEDIUMRAM constants definition +#include // Linux: Keycodes constants definition (KEY_A, ...) +#include // Linux: Joystick support library + +#include // Generic Buffer Management (native platform for EGL on DRM) +#include // Direct Rendering Manager user-level library interface +#include // Direct Rendering Manager mode setting (KMS) interface + +#include "EGL/egl.h" // Native platform windowing system interface +#include "EGL/eglext.h" // EGL extensions + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#define USE_LAST_TOUCH_DEVICE // When multiple touchscreens are connected, only use the one with the highest event number + +#define DEFAULT_GAMEPAD_DEV "/dev/input/js" // Gamepad input (base dev for all gamepads: js0, js1, ...) +#define DEFAULT_EVDEV_PATH "/dev/input/" // Path to the linux input events + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -//... + +typedef struct { + pthread_t threadId; // Event reading thread id + + int fd; // File descriptor to the device it is assigned to + int eventNum; // Number of 'event' device + Rectangle absRange; // Range of values for absolute pointing devices (touchscreens) + int touchSlot; // Hold the touch slot number of the currently being sent multitouch block + bool isMouse; // True if device supports relative X Y movements + bool isTouch; // True if device supports absolute X Y movements and has BTN_TOUCH + bool isMultitouch; // True if device supports multiple absolute movevents and has BTN_TOUCH + bool isKeyboard; // True if device has letter keycodes + bool isGamepad; // True if device has gamepad buttons +} InputEventWorker; + +typedef struct { + // Display data + int fd; // File descriptor for /dev/dri/... + drmModeConnector *connector; // Direct Rendering Manager (DRM) mode connector + drmModeCrtc *crtc; // CRT Controller + int modeIndex; // Index of the used mode of connector->modes + struct gbm_device *gbmDevice; // GBM device + struct gbm_surface *gbmSurface; // GBM surface + struct gbm_bo *prevBO; // Previous GBM buffer object (during frame swapping) + uint32_t prevFB; // Previous GBM framebufer (during frame swapping) + + EGLDisplay device; // Native display device (physical screen connection) + EGLSurface surface; // Surface to draw on, framebuffers (connected to context) + EGLContext context; // Graphic context, mode in which drawing can be done + EGLConfig config; // Graphic config + + // Input data + InputEventWorker eventWorker[10]; // List of worker threads for every monitored "/dev/input/event" + + // Keyboard data + int defaultKeyboardMode; // Default keyboard mode + bool eventKeyboardMode; // Keyboard in event mode + int defaultFileFlags; // Default IO file flags + struct termios defaultSettings; // Default keyboard settings + int keyboardFd; // File descriptor for the evdev keyboard + + // Mouse data + Vector2 eventWheelMove; // Registers the event mouse wheel variation + // NOTE: currentButtonState[] can't be written directly due to multithreading, app could miss the update + char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab + + // Gamepad data + pthread_t gamepadThreadId; // Gamepad reading thread id + int gamepadStreamFd[MAX_GAMEPADS]; // Gamepad device file descriptor + +} PlatformData; //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -extern CoreData CORE; // Global CORE state context +extern CoreData CORE; // Global CORE state context + +static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration @@ -227,67 +307,67 @@ void CloseWindow(void) timeEndPeriod(1); // Restore time period #endif - if (CORE.Window.prevFB) + if (platform.prevFB) { - drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB); - CORE.Window.prevFB = 0; + drmModeRmFB(platform.fd, platform.prevFB); + platform.prevFB = 0; } - if (CORE.Window.prevBO) + if (platform.prevBO) { - gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO); - CORE.Window.prevBO = NULL; + gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO); + platform.prevBO = NULL; } - if (CORE.Window.gbmSurface) + if (platform.gbmSurface) { - gbm_surface_destroy(CORE.Window.gbmSurface); - CORE.Window.gbmSurface = NULL; + gbm_surface_destroy(platform.gbmSurface); + platform.gbmSurface = NULL; } - if (CORE.Window.gbmDevice) + if (platform.gbmDevice) { - gbm_device_destroy(CORE.Window.gbmDevice); - CORE.Window.gbmDevice = NULL; + gbm_device_destroy(platform.gbmDevice); + platform.gbmDevice = NULL; } - if (CORE.Window.crtc) + if (platform.crtc) { - if (CORE.Window.connector) + if (platform.connector) { - drmModeSetCrtc(CORE.Window.fd, CORE.Window.crtc->crtc_id, CORE.Window.crtc->buffer_id, - CORE.Window.crtc->x, CORE.Window.crtc->y, &CORE.Window.connector->connector_id, 1, &CORE.Window.crtc->mode); - drmModeFreeConnector(CORE.Window.connector); - CORE.Window.connector = NULL; + drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, platform.crtc->buffer_id, + platform.crtc->x, platform.crtc->y, &platform.connector->connector_id, 1, &platform.crtc->mode); + drmModeFreeConnector(platform.connector); + platform.connector = NULL; } - drmModeFreeCrtc(CORE.Window.crtc); - CORE.Window.crtc = NULL; + drmModeFreeCrtc(platform.crtc); + platform.crtc = NULL; } - if (CORE.Window.fd != -1) + if (platform.fd != -1) { - close(CORE.Window.fd); - CORE.Window.fd = -1; + close(platform.fd); + platform.fd = -1; } // Close surface, context and display - if (CORE.Window.device != EGL_NO_DISPLAY) + if (platform.device != EGL_NO_DISPLAY) { - if (CORE.Window.surface != EGL_NO_SURFACE) + if (platform.surface != EGL_NO_SURFACE) { - eglDestroySurface(CORE.Window.device, CORE.Window.surface); - CORE.Window.surface = EGL_NO_SURFACE; + eglDestroySurface(platform.device, platform.surface); + platform.surface = EGL_NO_SURFACE; } - if (CORE.Window.context != EGL_NO_CONTEXT) + if (platform.context != EGL_NO_CONTEXT) { - eglDestroyContext(CORE.Window.device, CORE.Window.context); - CORE.Window.context = EGL_NO_CONTEXT; + eglDestroyContext(platform.device, platform.context); + platform.context = EGL_NO_CONTEXT; } - eglTerminate(CORE.Window.device); - CORE.Window.device = EGL_NO_DISPLAY; + eglTerminate(platform.device); + platform.device = EGL_NO_DISPLAY; } // Wait for mouse and gamepad threads to finish before closing @@ -297,21 +377,21 @@ void CloseWindow(void) CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called // Close the evdev keyboard - if (CORE.Input.Keyboard.fd != -1) + if (platform.keyboardFd != -1) { - close(CORE.Input.Keyboard.fd); - CORE.Input.Keyboard.fd = -1; + close(platform.keyboardFd); + platform.keyboardFd = -1; } - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i) { - if (CORE.Input.eventWorker[i].threadId) + if (platform.eventWorker[i].threadId) { - pthread_join(CORE.Input.eventWorker[i].threadId, NULL); + pthread_join(platform.eventWorker[i].threadId, NULL); } } - if (CORE.Input.Gamepad.threadId) pthread_join(CORE.Input.Gamepad.threadId, NULL); + if (platform.gamepadThreadId) pthread_join(platform.gamepadThreadId, NULL); #if defined(SUPPORT_EVENTS_AUTOMATION) RL_FREE(events); @@ -524,9 +604,9 @@ int GetMonitorRefreshRate(int monitor) { int refresh = 0; - if ((CORE.Window.connector) && (CORE.Window.modeIndex >= 0)) + if ((platform.connector) && (platform.modeIndex >= 0)) { - refresh = CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh; + refresh = platform.connector->modes[platform.modeIndex].vrefresh; } return refresh; @@ -659,7 +739,7 @@ const char *GetGamepadName(int gamepad) if (CORE.Input.Gamepad.ready[gamepad]) { - ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); + ioctl(platform.gamepadStreamFd[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); name = CORE.Input.Gamepad.name[gamepad]; } @@ -670,7 +750,7 @@ const char *GetGamepadName(int gamepad) int GetGamepadAxisCount(int gamepad) { int axisCount = 0; - if (CORE.Input.Gamepad.ready[gamepad]) ioctl(CORE.Input.Gamepad.streamId[gamepad], JSIOCGAXES, &axisCount); + if (CORE.Input.Gamepad.ready[gamepad]) ioctl(platform.gamepadStreamFd[gamepad], JSIOCGAXES, &axisCount); CORE.Input.Gamepad.axisCount = axisCount; return CORE.Input.Gamepad.axisCount; @@ -756,31 +836,31 @@ Vector2 GetTouchPosition(int index) // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { - eglSwapBuffers(CORE.Window.device, CORE.Window.surface); + eglSwapBuffers(platform.device, platform.surface); - if (!CORE.Window.gbmSurface || (-1 == CORE.Window.fd) || !CORE.Window.connector || !CORE.Window.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); + if (!platform.gbmSurface || (-1 == platform.fd) || !platform.connector || !platform.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); - struct gbm_bo *bo = gbm_surface_lock_front_buffer(CORE.Window.gbmSurface); + struct gbm_bo *bo = gbm_surface_lock_front_buffer(platform.gbmSurface); if (!bo) TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer"); uint32_t fb = 0; - int result = drmModeAddFB(CORE.Window.fd, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, 24, 32, gbm_bo_get_stride(bo), gbm_bo_get_handle(bo).u32, &fb); + int result = drmModeAddFB(platform.fd, platform.connector->modes[platform.modeIndex].hdisplay, platform.connector->modes[platform.modeIndex].vdisplay, 24, 32, gbm_bo_get_stride(bo), gbm_bo_get_handle(bo).u32, &fb); if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d", result); - result = drmModeSetCrtc(CORE.Window.fd, CORE.Window.crtc->crtc_id, fb, 0, 0, &CORE.Window.connector->connector_id, 1, &CORE.Window.connector->modes[CORE.Window.modeIndex]); + result = drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fb, 0, 0, &platform.connector->connector_id, 1, &platform.connector->modes[platform.modeIndex]); if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result); - if (CORE.Window.prevFB) + if (platform.prevFB) { - result = drmModeRmFB(CORE.Window.fd, CORE.Window.prevFB); + result = drmModeRmFB(platform.fd, platform.prevFB); if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result); } - CORE.Window.prevFB = fb; + platform.prevFB = fb; - if (CORE.Window.prevBO) gbm_surface_release_buffer(CORE.Window.gbmSurface, CORE.Window.prevBO); + if (platform.prevBO) gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO); - CORE.Window.prevBO = bo; + platform.prevBO = bo; } // Register all input events @@ -814,12 +894,12 @@ void PollInputEvents(void) // Register previous mouse states CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; - CORE.Input.Mouse.currentWheelMove = CORE.Input.Mouse.eventWheelMove; - CORE.Input.Mouse.eventWheelMove = (Vector2){ 0.0f, 0.0f }; + CORE.Input.Mouse.currentWheelMove = platform.eventWheelMove; + platform.eventWheelMove = (Vector2){ 0.0f, 0.0f }; for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) { CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; - CORE.Input.Mouse.currentButtonState[i] = CORE.Input.Mouse.currentButtonStateEvdev[i]; + CORE.Input.Mouse.currentButtonState[i] = platform.currentButtonStateEvdev[i]; } // Register gamepads buttons events @@ -844,7 +924,7 @@ void PollInputEvents(void) // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here. // stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console - if (!CORE.Input.Keyboard.evtMode) ProcessKeyboard(); + if (!platform.eventKeyboardMode) ProcessKeyboard(); // NOTE: Mouse input events polling is done asynchronously in another pthread - EventThread() // NOTE: Gamepad (Joystick) input events polling is done asynchonously in another pthread - GamepadThread() @@ -877,41 +957,41 @@ static bool InitGraphicsDevice(int width, int height) CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; - CORE.Window.fd = -1; - CORE.Window.connector = NULL; - CORE.Window.modeIndex = -1; - CORE.Window.crtc = NULL; - CORE.Window.gbmDevice = NULL; - CORE.Window.gbmSurface = NULL; - CORE.Window.prevBO = NULL; - CORE.Window.prevFB = 0; + platform.fd = -1; + platform.connector = NULL; + platform.modeIndex = -1; + platform.crtc = NULL; + platform.gbmDevice = NULL; + platform.gbmSurface = NULL; + platform.prevBO = NULL; + platform.prevFB = 0; #if defined(DEFAULT_GRAPHIC_DEVICE_DRM) - CORE.Window.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR); + platform.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR); #else TRACELOG(LOG_INFO, "DISPLAY: No graphic card set, trying platform-gpu-card"); - CORE.Window.fd = open("/dev/dri/by-path/platform-gpu-card", O_RDWR); // VideoCore VI (Raspberry Pi 4) + platform.fd = open("/dev/dri/by-path/platform-gpu-card", O_RDWR); // VideoCore VI (Raspberry Pi 4) - if ((CORE.Window.fd == -1) || (drmModeGetResources(CORE.Window.fd) == NULL)) + if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL)) { TRACELOG(LOG_INFO, "DISPLAY: Failed to open platform-gpu-card, trying card1"); - CORE.Window.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded + platform.fd = open("/dev/dri/card1", O_RDWR); // Other Embedded } - if ((CORE.Window.fd == -1) || (drmModeGetResources(CORE.Window.fd) == NULL)) + if ((platform.fd == -1) || (drmModeGetResources(platform.fd) == NULL)) { TRACELOG(LOG_INFO, "DISPLAY: Failed to open graphic card1, trying card0"); - CORE.Window.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3) + platform.fd = open("/dev/dri/card0", O_RDWR); // VideoCore IV (Raspberry Pi 1-3) } #endif - if (CORE.Window.fd == -1) + if (platform.fd == -1) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card"); return false; } - drmModeRes *res = drmModeGetResources(CORE.Window.fd); + drmModeRes *res = drmModeGetResources(platform.fd); if (!res) { TRACELOG(LOG_WARNING, "DISPLAY: Failed get DRM resources"); @@ -924,13 +1004,13 @@ static bool InitGraphicsDevice(int width, int height) { TRACELOG(LOG_TRACE, "DISPLAY: Connector index %i", i); - drmModeConnector *con = drmModeGetConnector(CORE.Window.fd, res->connectors[i]); + drmModeConnector *con = drmModeGetConnector(platform.fd, res->connectors[i]); TRACELOG(LOG_TRACE, "DISPLAY: Connector modes detected: %i", con->count_modes); if ((con->connection == DRM_MODE_CONNECTED) && (con->encoder_id)) { TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected"); - CORE.Window.connector = con; + platform.connector = con; break; } else @@ -940,14 +1020,14 @@ static bool InitGraphicsDevice(int width, int height) } } - if (!CORE.Window.connector) + if (!platform.connector) { TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found"); drmModeFreeResources(res); return false; } - drmModeEncoder *enc = drmModeGetEncoder(CORE.Window.fd, CORE.Window.connector->encoder_id); + drmModeEncoder *enc = drmModeGetEncoder(platform.fd, platform.connector->encoder_id); if (!enc) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder"); @@ -955,8 +1035,8 @@ static bool InitGraphicsDevice(int width, int height) return false; } - CORE.Window.crtc = drmModeGetCrtc(CORE.Window.fd, enc->crtc_id); - if (!CORE.Window.crtc) + platform.crtc = drmModeGetCrtc(platform.fd, enc->crtc_id); + if (!platform.crtc) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc"); drmModeFreeEncoder(enc); @@ -969,9 +1049,9 @@ static bool InitGraphicsDevice(int width, int height) { TRACELOG(LOG_TRACE, "DISPLAY: Selecting DRM connector mode for current used mode..."); - CORE.Window.modeIndex = FindMatchingConnectorMode(CORE.Window.connector, &CORE.Window.crtc->mode); + platform.modeIndex = FindMatchingConnectorMode(platform.connector, &platform.crtc->mode); - if (CORE.Window.modeIndex < 0) + if (platform.modeIndex < 0) { TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found"); drmModeFreeEncoder(enc); @@ -987,19 +1067,19 @@ static bool InitGraphicsDevice(int width, int height) const int fps = (CORE.Time.target > 0)? (1.0/CORE.Time.target) : 60; // Try to find an exact matching mode - CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced); + platform.modeIndex = FindExactConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced); // If nothing found, try to find a nearly matching mode - if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced); + if (platform.modeIndex < 0) platform.modeIndex = FindNearestConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, allowInterlaced); // If nothing found, try to find an exactly matching mode including interlaced - if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindExactConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true); + if (platform.modeIndex < 0) platform.modeIndex = FindExactConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true); // If nothing found, try to find a nearly matching mode including interlaced - if (CORE.Window.modeIndex < 0) CORE.Window.modeIndex = FindNearestConnectorMode(CORE.Window.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true); + if (platform.modeIndex < 0) platform.modeIndex = FindNearestConnectorMode(platform.connector, CORE.Window.screen.width, CORE.Window.screen.height, fps, true); // If nothing found, there is no suitable mode - if (CORE.Window.modeIndex < 0) + if (platform.modeIndex < 0) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode"); drmModeFreeEncoder(enc); @@ -1007,13 +1087,13 @@ static bool InitGraphicsDevice(int width, int height) return false; } - CORE.Window.display.width = CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay; - CORE.Window.display.height = CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay; + CORE.Window.display.width = platform.connector->modes[platform.modeIndex].hdisplay; + CORE.Window.display.height = platform.connector->modes[platform.modeIndex].vdisplay; - TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u)", CORE.Window.connector->modes[CORE.Window.modeIndex].name, - CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, - (CORE.Window.connector->modes[CORE.Window.modeIndex].flags & DRM_MODE_FLAG_INTERLACE)? 'i' : 'p', - CORE.Window.connector->modes[CORE.Window.modeIndex].vrefresh); + TRACELOG(LOG_INFO, "DISPLAY: Selected DRM connector mode %s (%ux%u%c@%u)", platform.connector->modes[platform.modeIndex].name, + platform.connector->modes[platform.modeIndex].hdisplay, platform.connector->modes[platform.modeIndex].vdisplay, + (platform.connector->modes[platform.modeIndex].flags & DRM_MODE_FLAG_INTERLACE)? 'i' : 'p', + platform.connector->modes[platform.modeIndex].vrefresh); // Use the width and height of the surface for render CORE.Window.render.width = CORE.Window.screen.width; @@ -1025,16 +1105,16 @@ static bool InitGraphicsDevice(int width, int height) drmModeFreeResources(res); res = NULL; - CORE.Window.gbmDevice = gbm_create_device(CORE.Window.fd); - if (!CORE.Window.gbmDevice) + platform.gbmDevice = gbm_create_device(platform.fd); + if (!platform.gbmDevice) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM device"); return false; } - CORE.Window.gbmSurface = gbm_surface_create(CORE.Window.gbmDevice, CORE.Window.connector->modes[CORE.Window.modeIndex].hdisplay, - CORE.Window.connector->modes[CORE.Window.modeIndex].vdisplay, GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); - if (!CORE.Window.gbmSurface) + platform.gbmSurface = gbm_surface_create(platform.gbmDevice, platform.connector->modes[platform.modeIndex].hdisplay, + platform.connector->modes[platform.modeIndex].vdisplay, GBM_FORMAT_ARGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); + if (!platform.gbmSurface) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM surface"); return false; @@ -1073,22 +1153,22 @@ static bool InitGraphicsDevice(int width, int height) EGLint numConfigs = 0; // Get an EGL device connection - CORE.Window.device = eglGetDisplay((EGLNativeDisplayType)CORE.Window.gbmDevice); - if (CORE.Window.device == EGL_NO_DISPLAY) + platform.device = eglGetDisplay((EGLNativeDisplayType)platform.gbmDevice); + if (platform.device == EGL_NO_DISPLAY) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); return false; } // Initialize the EGL device connection - if (eglInitialize(CORE.Window.device, NULL, NULL) == EGL_FALSE) + if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE) { // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred. TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); return false; } - if (!eglChooseConfig(CORE.Window.device, NULL, NULL, 0, &numConfigs)) + if (!eglChooseConfig(platform.device, NULL, NULL, 0, &numConfigs)) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError()); return false; @@ -1104,7 +1184,7 @@ static bool InitGraphicsDevice(int width, int height) } EGLint matchingNumConfigs = 0; - if (!eglChooseConfig(CORE.Window.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs)) + if (!eglChooseConfig(platform.device, framebufferAttribs, configs, numConfigs, &matchingNumConfigs)) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError()); free(configs); @@ -1118,7 +1198,7 @@ static bool InitGraphicsDevice(int width, int height) for (EGLint i = 0; i < matchingNumConfigs; ++i) { EGLint id = 0; - if (!eglGetConfigAttrib(CORE.Window.device, configs[i], EGL_NATIVE_VISUAL_ID, &id)) + if (!eglGetConfigAttrib(platform.device, configs[i], EGL_NATIVE_VISUAL_ID, &id)) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config attribute: 0x%x", eglGetError()); continue; @@ -1127,7 +1207,7 @@ static bool InitGraphicsDevice(int width, int height) if (GBM_FORMAT_ARGB8888 == id) { TRACELOG(LOG_TRACE, "DISPLAY: Using EGL config: %d", i); - CORE.Window.config = configs[i]; + platform.config = configs[i]; found = 1; break; } @@ -1145,8 +1225,8 @@ static bool InitGraphicsDevice(int width, int height) eglBindAPI(EGL_OPENGL_ES_API); // Create an EGL rendering context - CORE.Window.context = eglCreateContext(CORE.Window.device, CORE.Window.config, EGL_NO_CONTEXT, contextAttribs); - if (CORE.Window.context == EGL_NO_CONTEXT) + platform.context = eglCreateContext(platform.device, platform.config, EGL_NO_CONTEXT, contextAttribs); + if (platform.context == EGL_NO_CONTEXT) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); return false; @@ -1154,8 +1234,8 @@ static bool InitGraphicsDevice(int width, int height) // Create an EGL window surface //--------------------------------------------------------------------------------- - CORE.Window.surface = eglCreateWindowSurface(CORE.Window.device, CORE.Window.config, (EGLNativeWindowType)CORE.Window.gbmSurface, NULL); - if (EGL_NO_SURFACE == CORE.Window.surface) + platform.surface = eglCreateWindowSurface(platform.device, platform.config, (EGLNativeWindowType)platform.gbmSurface, NULL); + if (EGL_NO_SURFACE == platform.surface) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL window surface: 0x%04x", eglGetError()); return false; @@ -1169,9 +1249,9 @@ static bool InitGraphicsDevice(int width, int height) SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); // There must be at least one frame displayed before the buffers are swapped - //eglSwapInterval(CORE.Window.device, 1); + //eglSwapInterval(platform.device, 1); - if (eglMakeCurrent(CORE.Window.device, CORE.Window.surface, CORE.Window.surface, CORE.Window.context) == EGL_FALSE) + if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); return false; @@ -1214,11 +1294,11 @@ static void InitKeyboard(void) // Reading directly from stdin will give chars already key-mapped by kernel to ASCII or UNICODE // Save terminal keyboard settings - tcgetattr(STDIN_FILENO, &CORE.Input.Keyboard.defaultSettings); + tcgetattr(STDIN_FILENO, &platform.defaultSettings); // Reconfigure terminal with new settings struct termios keyboardNewSettings = { 0 }; - keyboardNewSettings = CORE.Input.Keyboard.defaultSettings; + keyboardNewSettings = platform.defaultSettings; // New terminal settings for keyboard: turn off buffering (non-canonical mode), echo and key processing // NOTE: ISIG controls if ^C and ^Z generate break signals or not @@ -1231,11 +1311,11 @@ static void InitKeyboard(void) tcsetattr(STDIN_FILENO, TCSANOW, &keyboardNewSettings); // Save old keyboard mode to restore it at the end - CORE.Input.Keyboard.defaultFileFlags = fcntl(STDIN_FILENO, F_GETFL, 0); // F_GETFL: Get the file access mode and the file status flags - fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags | O_NONBLOCK); // F_SETFL: Set the file status flags to the value specified + platform.defaultFileFlags = fcntl(STDIN_FILENO, F_GETFL, 0); // F_GETFL: Get the file access mode and the file status flags + fcntl(STDIN_FILENO, F_SETFL, platform.defaultFileFlags | O_NONBLOCK); // F_SETFL: Set the file status flags to the value specified // NOTE: If ioctl() returns -1, it means the call failed for some reason (error code set in errno) - int result = ioctl(STDIN_FILENO, KDGKBMODE, &CORE.Input.Keyboard.defaultMode); + int result = ioctl(STDIN_FILENO, KDGKBMODE, &platform.defaultKeyboardMode); // In case of failure, it could mean a remote keyboard is used (SSH) if (result < 0) TRACELOG(LOG_WARNING, "RPI: Failed to change keyboard mode, an SSH keyboard is probably used"); @@ -1257,11 +1337,11 @@ static void InitKeyboard(void) static void RestoreKeyboard(void) { // Reset to default keyboard settings - tcsetattr(STDIN_FILENO, TCSANOW, &CORE.Input.Keyboard.defaultSettings); + tcsetattr(STDIN_FILENO, TCSANOW, &platform.defaultSettings); // Reconfigure keyboard to default mode - fcntl(STDIN_FILENO, F_SETFL, CORE.Input.Keyboard.defaultFileFlags); - ioctl(STDIN_FILENO, KDSKBMODE, CORE.Input.Keyboard.defaultMode); + fcntl(STDIN_FILENO, F_SETFL, platform.defaultFileFlags); + ioctl(STDIN_FILENO, KDSKBMODE, platform.defaultKeyboardMode); } #if defined(SUPPORT_SSH_KEYBOARD_RPI) @@ -1389,7 +1469,7 @@ static void InitEvdevInput(void) struct dirent *entity = NULL; // Initialise keyboard file descriptor - CORE.Input.Keyboard.fd = -1; + platform.keyboardFd = -1; // Reset variables for (int i = 0; i < MAX_TOUCH_POINTS; ++i) @@ -1451,9 +1531,9 @@ static void ConfigureEvdevDevice(char *device) // Open the device and allocate worker //------------------------------------------------------------------------------------------------------- // Find a free spot in the workers array - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i) { - if (CORE.Input.eventWorker[i].threadId == 0) + if (platform.eventWorker[i].threadId == 0) { freeWorkerId = i; break; @@ -1463,7 +1543,7 @@ static void ConfigureEvdevDevice(char *device) // Select the free worker from array if (freeWorkerId >= 0) { - worker = &(CORE.Input.eventWorker[freeWorkerId]); // Grab a pointer to the worker + worker = &(platform.eventWorker[freeWorkerId]); // Grab a pointer to the worker memset(worker, 0, sizeof(InputEventWorker)); // Clear the worker } else @@ -1574,13 +1654,13 @@ static void ConfigureEvdevDevice(char *device) // Decide what to do with the device //------------------------------------------------------------------------------------------------------- - if (worker->isKeyboard && (CORE.Input.Keyboard.fd == -1)) + if (worker->isKeyboard && (platform.keyboardFd == -1)) { // Use the first keyboard encountered. This assumes that a device that says it's a keyboard is just a // keyboard. The keyboard is polled synchronously, whereas other input devices are polled in separate // threads so that they don't drop events when the frame rate is slow. TRACELOG(LOG_INFO, "RPI: Opening keyboard device: %s", device); - CORE.Input.Keyboard.fd = worker->fd; + platform.keyboardFd = worker->fd; } else if (worker->isTouch || worker->isMouse) { @@ -1604,21 +1684,21 @@ static void ConfigureEvdevDevice(char *device) // Find touchscreen with the highest index int maxTouchNumber = -1; - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i) { - if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum > maxTouchNumber)) maxTouchNumber = CORE.Input.eventWorker[i].eventNum; + if (platform.eventWorker[i].isTouch && (platform.eventWorker[i].eventNum > maxTouchNumber)) maxTouchNumber = platform.eventWorker[i].eventNum; } // Find touchscreens with lower indexes - for (int i = 0; i < sizeof(CORE.Input.eventWorker)/sizeof(InputEventWorker); ++i) + for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i) { - if (CORE.Input.eventWorker[i].isTouch && (CORE.Input.eventWorker[i].eventNum < maxTouchNumber)) + if (platform.eventWorker[i].isTouch && (platform.eventWorker[i].eventNum < maxTouchNumber)) { - if (CORE.Input.eventWorker[i].threadId != 0) + if (platform.eventWorker[i].threadId != 0) { TRACELOG(LOG_WARNING, "RPI: Found duplicate touchscreen, killing touchscreen on event: %d", i); - pthread_cancel(CORE.Input.eventWorker[i].threadId); - close(CORE.Input.eventWorker[i].fd); + pthread_cancel(platform.eventWorker[i].threadId); + close(platform.eventWorker[i].fd); } } } @@ -1652,7 +1732,7 @@ static void PollKeyboardEvents(void) 243, 244, 245, 246, 247, 248, 0, 0, 0, 0, 0, 0, 0 }; - int fd = CORE.Input.Keyboard.fd; + int fd = platform.keyboardFd; if (fd == -1) return; struct input_event event = { 0 }; @@ -1666,7 +1746,7 @@ static void PollKeyboardEvents(void) { #if defined(SUPPORT_SSH_KEYBOARD_RPI) // Change keyboard mode to events - CORE.Input.Keyboard.evtMode = true; + platform.eventKeyboardMode = true; #endif // Keyboard button parsing if ((event.code >= 1) && (event.code <= 255)) //Keyboard keys appear for codes 1 to 255 @@ -1739,7 +1819,7 @@ static void *EventThread(void *arg) gestureUpdate = true; } - if (event.code == REL_WHEEL) CORE.Input.Mouse.eventWheelMove.y += event.value; + if (event.code == REL_WHEEL) platform.eventWheelMove.y += event.value; } // Absolute movement parsing @@ -1790,11 +1870,11 @@ static void *EventThread(void *arg) // Touchscreen tap if (event.code == ABS_PRESSURE) { - int previousMouseLeftButtonState = CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT]; + int previousMouseLeftButtonState = platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT]; if (!event.value && previousMouseLeftButtonState) { - CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0; + platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 0; touchAction = 0; // TOUCH_ACTION_UP gestureUpdate = true; @@ -1802,7 +1882,7 @@ static void *EventThread(void *arg) if (event.value && !previousMouseLeftButtonState) { - CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1; + platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = 1; touchAction = 1; // TOUCH_ACTION_DOWN gestureUpdate = true; @@ -1817,19 +1897,19 @@ static void *EventThread(void *arg) // Mouse button parsing if ((event.code == BTN_TOUCH) || (event.code == BTN_LEFT)) { - CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value; + platform.currentButtonStateEvdev[MOUSE_BUTTON_LEFT] = event.value; if (event.value > 0) touchAction = 1; // TOUCH_ACTION_DOWN else touchAction = 0; // TOUCH_ACTION_UP gestureUpdate = true; } - if (event.code == BTN_RIGHT) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value; - if (event.code == BTN_MIDDLE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_MIDDLE] = event.value; - if (event.code == BTN_SIDE) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_SIDE] = event.value; - if (event.code == BTN_EXTRA) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_EXTRA] = event.value; - if (event.code == BTN_FORWARD) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_FORWARD] = event.value; - if (event.code == BTN_BACK) CORE.Input.Mouse.currentButtonStateEvdev[MOUSE_BUTTON_BACK] = event.value; + if (event.code == BTN_RIGHT) platform.currentButtonStateEvdev[MOUSE_BUTTON_RIGHT] = event.value; + if (event.code == BTN_MIDDLE) platform.currentButtonStateEvdev[MOUSE_BUTTON_MIDDLE] = event.value; + if (event.code == BTN_SIDE) platform.currentButtonStateEvdev[MOUSE_BUTTON_SIDE] = event.value; + if (event.code == BTN_EXTRA) platform.currentButtonStateEvdev[MOUSE_BUTTON_EXTRA] = event.value; + if (event.code == BTN_FORWARD) platform.currentButtonStateEvdev[MOUSE_BUTTON_FORWARD] = event.value; + if (event.code == BTN_BACK) platform.currentButtonStateEvdev[MOUSE_BUTTON_BACK] = event.value; } // Screen confinement @@ -1885,7 +1965,7 @@ static void InitGamepad(void) { sprintf(gamepadDev, "%s%i", DEFAULT_GAMEPAD_DEV, i); - if ((CORE.Input.Gamepad.streamId[i] = open(gamepadDev, O_RDONLY | O_NONBLOCK)) < 0) + if ((platform.gamepadStreamFd[i] = open(gamepadDev, O_RDONLY | O_NONBLOCK)) < 0) { // NOTE: Only show message for first gamepad if (i == 0) TRACELOG(LOG_WARNING, "RPI: Failed to open Gamepad device, no gamepad available"); @@ -1897,7 +1977,7 @@ static void InitGamepad(void) // NOTE: Only create one thread if (i == 0) { - int error = pthread_create(&CORE.Input.Gamepad.threadId, NULL, &GamepadThread, NULL); + int error = pthread_create(&platform.gamepadThreadId, NULL, &GamepadThread, NULL); if (error != 0) TRACELOG(LOG_WARNING, "RPI: Failed to create gamepad input event thread"); else TRACELOG(LOG_INFO, "RPI: Gamepad device initialized successfully"); @@ -1927,7 +2007,7 @@ static void *GamepadThread(void *arg) { for (int i = 0; i < MAX_GAMEPADS; i++) { - if (read(CORE.Input.Gamepad.streamId[i], &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event)) + if (read(platform.gamepadStreamFd[i], &gamepadEvent, sizeof(struct js_event)) == (int)sizeof(struct js_event)) { gamepadEvent.type &= ~JS_EVENT_INIT; // Ignore synthetic events @@ -1977,7 +2057,7 @@ static int FindMatchingConnectorMode(const drmModeConnector *connector, const dr TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, connector->modes[i].hdisplay, connector->modes[i].vdisplay, connector->modes[i].vrefresh, (connector->modes[i].flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive"); - if (0 == BINCMP(&CORE.Window.crtc->mode, &CORE.Window.connector->modes[i])) return i; + if (0 == BINCMP(&platform.crtc->mode, &platform.connector->modes[i])) return i; } return -1; @@ -1992,9 +2072,9 @@ static int FindExactConnectorMode(const drmModeConnector *connector, uint width, if (NULL == connector) return -1; - for (int i = 0; i < CORE.Window.connector->count_modes; i++) + for (int i = 0; i < platform.connector->count_modes; i++) { - const drmModeModeInfo *const mode = &CORE.Window.connector->modes[i]; + const drmModeModeInfo *const mode = &platform.connector->modes[i]; TRACELOG(LOG_TRACE, "DISPLAY: DRM Mode %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive"); @@ -2015,9 +2095,9 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt if (NULL == connector) return -1; int nearestIndex = -1; - for (int i = 0; i < CORE.Window.connector->count_modes; i++) + for (int i = 0; i < platform.connector->count_modes; i++) { - const drmModeModeInfo *const mode = &CORE.Window.connector->modes[i]; + const drmModeModeInfo *const mode = &platform.connector->modes[i]; TRACELOG(LOG_TRACE, "DISPLAY: DRM mode: %d %ux%u@%u %s", i, mode->hdisplay, mode->vdisplay, mode->vrefresh, (mode->flags & DRM_MODE_FLAG_INTERLACE)? "interlaced" : "progressive"); @@ -2044,9 +2124,9 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt const int heightDiff = abs(mode->vdisplay - height); const int fpsDiff = abs(mode->vrefresh - fps); - const int nearestWidthDiff = abs(CORE.Window.connector->modes[nearestIndex].hdisplay - width); - const int nearestHeightDiff = abs(CORE.Window.connector->modes[nearestIndex].vdisplay - height); - const int nearestFpsDiff = abs(CORE.Window.connector->modes[nearestIndex].vrefresh - fps); + const int nearestWidthDiff = abs(platform.connector->modes[nearestIndex].hdisplay - width); + const int nearestHeightDiff = abs(platform.connector->modes[nearestIndex].vdisplay - height); + const int nearestFpsDiff = abs(platform.connector->modes[nearestIndex].vrefresh - fps); if ((widthDiff < nearestWidthDiff) || (heightDiff < nearestHeightDiff) || (fpsDiff < nearestFpsDiff)) { nearestIndex = i; diff --git a/src/rcore_web.c b/src/rcore_web.c index baafd60d6..fa296b90b 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -47,23 +47,42 @@ #include "rcore.h" -#define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) -// #define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?) -#include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management +#define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) +// #define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?) +#include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management + +#include // Emscripten functionality for C +#include // Emscripten HTML5 library + #include // Required for: timespec, nanosleep(), select() - POSIX -#include // Emscripten functionality for C -#include // Emscripten HTML5 library +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// TODO: HACK: Added flag if not provided by GLFW when using external library +// Latest GLFW release (GLFW 3.3.8) does not implement this flag, it was added for 3.4.0-dev +#if !defined(GLFW_MOUSE_PASSTHROUGH) + #define GLFW_MOUSE_PASSTHROUGH 0x0002000D +#endif + +#if (_POSIX_C_SOURCE < 199309L) + #undef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext. +#endif //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- -//... +typedef struct { + GLFWwindow *handle; // GLFW window handle (graphic device) +} PlatformData; //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -extern CoreData CORE; // Global CORE state context +extern CoreData CORE; // Global CORE state context + +static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration @@ -263,7 +282,7 @@ void CloseWindow(void) rlglClose(); // De-init rlgl - glfwDestroyWindow(CORE.Window.handle); + glfwDestroyWindow(platform.handle); glfwTerminate(); #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) @@ -481,7 +500,7 @@ void SetWindowMaxSize(int width, int height) // Set window dimensions void SetWindowSize(int width, int height) { - glfwSetWindowSize(CORE.Window.handle, width, height); + glfwSetWindowSize(platform.handle, width, height); } // Set window opacity, value opacity is between 0.0 and 1.0 @@ -759,7 +778,7 @@ void SetMousePosition(int x, int y) CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; // NOTE: emscripten not implemented - glfwSetCursorPos(CORE.Window.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); + glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); } // Get mouse wheel movement Y @@ -806,7 +825,7 @@ Vector2 GetTouchPosition(int index) // Swap back buffer with front buffer (screen drawing) void SwapScreenBuffer(void) { - glfwSwapBuffers(CORE.Window.handle); + glfwSwapBuffers(platform.handle); } // Register all input events @@ -1110,24 +1129,24 @@ static bool InitGraphicsDevice(int width, int height) // HighDPI monitors are properly considered in a following similar function: SetupViewport() SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); - CORE.Window.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL); + platform.handle = glfwCreateWindow(CORE.Window.display.width, CORE.Window.display.height, (CORE.Window.title != 0)? CORE.Window.title : " ", glfwGetPrimaryMonitor(), NULL); // NOTE: Full-screen change, not working properly... - // glfwSetWindowMonitor(CORE.Window.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); + // glfwSetWindowMonitor(platform.handle, glfwGetPrimaryMonitor(), 0, 0, CORE.Window.screen.width, CORE.Window.screen.height, GLFW_DONT_CARE); } else { // No-fullscreen window creation - CORE.Window.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); + platform.handle = glfwCreateWindow(CORE.Window.screen.width, CORE.Window.screen.height, (CORE.Window.title != 0)? CORE.Window.title : " ", NULL, NULL); - if (CORE.Window.handle) + if (platform.handle) { CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; } } - if (!CORE.Window.handle) + if (!platform.handle) { glfwTerminate(); TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); @@ -1138,20 +1157,20 @@ static bool InitGraphicsDevice(int width, int height) emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " "); // Set window callback events - glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! - glfwSetWindowIconifyCallback(CORE.Window.handle, WindowIconifyCallback); - glfwSetWindowFocusCallback(CORE.Window.handle, WindowFocusCallback); - glfwSetDropCallback(CORE.Window.handle, WindowDropCallback); + glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! + glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback); + glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback); + glfwSetDropCallback(platform.handle, WindowDropCallback); // Set input callback events - glfwSetKeyCallback(CORE.Window.handle, KeyCallback); - glfwSetCharCallback(CORE.Window.handle, CharCallback); - glfwSetMouseButtonCallback(CORE.Window.handle, MouseButtonCallback); - glfwSetCursorPosCallback(CORE.Window.handle, MouseCursorPosCallback); // Track mouse position changes - glfwSetScrollCallback(CORE.Window.handle, MouseScrollCallback); - glfwSetCursorEnterCallback(CORE.Window.handle, CursorEnterCallback); + glfwSetKeyCallback(platform.handle, KeyCallback); + glfwSetCharCallback(platform.handle, CharCallback); + glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback); + glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes + glfwSetScrollCallback(platform.handle, MouseScrollCallback); + glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); - glfwMakeContextCurrent(CORE.Window.handle); + glfwMakeContextCurrent(platform.handle); // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need @@ -1293,7 +1312,7 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i } // Check the exit key to set close window - if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(CORE.Window.handle, GLFW_TRUE); + if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE); #if defined(SUPPORT_SCREEN_CAPTURE) if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) diff --git a/src/utils.h b/src/utils.h index ff8246a7b..6ec0f7f1b 100644 --- a/src/utils.h +++ b/src/utils.h @@ -32,7 +32,7 @@ #include // Required for: AAssetManager #endif -#if defined(SUPPORT_TRACELOG) +#if defined(SUPPORT_TRACELOG) && !defined(TRACELOG) #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) #if defined(SUPPORT_TRACELOG_DEBUG) From df8d3a5afb6bec908ad0e720a1cbb2b444cb951d Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 00:47:22 +0200 Subject: [PATCH 0317/1350] REVIEWED: Some warnings #3313 --- src/rcore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index f28aff74f..85a84f590 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1413,10 +1413,10 @@ const char *GetDirectoryPath(const char *filePath) else { // NOTE: Be careful, strncpy() is not safe, it does not care about '\0' - unsigned char *dirPathPtr = dirPath; + char *dirPathPtr = dirPath; if ((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/')) dirPathPtr += 2; // Skip drive letter, "C:" memcpy(dirPathPtr, filePath, strlen(filePath) - (strlen(lastSlash) - 1)); - dirPath[strlen(filePath) - strlen(lastSlash) + ((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/'))? 2 : 0] = '\0'; // Add '\0' manually + dirPath[strlen(filePath) - strlen(lastSlash) + (((filePath[1] != ':') && (filePath[0] != '\\') && (filePath[0] != '/'))? 2 : 0)] = '\0'; // Add '\0' manually } } From a0b30b0363cfbadb359db87ffd8fa80748485980 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 01:02:19 +0200 Subject: [PATCH 0318/1350] REVIEWED: `SetupViewport()` macOS #3313 --- src/rcore.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 85a84f590..3a8820e38 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2162,9 +2162,10 @@ void SetupViewport(int width, int height) // NOTE: We consider render size (scaled) and offset in case black bars are required and // render area does not match full display area (this situation is only applicable on fullscreen mode) #if defined(__APPLE__) - float xScale = 1.0f, yScale = 1.0f; - glfwGetWindowContentScale(CORE.Window.handle, &xScale, &yScale); - rlViewport(CORE.Window.renderOffset.x/2*xScale, CORE.Window.renderOffset.y/2*yScale, (CORE.Window.render.width)*xScale, (CORE.Window.render.height)*yScale); + //float xScale = 1.0f, yScale = 1.0f; + //glfwGetWindowContentScale(CORE.Window.handle, &xScale, &yScale); + Vector2 scale = GetWindowScaleDPI(); + rlViewport(CORE.Window.renderOffset.x/2*scale.x, CORE.Window.renderOffset.y/2*scale.y, (CORE.Window.render.width)*scale.x, (CORE.Window.render.height)*scale.y); #else rlViewport(CORE.Window.renderOffset.x/2, CORE.Window.renderOffset.y/2, CORE.Window.render.width, CORE.Window.render.height); #endif From 5ed7717f0d9e524b72f1bddc6fcd3e2e5f32e217 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 01:21:46 +0200 Subject: [PATCH 0319/1350] REVIEWED: `WaitTime()`, added validation #3377 --- src/rcore.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rcore.c b/src/rcore.c index 3a8820e38..69b5e1a83 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2290,6 +2290,8 @@ void InitTimer(void) // Ref: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timing on Win32! void WaitTime(double seconds) { + if (seconds < 0) return; + #if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) double destinationTime = GetTime() + seconds; #endif From 682992e868ffd97fa25d432e15ec209a161c26d9 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 09:45:57 +0200 Subject: [PATCH 0320/1350] REVIEWED: Reorganize functions `TakeScreenshot()` moved to `rcore.c` --- src/rcore.c | 23 ++++++++++++ src/rcore_android.c | 44 ++++++++-------------- src/rcore_desktop.c | 42 ++++++--------------- src/rcore_drm.c | 90 ++++++++++++++++++--------------------------- src/rcore_web.c | 46 ++++++----------------- 5 files changed, 97 insertions(+), 148 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 69b5e1a83..a3075aac5 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2329,6 +2329,29 @@ void WaitTime(double seconds) #endif } +// Takes a screenshot of current screen (saved a .png) +void TakeScreenshot(const char *fileName) +{ +#if defined(SUPPORT_MODULE_RTEXTURES) + // Security check to (partially) avoid malicious code + if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } + + Vector2 scale = GetWindowScaleDPI(); + unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); + Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + + char path[512] = { 0 }; + strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); + + ExportImage(image, path); // WARNING: Module required: rtextures + RL_FREE(imgData); + + TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); +#else + TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); +#endif +} + // Scan all files and directories in a base path // WARNING: files.paths[] must be previously allocated and // contain enough space to store all required paths diff --git a/src/rcore_android.c b/src/rcore_android.c index ad7a905c1..a7c8f3307 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -133,6 +133,10 @@ struct android_app *GetAndroidApp(void) return platform.app; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Window and Graphics Device +//---------------------------------------------------------------------------------- + // Initialize window and OpenGL context // NOTE: data parameter could be used to pass any kind of required data to the initialization void InitWindow(int width, int height, const char *title) @@ -563,6 +567,16 @@ void DisableCursor(void) CORE.Input.Mouse.cursorHidden = true; } +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + eglSwapBuffers(platform.device, platform.surface); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + // Get elapsed time measure in seconds since InitTimer() double GetTime(void) { @@ -576,29 +590,6 @@ double GetTime(void) return time; } -// Takes a screenshot of current screen (saved a .png) -void TakeScreenshot(const char *fileName) -{ -#if defined(SUPPORT_MODULE_RTEXTURES) - // Security check to (partially) avoid malicious code on PLATFORM_ANDROID - if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } - - Vector2 scale = GetWindowScaleDPI(); - unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); - Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - - char path[2048] = { 0 }; - strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); - - ExportImage(image, path); // WARNING: Module required: rtextures - RL_FREE(imgData); - - TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); -#else - TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); -#endif -} - // Open URL with default system browser (if available) // NOTE: This function is only safe to use if you control the URL given. // A user could craft a malicious string performing another action. @@ -726,12 +717,6 @@ Vector2 GetTouchPosition(int index) return position; } -// Swap back buffer with front buffer (screen drawing) -void SwapScreenBuffer(void) -{ - eglSwapBuffers(platform.device, platform.surface); -} - // Register all input events void PollInputEvents(void) { @@ -787,6 +772,7 @@ void PollInputEvents(void) } } + //---------------------------------------------------------------------------------- // Module Internal Functions Definition //---------------------------------------------------------------------------------- diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 33a4aab0e..dcd71ef18 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -137,7 +137,7 @@ static void CursorEnterCallback(GLFWwindow *window, int enter); // NOTE: Functions declaration is provided by raylib.h //---------------------------------------------------------------------------------- -// Module Functions Definition +// Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- // Initialize window and OpenGL context @@ -1168,6 +1168,16 @@ void DisableCursor(void) CORE.Input.Mouse.cursorHidden = true; } +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + glfwSwapBuffers(platform.handle); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + // Get elapsed time measure in seconds since InitTimer() double GetTime(void) { @@ -1175,30 +1185,6 @@ double GetTime(void) return time; } -// Takes a screenshot of current screen (saved a .png) -// WARNING: This function requires [rtextures] module functionality -void TakeScreenshot(const char *fileName) -{ -#if defined(SUPPORT_MODULE_RTEXTURES) - // Security check to (partially) avoid malicious code - if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } - - Vector2 scale = GetWindowScaleDPI(); - unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); - Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - - char path[2048] = { 0 }; - strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); - - ExportImage(image, path); // WARNING: Module required: rtextures - RL_FREE(imgData); - - TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); -#else - TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); -#endif -} - // Open URL with default system browser (if available) // NOTE: This function is only safe to use if you control the URL given. // A user could craft a malicious string performing another action. @@ -1341,12 +1327,6 @@ Vector2 GetTouchPosition(int index) return position; } -// Swap back buffer with front buffer (screen drawing) -void SwapScreenBuffer(void) -{ - glfwSwapBuffers(platform.handle); -} - // Register all input events void PollInputEvents(void) { diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 8c63b63da..aadf0f475 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -166,7 +166,7 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt // NOTE: Functions declaration is provided by raylib.h //---------------------------------------------------------------------------------- -// Module Functions Definition +// Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- // Initialize window and OpenGL context @@ -675,6 +675,40 @@ void DisableCursor(void) CORE.Input.Mouse.cursorHidden = true; } +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + eglSwapBuffers(platform.device, platform.surface); + + if (!platform.gbmSurface || (-1 == platform.fd) || !platform.connector || !platform.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); + + struct gbm_bo *bo = gbm_surface_lock_front_buffer(platform.gbmSurface); + if (!bo) TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer"); + + uint32_t fb = 0; + int result = drmModeAddFB(platform.fd, platform.connector->modes[platform.modeIndex].hdisplay, platform.connector->modes[platform.modeIndex].vdisplay, 24, 32, gbm_bo_get_stride(bo), gbm_bo_get_handle(bo).u32, &fb); + if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d", result); + + result = drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fb, 0, 0, &platform.connector->connector_id, 1, &platform.connector->modes[platform.modeIndex]); + if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result); + + if (platform.prevFB) + { + result = drmModeRmFB(platform.fd, platform.prevFB); + if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result); + } + + platform.prevFB = fb; + + if (platform.prevBO) gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO); + + platform.prevBO = bo; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + // Get elapsed time measure in seconds since InitTimer() double GetTime(void) { @@ -688,29 +722,6 @@ double GetTime(void) return time; } -// Takes a screenshot of current screen (saved a .png) -void TakeScreenshot(const char *fileName) -{ -#if defined(SUPPORT_MODULE_RTEXTURES) - // Security check to (partially) avoid malicious code on PLATFORM_WEB - if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } - - Vector2 scale = GetWindowScaleDPI(); - unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); - Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - - char path[2048] = { 0 }; - strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); - - ExportImage(image, path); // WARNING: Module required: rtextures - RL_FREE(imgData); - - TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); -#else - TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); -#endif -} - // Open URL with default system browser (if available) // NOTE: This function is only safe to use if you control the URL given. // A user could craft a malicious string performing another action. @@ -833,36 +844,6 @@ Vector2 GetTouchPosition(int index) return position; } -// Swap back buffer with front buffer (screen drawing) -void SwapScreenBuffer(void) -{ - eglSwapBuffers(platform.device, platform.surface); - - if (!platform.gbmSurface || (-1 == platform.fd) || !platform.connector || !platform.crtc) TRACELOG(LOG_ERROR, "DISPLAY: DRM initialization failed to swap"); - - struct gbm_bo *bo = gbm_surface_lock_front_buffer(platform.gbmSurface); - if (!bo) TRACELOG(LOG_ERROR, "DISPLAY: Failed GBM to lock front buffer"); - - uint32_t fb = 0; - int result = drmModeAddFB(platform.fd, platform.connector->modes[platform.modeIndex].hdisplay, platform.connector->modes[platform.modeIndex].vdisplay, 24, 32, gbm_bo_get_stride(bo), gbm_bo_get_handle(bo).u32, &fb); - if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeAddFB() failed with result: %d", result); - - result = drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, fb, 0, 0, &platform.connector->connector_id, 1, &platform.connector->modes[platform.modeIndex]); - if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeSetCrtc() failed with result: %d", result); - - if (platform.prevFB) - { - result = drmModeRmFB(platform.fd, platform.prevFB); - if (result != 0) TRACELOG(LOG_ERROR, "DISPLAY: drmModeRmFB() failed with result: %d", result); - } - - platform.prevFB = fb; - - if (platform.prevBO) gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO); - - platform.prevBO = bo; -} - // Register all input events void PollInputEvents(void) { @@ -931,6 +912,7 @@ void PollInputEvents(void) #endif } + //---------------------------------------------------------------------------------- // Module Internal Functions Definition //---------------------------------------------------------------------------------- diff --git a/src/rcore_web.c b/src/rcore_web.c index fa296b90b..249f0db4c 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -122,7 +122,7 @@ static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadE // NOTE: Functions declaration is provided by raylib.h //---------------------------------------------------------------------------------- -// Module Functions Definition +// Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- // Initialize window and OpenGL context @@ -664,6 +664,16 @@ void DisableCursor(void) CORE.Input.Mouse.cursorHidden = true; } +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + glfwSwapBuffers(platform.handle); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + // Get elapsed time measure in seconds since InitTimer() double GetTime(void) { @@ -671,33 +681,6 @@ double GetTime(void) return time; } -// Takes a screenshot of current screen (saved a .png) -void TakeScreenshot(const char *fileName) -{ -#if defined(SUPPORT_MODULE_RTEXTURES) - // Security check to (partially) avoid malicious code on PLATFORM_WEB - if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } - - Vector2 scale = GetWindowScaleDPI(); - unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); - Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - - char path[2048] = { 0 }; - strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); - - ExportImage(image, path); // WARNING: Module required: rtextures - RL_FREE(imgData); - - // Download file from MEMFS (emscripten memory filesystem) - // saveFileFromMEMFSToDisk() function is defined in raylib/src/shell.html - emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", GetFileName(path), GetFileName(path))); - - TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); -#else - TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); -#endif -} - // Open URL with default system browser (if available) // NOTE: This function is only safe to use if you control the URL given. // A user could craft a malicious string performing another action. @@ -822,12 +805,6 @@ Vector2 GetTouchPosition(int index) return position; } -// Swap back buffer with front buffer (screen drawing) -void SwapScreenBuffer(void) -{ - glfwSwapBuffers(platform.handle); -} - // Register all input events void PollInputEvents(void) { @@ -944,6 +921,7 @@ void PollInputEvents(void) } } + //---------------------------------------------------------------------------------- // Module Internal Functions Definition //---------------------------------------------------------------------------------- From ea9de852bdc6b6867fdf2805e6c5caf476b61bde Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 09:46:57 +0200 Subject: [PATCH 0321/1350] ADDED: Custom platform template! #3313 --- src/rcore_custom.c | 789 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 789 insertions(+) create mode 100644 src/rcore_custom.c diff --git a/src/rcore_custom.c b/src/rcore_custom.c new file mode 100644 index 000000000..f20996268 --- /dev/null +++ b/src/rcore_custom.c @@ -0,0 +1,789 @@ +/********************************************************************************************** +* +* rcore_ - Functions to manage window, graphics device and inputs +* +* PLATFORM: +* - TODO: Define the target platform for the core +* +* LIMITATIONS: +* - Limitation 01 +* - Limitation 02 +* +* POSSIBLE IMPROVEMENTS: +* - Improvement 01 +* - Improvement 02 +* +* ADDITIONAL NOTES: +* - TRACELOG() function is located in raylib [utils] module +* +* CONFIGURATION: +* #define RCORE_CUSTOM_FLAG +* Custom flag for rcore on PLATFORM_ -not used- +* +* DEPENDENCIES: +* - Dependency 01 +* - Dependency 02 +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) and contributors +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#include "rcore.h" + +// TODO: Include the platform specific libraries + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef struct { + // TODO: Define the platform specific variables required + + // Display data + EGLDisplay device; // Native display device (physical screen connection) + EGLSurface surface; // Surface to draw on, framebuffers (connected to context) + EGLContext context; // Graphic context, mode in which drawing can be done + EGLConfig config; // Graphic config +} PlatformData; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; // Global CORE state context + +static PlatformData platform = { 0 }; // Platform specific data + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static bool InitGraphicsDevice(int width, int height); // Initialize graphics device + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +// NOTE: Functions declaration is provided by raylib.h + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Window and Graphics Device +//---------------------------------------------------------------------------------- + +// Initialize window and OpenGL context +// NOTE: data parameter could be used to pass any kind of required data to the initialization +void InitWindow(int width, int height, const char *title) +{ + TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); + + TRACELOG(LOG_INFO, "Supported raylib modules:"); + TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); + TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); +#if defined(SUPPORT_MODULE_RSHAPES) + TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXTURES) + TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXT) + TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RMODELS) + TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RAUDIO) + TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); +#endif + + // NOTE: Keep internal pointer to input title string (no copy) + if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; + + // Initialize global input state + memset(&CORE.Input, 0, sizeof(CORE.Input)); + CORE.Input.Keyboard.exitKey = KEY_ESCAPE; + CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; + CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Window.eventWaiting = false; + + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + CORE.Window.currentFbo.width = width; + CORE.Window.currentFbo.height = height; + + // TODO: Initialize window/display system + + // TODO: Initialize input events system + + // TODO: Initialize assets manager + + // TODO: Initialize base path for storage + //CORE.Storage.basePath = platform.app->activity->internalDataPath; + + TRACELOG(LOG_INFO, "PLATFORM: Application initialized successfully"); +} + +// Close window and unload OpenGL context +void CloseWindow(void) +{ +#if defined(SUPPORT_GIF_RECORDING) + if (gifRecording) + { + MsfGifResult result = msf_gif_end(&gifState); + msf_gif_free(result); + gifRecording = false; + } +#endif + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + UnloadFontDefault(); // WARNING: Module required: rtext +#endif + + rlglClose(); // De-init rlgl + +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + timeEndPeriod(1); // Restore time period +#endif + + // TODO: Close surface, context and display + +#if defined(SUPPORT_EVENTS_AUTOMATION) + RL_FREE(events); +#endif + + CORE.Window.ready = false; + TRACELOG(LOG_INFO, "Window closed successfully"); +} + +// Check if application should close +bool WindowShouldClose(void) +{ + if (CORE.Window.ready) return CORE.Window.shouldClose; + else return true; +} + +// Check if window is currently hidden +bool IsWindowHidden(void) +{ + return false; +} + +// Check if window has been minimized +bool IsWindowMinimized(void) +{ + return false; +} + +// Check if window has been maximized +bool IsWindowMaximized(void) +{ + return false; +} + +// Check if window has the focus +bool IsWindowFocused(void) +{ + return platform.appEnabled; +} + +// Check if window has been resizedLastFrame +bool IsWindowResized(void) +{ + return false; +} + +// Toggle fullscreen mode +void ToggleFullscreen(void) +{ + TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on PLATFORM_CUSTOM"); +} + +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_CUSTOM"); +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_CUSTOM"); +} + +// Set window state: not minimized/maximized +void RestoreWindow(void) +{ + TRACELOG(LOG_WARNING, "RestoreWindow() not available on PLATFORM_CUSTOM"); +} + +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_CUSTOM"); +} + +// Set window configuration state using flags +void SetWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_CUSTOM"); +} + +// Clear window configuration state flags +void ClearWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "ClearWindowState() not available on PLATFORM_CUSTOM"); +} + +// Set icon for window +void SetWindowIcon(Image image) +{ + TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_CUSTOM"); +} + +// Set icon for window +void SetWindowIcons(Image *images, int count) +{ + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on PLATFORM_CUSTOM"); +} + +// Set title for window +void SetWindowTitle(const char *title) +{ + CORE.Window.title = title; +} + +// Set window position on screen (windowed mode) +void SetWindowPosition(int x, int y) +{ + TRACELOG(LOG_WARNING, "SetWindowPosition() not available on PLATFORM_CUSTOM"); +} + +// Set monitor for the current window +void SetWindowMonitor(int monitor) +{ + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on PLATFORM_CUSTOM"); +} + +// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMinSize(int width, int height) +{ + CORE.Window.windowMin.width = width; + CORE.Window.windowMin.height = height; +} + +// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMaxSize(int width, int height) +{ + CORE.Window.windowMax.width = width; + CORE.Window.windowMax.height = height; +} + +// Set window dimensions +void SetWindowSize(int width, int height) +{ + TRACELOG(LOG_WARNING, "SetWindowSize() not available on PLATFORM_CUSTOM"); +} + +// Set window opacity, value opacity is between 0.0 and 1.0 +void SetWindowOpacity(float opacity) +{ + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on PLATFORM_CUSTOM"); +} + +// Set window focused +void SetWindowFocused(void) +{ + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_CUSTOM"); +} + +// Get native window handle +void *GetWindowHandle(void) +{ + TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_CUSTOM"); + return NULL; +} + +// Get number of monitors +int GetMonitorCount(void) +{ + TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on PLATFORM_CUSTOM"); + return 1; +} + +// Get number of monitors +int GetCurrentMonitor(void) +{ + TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on PLATFORM_CUSTOM"); + return 0; +} + +// Get selected monitor position +Vector2 GetMonitorPosition(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on PLATFORM_CUSTOM"); + return (Vector2){ 0, 0 }; +} + +// Get selected monitor width (currently used by monitor) +int GetMonitorWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on PLATFORM_CUSTOM"); + return 0; +} + +// Get selected monitor height (currently used by monitor) +int GetMonitorHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on PLATFORM_CUSTOM"); + return 0; +} + +// Get selected monitor physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on PLATFORM_CUSTOM"); + return 0; +} + +// Get selected monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on PLATFORM_CUSTOM"); + return 0; +} + +// Get selected monitor refresh rate +int GetMonitorRefreshRate(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on PLATFORM_CUSTOM"); + return 0; +} + +// Get the human-readable, UTF-8 encoded name of the selected monitor +const char *GetMonitorName(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on PLATFORM_CUSTOM"); + return ""; +} + +// Get window position XY on monitor +Vector2 GetWindowPosition(void) +{ + TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on PLATFORM_CUSTOM"); + return (Vector2){ 0, 0 }; +} + +// Get window scale DPI factor for current monitor +Vector2 GetWindowScaleDPI(void) +{ + TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on PLATFORM_CUSTOM"); + return (Vector2){ 1.0f, 1.0f }; +} + +// Set clipboard text content +void SetClipboardText(const char *text) +{ + TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on PLATFORM_CUSTOM"); +} + +// Get clipboard text content +// NOTE: returned string is allocated and freed by GLFW +const char *GetClipboardText(void) +{ + TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on PLATFORM_CUSTOM"); + return NULL; +} + +// Show mouse cursor +void ShowCursor(void) +{ + CORE.Input.Mouse.cursorHidden = false; +} + +// Hides mouse cursor +void HideCursor(void) +{ + CORE.Input.Mouse.cursorHidden = true; +} + +// Enables cursor (unlock cursor) +void EnableCursor(void) +{ + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + + CORE.Input.Mouse.cursorHidden = false; +} + +// Disables cursor (lock cursor) +void DisableCursor(void) +{ + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + + CORE.Input.Mouse.cursorHidden = true; +} + +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + eglSwapBuffers(platform.device, platform.surface); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + +// Get elapsed time measure in seconds since InitTimer() +double GetTime(void) +{ + double time = 0.0; + struct timespec ts = { 0 }; + clock_gettime(CLOCK_MONOTONIC, &ts); + unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; + + time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer() + + return time; +} + +// Open URL with default system browser (if available) +// NOTE: This function is only safe to use if you control the URL given. +// A user could craft a malicious string performing another action. +// Only call this function yourself not with user input or make sure to check the string yourself. +// Ref: https://github.com/raysan5/raylib/issues/686 +void OpenURL(const char *url) +{ + // Security check to (partially) avoid malicious code on PLATFORM_CUSTOM + if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); + else + { + JNIEnv *env = NULL; + JavaVM *vm = platform.app->activity->vm; + (*vm)->AttachCurrentThread(vm, &env, NULL); + + jstring urlString = (*env)->NewStringUTF(env, url); + jclass uriClass = (*env)->FindClass(env, "android/net/Uri"); + jmethodID uriParse = (*env)->GetStaticMethodID(env, uriClass, "parse", "(Ljava/lang/String;)Landroid/net/Uri;"); + jobject uri = (*env)->CallStaticObjectMethod(env, uriClass, uriParse, urlString); + + jclass intentClass = (*env)->FindClass(env, "android/content/Intent"); + jfieldID actionViewId = (*env)->GetStaticFieldID(env, intentClass, "ACTION_VIEW", "Ljava/lang/String;"); + jobject actionView = (*env)->GetStaticObjectField(env, intentClass, actionViewId); + jmethodID newIntent = (*env)->GetMethodID(env, intentClass, "", "(Ljava/lang/String;Landroid/net/Uri;)V"); + jobject intent = (*env)->AllocObject(env, intentClass); + + (*env)->CallVoidMethod(env, intent, newIntent, actionView, uri); + jclass activityClass = (*env)->FindClass(env, "android/app/Activity"); + jmethodID startActivity = (*env)->GetMethodID(env, activityClass, "startActivity", "(Landroid/content/Intent;)V"); + (*env)->CallVoidMethod(env, platform.app->activity->clazz, startActivity, intent); + + (*vm)->DetachCurrentThread(vm); + } +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Inputs +//---------------------------------------------------------------------------------- + +// Set a custom key to exit program +void SetExitKey(int key) +{ + TRACELOG(LOG_WARNING, "SetExitKey() not implemented on PLATFORM_CUSTOM"); +} + +// Get gamepad internal name id +const char *GetGamepadName(int gamepad) +{ + TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on PLATFORM_CUSTOM"); + return NULL; +} + +// Get gamepad axis count +int GetGamepadAxisCount(int gamepad) +{ + return CORE.Input.Gamepad.axisCount; +} + +// Set internal gamepad mappings +int SetGamepadMappings(const char *mappings) +{ + TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on PLATFORM_CUSTOM"); + return 0; +} + +// Get mouse position X +int GetMouseX(void) +{ + return (int)CORE.Input.Touch.position[0].x; +} + +// Get mouse position Y +int GetMouseY(void) +{ + return (int)CORE.Input.Touch.position[0].y; +} + +// Get mouse position XY +Vector2 GetMousePosition(void) +{ + return GetTouchPosition(0); +} + +// Set mouse position XY +void SetMousePosition(int x, int y) +{ + CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; +} + +// Get mouse wheel movement Y +float GetMouseWheelMove(void) +{ + TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on PLATFORM_CUSTOM"); + return 0.0f; +} + +// Set mouse cursor +void SetMouseCursor(int cursor) +{ + TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on PLATFORM_CUSTOM"); +} + +// Get touch position X for touch point 0 (relative to screen size) +int GetTouchX(void) +{ + return (int)CORE.Input.Touch.position[0].x; +} + +// Get touch position Y for touch point 0 (relative to screen size) +int GetTouchY(void) +{ + return (int)CORE.Input.Touch.position[0].y; +} + +// Get touch position XY for a touch point index (relative to screen size) +// TODO: Touch position should be scaled depending on display size and render size +Vector2 GetTouchPosition(int index) +{ + Vector2 position = { -1.0f, -1.0f }; + + if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index]; + else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS); + + return position; +} + +// Register all input events +void PollInputEvents(void) +{ +#if defined(SUPPORT_GESTURES_SYSTEM) + // NOTE: Gestures update must be called every frame to reset gestures correctly + // because ProcessGestureEvent() is just called on an event, not every frame + UpdateGestures(); +#endif + + // Reset keys/chars pressed registered + CORE.Input.Keyboard.keyPressedQueueCount = 0; + CORE.Input.Keyboard.charPressedQueueCount = 0; + + // Reset key repeats + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + + // Reset last gamepad button/axis registered state + CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN + CORE.Input.Gamepad.axisCount = 0; + + // Register previous touch states + for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; + + // Reset touch positions + // TODO: It resets on PLATFORM_CUSTOM the mouse position and not filled again until a move-event, + // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! + //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; + + // Register previous keys states + // NOTE: Android supports up to 260 keys + for (int i = 0; i < 260; i++) + { + CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } + + // TODO: Poll input events for current plaform +} + + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize display device and framebuffer +// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size +// If width or height are 0, default display size will be used for framebuffer size +// NOTE: returns false in case graphic device could not be created +static bool InitGraphicsDevice(int width, int height) +{ + CORE.Window.screen.width = width; // User desired width + CORE.Window.screen.height = height; // User desired height + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + + // Set the screen minimum and maximum default values to 0 + CORE.Window.screenMin.width = 0; + CORE.Window.screenMin.height = 0; + CORE.Window.screenMax.width = 0; + CORE.Window.screenMax.height = 0; + + // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... + // ...in top-down or left-right to match display aspect ratio (no weird scaling) + + CORE.Window.fullscreen = true; + CORE.Window.flags |= FLAG_FULLSCREEN_MODE; + + EGLint samples = 0; + EGLint sampleBuffer = 0; + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + samples = 4; + sampleBuffer = 1; + TRACELOG(LOG_INFO, "DISPLAY: Trying to enable MSAA x4"); + } + + const EGLint framebufferAttribs[] = + { + EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, // Type of context support + EGL_RED_SIZE, 8, // RED color bit depth (alternative: 5) + EGL_GREEN_SIZE, 8, // GREEN color bit depth (alternative: 6) + EGL_BLUE_SIZE, 8, // BLUE color bit depth (alternative: 5) + //EGL_TRANSPARENT_TYPE, EGL_NONE, // Request transparent framebuffer (EGL_TRANSPARENT_RGB does not work on RPI) + EGL_DEPTH_SIZE, 16, // Depth buffer size (Required to use Depth testing!) + //EGL_STENCIL_SIZE, 8, // Stencil buffer size + EGL_SAMPLE_BUFFERS, sampleBuffer, // Activate MSAA + EGL_SAMPLES, samples, // 4x Antialiasing if activated (Free on MALI GPUs) + EGL_NONE + }; + + const EGLint contextAttribs[] = + { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE + }; + + EGLint numConfigs = 0; + + // Get an EGL device connection + platform.device = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (platform.device == EGL_NO_DISPLAY) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); + return false; + } + + // Initialize the EGL device connection + if (eglInitialize(platform.device, NULL, NULL) == EGL_FALSE) + { + // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred. + TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); + return false; + } + + // Get an appropriate EGL framebuffer configuration + eglChooseConfig(platform.device, framebufferAttribs, &platform.config, 1, &numConfigs); + + // Set rendering API + eglBindAPI(EGL_OPENGL_ES_API); + + // Create an EGL rendering context + platform.context = eglCreateContext(platform.device, platform.config, EGL_NO_CONTEXT, contextAttribs); + if (platform.context == EGL_NO_CONTEXT) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); + return false; + } + + // Create an EGL window surface + //--------------------------------------------------------------------------------- + EGLint displayFormat = 0; + + // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry() + // As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID + eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat); + + // At this point we need to manage render size vs screen size + // NOTE: This function use and modify global module variables: + // -> CORE.Window.screen.width/CORE.Window.screen.height + // -> CORE.Window.render.width/CORE.Window.render.height + // -> CORE.Window.screenScale + SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); + + ANativeWindow_setBuffersGeometry(platform.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat); + //ANativeWindow_setBuffersGeometry(platform.app->window, 0, 0, displayFormat); // Force use of native display size + + platform.surface = eglCreateWindowSurface(platform.device, platform.config, platform.app->window, NULL); + + // There must be at least one frame displayed before the buffers are swapped + //eglSwapInterval(platform.device, 1); + + if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) + { + TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); + return false; + } + else + { + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + } + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(eglGetProcAddress); + + // Initialize OpenGL context (states and resources) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + CORE.Window.ready = true; + + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + return true; +} + +// EOF From d309b1eaa7712dc0d15431bfb6efc0508e6ecbc8 Mon Sep 17 00:00:00 2001 From: Masoud Naservand Date: Mon, 9 Oct 2023 11:17:54 +0330 Subject: [PATCH 0322/1350] Call nsvgDeleteRasterizer() on created rasterizer (#3392) the `NSVGrasterizer *rast` needs to be passed to nsvgDeleteRasterizer() when we are done with it. --- src/rtextures.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rtextures.c b/src/rtextures.c index fa8d91ea4..dc7f4af05 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -384,6 +384,7 @@ Image LoadImageSvg(const char *fileNameOrString, int width, int height) // Free used memory nsvgDelete(svgImage); + nsvgDeleteRasterizer(rast); } if (isSvgStringValid && (fileData != fileNameOrString)) UnloadFileData(fileData); @@ -555,6 +556,7 @@ Image LoadImageFromMemory(const char *fileType, const unsigned char *fileData, i image.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8; nsvgDelete(svgImage); + nsvgDeleteRasterizer(rast); } } #endif From 7ab911b9a4fb0ba3798bb8f117e69475a4fb89bc Mon Sep 17 00:00:00 2001 From: "Dennis E. Hamilton" Date: Mon, 9 Oct 2023 00:49:58 -0700 Subject: [PATCH 0323/1350] Ensure m3d faces in non-decreasing materialid sequence (#3385) This modification replaces the expensive qsort protection with an insertion sort that is near-instantaneous in the expected ordered case. --- src/rmodels.c | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 490eac555..77c89b6e9 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5614,19 +5614,26 @@ static Model LoadM3D(const char *fileName) // We always need a default material, so we add +1 model.materialCount++; - // WARNING: Sorting is not needed, valid M3D model files should already be sorted - // faces should already by grouped by material - // Just keeping the sorting function for reference (Check PR #3363) - /* - static int m3d_compare_faces(const void *a, const void *b) + // Faces must be in non-decreasing materialid order + // Verify that quickly, sorting them otherwise. + for (i = 1; i < m3d->numface; i++) { - m3df_t *fa = (m3df_t *)a; - m3df_t *fb = (m3df_t *)b; - return (fa->materialid - fb->materialid); + if ( m3d->face[i-1].materialid <= m3d->face[i].materialid ) + continue; + + // face[i-1] > face[i]. slide face[i] lower. + m3df_t slider = m3d->face[i]; + j = i-1; + + do + { // face[j] > slider, face[j+1] is svailable vacant gap. + m3d->face[j+1] = m3d->face[j]; + j = j-1; + } + while (j >= 0 && m3d->face[j].materialid > slider.materialid); + + m3d->face[j+1] = slider; } - - qsort(m3d->face, m3d->numface, sizeof(m3df_t), m3d_compare_faces); - */ model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); From 33c84b3c0092982aa3472e94a766f6bf711a31b9 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 09:53:52 +0200 Subject: [PATCH 0324/1350] Update rmodels.c --- src/rmodels.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 77c89b6e9..0809fce80 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5614,12 +5614,13 @@ static Model LoadM3D(const char *fileName) // We always need a default material, so we add +1 model.materialCount++; - // Faces must be in non-decreasing materialid order - // Verify that quickly, sorting them otherwise. + // Faces must be in non-decreasing materialid order. Verify that quickly, sorting them otherwise. + // WARNING: Sorting is not needed, valid M3D model files should already be sorted + // Just keeping the sorting function for reference (Check PR #3363 #3385) + /* for (i = 1; i < m3d->numface; i++) { - if ( m3d->face[i-1].materialid <= m3d->face[i].materialid ) - continue; + if (m3d->face[i-1].materialid <= m3d->face[i].materialid) continue; // face[i-1] > face[i]. slide face[i] lower. m3df_t slider = m3d->face[i]; @@ -5634,6 +5635,7 @@ static Model LoadM3D(const char *fileName) m3d->face[j+1] = slider; } + */ model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); From dfb0326d00c65e145472464b5ebe74802fe630cc Mon Sep 17 00:00:00 2001 From: SuperUserNameMan Date: Mon, 9 Oct 2023 09:54:43 +0200 Subject: [PATCH 0325/1350] Update rcore.c (#3326) --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index a3075aac5..29881198a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -762,7 +762,7 @@ VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device) { VrStereoConfig config = { 0 }; - if ((rlGetVersion() == RL_OPENGL_33) || (rlGetVersion() >= RL_OPENGL_ES_20)) + if (rlGetVersion() != RL_OPENGL_11) { // Compute aspect ratio float aspect = ((float)device.hResolution*0.5f)/(float)device.vResolution; From f86f4159e6eb4ed84cca6147c2f1e76ff2524c19 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 10:51:37 +0200 Subject: [PATCH 0326/1350] Avoid references to `PLATFORM_` flags #3313 --- src/rcore_android.c | 76 ++++++++++++++++++++++----------------------- src/rcore_custom.c | 74 +++++++++++++++++++++---------------------- src/rcore_desktop.c | 6 ++-- src/rcore_drm.c | 60 +++++++++++++++++------------------ src/rcore_web.c | 62 ++++++++++++++++++------------------ 5 files changed, 135 insertions(+), 143 deletions(-) diff --git a/src/rcore_android.c b/src/rcore_android.c index a7c8f3307..bd0674b29 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -17,8 +17,8 @@ * - TRACELOG() function is located in raylib [utils] module * * CONFIGURATION: -* #define RCORE_ANDROID_CUSTOM_FLAG -* Custom flag for rcore on PLATFORM_ANDROID -not used- +* #define RCORE_PLATFORM_CUSTOM_FLAG +* Custom flag for rcore on target platform -not used- * * DEPENDENCIES: * Android NDK - Provides C API to access Android functionality @@ -338,55 +338,55 @@ bool IsWindowResized(void) // Toggle fullscreen mode void ToggleFullscreen(void) { - TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform"); } // Set window state: maximized, if resizable void MaximizeWindow(void) { - TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform"); } // Set window state: minimized void MinimizeWindow(void) { - TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform"); } // Set window state: not minimized/maximized void RestoreWindow(void) { - TRACELOG(LOG_WARNING, "RestoreWindow() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); } // Set window configuration state using flags void SetWindowState(unsigned int flags) { - TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetWindowState() not available on target platform"); } // Clear window configuration state flags void ClearWindowState(unsigned int flags) { - TRACELOG(LOG_WARNING, "ClearWindowState() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform"); } // Set icon for window void SetWindowIcon(Image image) { - TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform"); } // Set icon for window void SetWindowIcons(Image *images, int count) { - TRACELOG(LOG_WARNING, "SetWindowIcons() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform"); } // Set title for window @@ -398,13 +398,13 @@ void SetWindowTitle(const char *title) // Set window position on screen (windowed mode) void SetWindowPosition(int x, int y) { - TRACELOG(LOG_WARNING, "SetWindowPosition() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform"); } // Set monitor for the current window void SetWindowMonitor(int monitor) { - TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform"); } // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) @@ -424,116 +424,116 @@ void SetWindowMaxSize(int width, int height) // Set window dimensions void SetWindowSize(int width, int height) { - TRACELOG(LOG_WARNING, "SetWindowSize() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetWindowSize() not available on target platform"); } // Set window opacity, value opacity is between 0.0 and 1.0 void SetWindowOpacity(float opacity) { - TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform"); } // Set window focused void SetWindowFocused(void) { - TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform"); } // Get native window handle void *GetWindowHandle(void) { - TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform"); return NULL; } // Get number of monitors int GetMonitorCount(void) { - TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform"); return 1; } // Get number of monitors int GetCurrentMonitor(void) { - TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform"); return 0; } // Get selected monitor position Vector2 GetMonitorPosition(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform"); return (Vector2){ 0, 0 }; } // Get selected monitor width (currently used by monitor) int GetMonitorWidth(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on target platform"); return 0; } // Get selected monitor height (currently used by monitor) int GetMonitorHeight(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on target platform"); return 0; } // Get selected monitor physical width in millimetres int GetMonitorPhysicalWidth(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform"); return 0; } // Get selected monitor physical height in millimetres int GetMonitorPhysicalHeight(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform"); return 0; } // Get selected monitor refresh rate int GetMonitorRefreshRate(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform"); return 0; } // Get the human-readable, UTF-8 encoded name of the selected monitor const char *GetMonitorName(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on target platform"); return ""; } // Get window position XY on monitor Vector2 GetWindowPosition(void) { - TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on target platform"); return (Vector2){ 0, 0 }; } // Get window scale DPI factor for current monitor Vector2 GetWindowScaleDPI(void) { - TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform"); return (Vector2){ 1.0f, 1.0f }; } // Set clipboard text content void SetClipboardText(const char *text) { - TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on target platform"); } // Get clipboard text content // NOTE: returned string is allocated and freed by GLFW const char *GetClipboardText(void) { - TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform"); return NULL; } @@ -597,7 +597,7 @@ double GetTime(void) // Ref: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { - // Security check to (partially) avoid malicious code on PLATFORM_ANDROID + // Security check to (partially) avoid malicious code if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); else { @@ -632,13 +632,13 @@ void OpenURL(const char *url) // Set a custom key to exit program void SetExitKey(int key) { - TRACELOG(LOG_WARNING, "SetExitKey() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetExitKey() not implemented on target platform"); } // Get gamepad internal name id const char *GetGamepadName(int gamepad) { - TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on target platform"); return NULL; } @@ -651,7 +651,7 @@ int GetGamepadAxisCount(int gamepad) // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { - TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform"); return 0; } @@ -683,14 +683,14 @@ void SetMousePosition(int x, int y) // Get mouse wheel movement Y float GetMouseWheelMove(void) { - TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on target platform"); return 0.0f; } // Set mouse cursor void SetMouseCursor(int cursor) { - TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on PLATFORM_ANDROID"); + TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); } // Get touch position X for touch point 0 (relative to screen size) @@ -740,8 +740,6 @@ void PollInputEvents(void) for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; // Reset touch positions - // TODO: It resets on PLATFORM_ANDROID the mouse position and not filled again until a move-event, - // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; // Register previous keys states @@ -1222,7 +1220,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) int32_t action = AMotionEvent_getAction(event); unsigned int flags = action & AMOTION_EVENT_ACTION_MASK; -#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_ANDROID +#if defined(SUPPORT_GESTURES_SYSTEM) GestureEvent gestureEvent = { 0 }; gestureEvent.pointCount = CORE.Input.Touch.pointCount; diff --git a/src/rcore_custom.c b/src/rcore_custom.c index f20996268..2d35a3b4f 100644 --- a/src/rcore_custom.c +++ b/src/rcore_custom.c @@ -17,8 +17,8 @@ * - TRACELOG() function is located in raylib [utils] module * * CONFIGURATION: -* #define RCORE_CUSTOM_FLAG -* Custom flag for rcore on PLATFORM_ -not used- +* #define RCORE_PLATFORM_CUSTOM_FLAG +* Custom flag for rcore on target platform -not used- * * DEPENDENCIES: * - Dependency 01 @@ -219,55 +219,55 @@ bool IsWindowResized(void) // Toggle fullscreen mode void ToggleFullscreen(void) { - TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform"); } // Set window state: maximized, if resizable void MaximizeWindow(void) { - TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform"); } // Set window state: minimized void MinimizeWindow(void) { - TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform"); } // Set window state: not minimized/maximized void RestoreWindow(void) { - TRACELOG(LOG_WARNING, "RestoreWindow() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); } // Set window configuration state using flags void SetWindowState(unsigned int flags) { - TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetWindowState() not available on target platform"); } // Clear window configuration state flags void ClearWindowState(unsigned int flags) { - TRACELOG(LOG_WARNING, "ClearWindowState() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform"); } // Set icon for window void SetWindowIcon(Image image) { - TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform"); } // Set icon for window void SetWindowIcons(Image *images, int count) { - TRACELOG(LOG_WARNING, "SetWindowIcons() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform"); } // Set title for window @@ -279,13 +279,13 @@ void SetWindowTitle(const char *title) // Set window position on screen (windowed mode) void SetWindowPosition(int x, int y) { - TRACELOG(LOG_WARNING, "SetWindowPosition() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform"); } // Set monitor for the current window void SetWindowMonitor(int monitor) { - TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform"); } // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) @@ -305,116 +305,116 @@ void SetWindowMaxSize(int width, int height) // Set window dimensions void SetWindowSize(int width, int height) { - TRACELOG(LOG_WARNING, "SetWindowSize() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetWindowSize() not available on target platform"); } // Set window opacity, value opacity is between 0.0 and 1.0 void SetWindowOpacity(float opacity) { - TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform"); } // Set window focused void SetWindowFocused(void) { - TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform"); } // Get native window handle void *GetWindowHandle(void) { - TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform"); return NULL; } // Get number of monitors int GetMonitorCount(void) { - TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform"); return 1; } // Get number of monitors int GetCurrentMonitor(void) { - TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform"); return 0; } // Get selected monitor position Vector2 GetMonitorPosition(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform"); return (Vector2){ 0, 0 }; } // Get selected monitor width (currently used by monitor) int GetMonitorWidth(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on target platform"); return 0; } // Get selected monitor height (currently used by monitor) int GetMonitorHeight(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on target platform"); return 0; } // Get selected monitor physical width in millimetres int GetMonitorPhysicalWidth(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform"); return 0; } // Get selected monitor physical height in millimetres int GetMonitorPhysicalHeight(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform"); return 0; } // Get selected monitor refresh rate int GetMonitorRefreshRate(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform"); return 0; } // Get the human-readable, UTF-8 encoded name of the selected monitor const char *GetMonitorName(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on target platform"); return ""; } // Get window position XY on monitor Vector2 GetWindowPosition(void) { - TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on target platform"); return (Vector2){ 0, 0 }; } // Get window scale DPI factor for current monitor Vector2 GetWindowScaleDPI(void) { - TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform"); return (Vector2){ 1.0f, 1.0f }; } // Set clipboard text content void SetClipboardText(const char *text) { - TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on target platform"); } // Get clipboard text content // NOTE: returned string is allocated and freed by GLFW const char *GetClipboardText(void) { - TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform"); return NULL; } @@ -478,7 +478,7 @@ double GetTime(void) // Ref: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { - // Security check to (partially) avoid malicious code on PLATFORM_CUSTOM + // Security check to (partially) avoid malicious code on target platform if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); else { @@ -513,13 +513,13 @@ void OpenURL(const char *url) // Set a custom key to exit program void SetExitKey(int key) { - TRACELOG(LOG_WARNING, "SetExitKey() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetExitKey() not implemented on target platform"); } // Get gamepad internal name id const char *GetGamepadName(int gamepad) { - TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on target platform"); return NULL; } @@ -532,7 +532,7 @@ int GetGamepadAxisCount(int gamepad) // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { - TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform"); return 0; } @@ -564,14 +564,14 @@ void SetMousePosition(int x, int y) // Get mouse wheel movement Y float GetMouseWheelMove(void) { - TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on target platform"); return 0.0f; } // Set mouse cursor void SetMouseCursor(int cursor) { - TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on PLATFORM_CUSTOM"); + TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); } // Get touch position X for touch point 0 (relative to screen size) @@ -622,7 +622,7 @@ void PollInputEvents(void) for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; // Reset touch positions - // TODO: It resets on PLATFORM_CUSTOM the mouse position and not filled again until a move-event, + // TODO: It resets on target platform the mouse position and not filled again until a move-event, // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index dcd71ef18..2c9872a34 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -20,8 +20,8 @@ * - TRACELOG() function is located in raylib [utils] module * * CONFIGURATION: -* #define RCORE_DESKTOP_CUSTOM_FLAG -* Custom flag for rcore on PLATFORM_DESKTOP -not used- +* #define RCORE_PLATFORM_CUSTOM_FLAG +* Custom flag for rcore on target platform -not used- * * DEPENDENCIES: * rglfw - Manage graphic device, OpenGL context and inputs (Windows, Linux, OSX, FreeBSD...) @@ -1368,8 +1368,6 @@ void PollInputEvents(void) for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; // Reset touch positions - // TODO: It resets on PLATFORM_WEB the mouse position and not filled again until a move-event, - // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; // Check if gamepads are ready diff --git a/src/rcore_drm.c b/src/rcore_drm.c index aadf0f475..50e45c4ae 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -442,55 +442,55 @@ bool IsWindowResized(void) // Toggle fullscreen mode void ToggleFullscreen(void) { - TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform"); } // Set window state: maximized, if resizable void MaximizeWindow(void) { - TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform"); } // Set window state: minimized void MinimizeWindow(void) { - TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform"); } // Set window state: not minimized/maximized void RestoreWindow(void) { - TRACELOG(LOG_WARNING, "RestoreWindow() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); } // Set window configuration state using flags void SetWindowState(unsigned int flags) { - TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetWindowState() not available on target platform"); } // Clear window configuration state flags void ClearWindowState(unsigned int flags) { - TRACELOG(LOG_WARNING, "ClearWindowState() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform"); } // Set icon for window void SetWindowIcon(Image image) { - TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform"); } // Set icon for window void SetWindowIcons(Image *images, int count) { - TRACELOG(LOG_WARNING, "SetWindowIcons() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform"); } // Set title for window @@ -502,13 +502,13 @@ void SetWindowTitle(const char *title) // Set window position on screen (windowed mode) void SetWindowPosition(int x, int y) { - TRACELOG(LOG_WARNING, "SetWindowPosition() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform"); } // Set monitor for the current window void SetWindowMonitor(int monitor) { - TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform"); } // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) @@ -528,74 +528,74 @@ void SetWindowMaxSize(int width, int height) // Set window dimensions void SetWindowSize(int width, int height) { - TRACELOG(LOG_WARNING, "SetWindowSize() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetWindowSize() not available on target platform"); } // Set window opacity, value opacity is between 0.0 and 1.0 void SetWindowOpacity(float opacity) { - TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform"); } // Set window focused void SetWindowFocused(void) { - TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform"); } // Get native window handle void *GetWindowHandle(void) { - TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform"); return NULL; } // Get number of monitors int GetMonitorCount(void) { - TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform"); return 1; } // Get number of monitors int GetCurrentMonitor(void) { - TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform"); return 0; } // Get selected monitor position Vector2 GetMonitorPosition(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform"); return (Vector2){ 0, 0 }; } // Get selected monitor width (currently used by monitor) int GetMonitorWidth(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on target platform"); return 0; } // Get selected monitor height (currently used by monitor) int GetMonitorHeight(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on target platform"); return 0; } // Get selected monitor physical width in millimetres int GetMonitorPhysicalWidth(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform"); return 0; } // Get selected monitor physical height in millimetres int GetMonitorPhysicalHeight(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform"); return 0; } @@ -615,7 +615,7 @@ int GetMonitorRefreshRate(int monitor) // Get the human-readable, UTF-8 encoded name of the selected monitor const char *GetMonitorName(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on target platform"); return ""; } @@ -634,14 +634,14 @@ Vector2 GetWindowScaleDPI(void) // Set clipboard text content void SetClipboardText(const char *text) { - TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on target platform"); } // Get clipboard text content // NOTE: returned string is allocated and freed by GLFW const char *GetClipboardText(void) { - TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform"); return NULL; } @@ -729,7 +729,7 @@ double GetTime(void) // Ref: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { - TRACELOG(LOG_WARNING, "OpenURL() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "OpenURL() not implemented on target platform"); } //---------------------------------------------------------------------------------- @@ -770,7 +770,7 @@ int GetGamepadAxisCount(int gamepad) // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { - TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform"); return 0; } @@ -818,7 +818,7 @@ float GetMouseWheelMove(void) // Set mouse cursor void SetMouseCursor(int cursor) { - TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on PLATFORM_DRM"); + TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); } // Get touch position X for touch point 0 (relative to screen size) @@ -897,8 +897,6 @@ void PollInputEvents(void) for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; // Reset touch positions - // TODO: It resets on PLATFORM_WEB the mouse position and not filled again until a move-event, - // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; #if defined(SUPPORT_SSH_KEYBOARD_RPI) @@ -1911,7 +1909,7 @@ static void *EventThread(void *arg) if (CORE.Input.Touch.position[i].x >= 0) CORE.Input.Touch.pointCount++; } -#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_DRM +#if defined(SUPPORT_GESTURES_SYSTEM) if (gestureUpdate) { GestureEvent gestureEvent = { 0 }; diff --git a/src/rcore_web.c b/src/rcore_web.c index 249f0db4c..111666045 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -16,8 +16,8 @@ * - TRACELOG() function is located in raylib [utils] module * * CONFIGURATION: -* #define RCORE_WEB_CUSTOM_FLAG -* Custom flag for rcore on PLATFORM_WEB -not used- +* #define RCORE_PLATFORM_CUSTOM_FLAG +* Custom flag for rcore on target platform -not used- * * DEPENDENCIES: * emscripten - Allow interaction between browser API and C @@ -413,49 +413,49 @@ void ToggleFullscreen(void) // Set window state: maximized, if resizable void MaximizeWindow(void) { - TRACELOG(LOG_WARNING, "MaximizeWindow() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "MaximizeWindow() not available on target platform"); } // Set window state: minimized void MinimizeWindow(void) { - TRACELOG(LOG_WARNING, "MinimizeWindow() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "MinimizeWindow() not available on target platform"); } // Set window state: not minimized/maximized void RestoreWindow(void) { - TRACELOG(LOG_WARNING, "RestoreWindow() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); } // Set window configuration state using flags void SetWindowState(unsigned int flags) { - TRACELOG(LOG_WARNING, "SetWindowState() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "SetWindowState() not available on target platform"); } // Clear window configuration state flags void ClearWindowState(unsigned int flags) { - TRACELOG(LOG_WARNING, "ClearWindowState() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform"); } // Set icon for window void SetWindowIcon(Image image) { - TRACELOG(LOG_WARNING, "SetWindowIcon() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform"); } // Set icon for window, multiple images void SetWindowIcons(Image *images, int count) { - TRACELOG(LOG_WARNING, "SetWindowIcons() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform"); } // Set title for window @@ -468,13 +468,13 @@ void SetWindowTitle(const char *title) // Set window position on screen (windowed mode) void SetWindowPosition(int x, int y) { - TRACELOG(LOG_WARNING, "SetWindowPosition() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform"); } // Set monitor for the current window void SetWindowMonitor(int monitor) { - TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform"); } // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) @@ -506,96 +506,96 @@ void SetWindowSize(int width, int height) // Set window opacity, value opacity is between 0.0 and 1.0 void SetWindowOpacity(float opacity) { - TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform"); } // Set window focused void SetWindowFocused(void) { - TRACELOG(LOG_WARNING, "SetWindowFocused() not available on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform"); } // Get native window handle void *GetWindowHandle(void) { - TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform"); return NULL; } // Get number of monitors int GetMonitorCount(void) { - TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform"); return 1; } // Get number of monitors int GetCurrentMonitor(void) { - TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform"); return 0; } // Get selected monitor position Vector2 GetMonitorPosition(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform"); return (Vector2){ 0, 0 }; } // Get selected monitor width (currently used by monitor) int GetMonitorWidth(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on target platform"); return 0; } // Get selected monitor height (currently used by monitor) int GetMonitorHeight(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on target platform"); return 0; } // Get selected monitor physical width in millimetres int GetMonitorPhysicalWidth(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform"); return 0; } // Get selected monitor physical height in millimetres int GetMonitorPhysicalHeight(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform"); return 0; } // Get selected monitor refresh rate int GetMonitorRefreshRate(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform"); return 0; } // Get the human-readable, UTF-8 encoded name of the selected monitor const char *GetMonitorName(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on target platform"); return ""; } // Get window position XY on monitor Vector2 GetWindowPosition(void) { - TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on target platform"); return (Vector2){ 0, 0 }; } // Get window scale DPI factor for current monitor Vector2 GetWindowScaleDPI(void) { - TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on PLATFORM_WEB"); + TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform"); return (Vector2){ 1.0f, 1.0f }; } @@ -688,7 +688,7 @@ double GetTime(void) // Ref: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { - // Security check to (partially) avoid malicious code on PLATFORM_WEB + // Security check to (partially) avoid malicious code on target platform if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); else emscripten_run_script(TextFormat("window.open('%s', '_blank')", url)); } @@ -745,9 +745,7 @@ Vector2 GetMousePosition(void) { Vector2 position = { 0 }; - // TODO: Review touch position - - // NOTE: On PLATFORM_WEB, even on canvas scaling, mouse position is proportionally returned + // NOTE: On canvas scaling, mouse position is proportionally returned position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; @@ -846,7 +844,7 @@ void PollInputEvents(void) for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; // Reset touch positions - // TODO: It resets on PLATFORM_WEB the mouse position and not filled again until a move-event, + // TODO: It resets on target platform the mouse position and not filled again until a move-event, // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; @@ -1567,7 +1565,7 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent else if (eventType == EMSCRIPTEN_EVENT_TOUCHEND) CORE.Input.Touch.currentTouchState[i] = 0; } -#if defined(SUPPORT_GESTURES_SYSTEM) // PLATFORM_WEB +#if defined(SUPPORT_GESTURES_SYSTEM) GestureEvent gestureEvent = {0}; gestureEvent.pointCount = CORE.Input.Touch.pointCount; From b55cf40b910a428cba7e69ea9133d555a63b818b Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 11:07:02 +0200 Subject: [PATCH 0327/1350] Format tweaks --- src/rcore.h | 10 +++++----- src/rcore_android.c | 19 ++++++++++++------- src/rcore_custom.c | 25 ++++++++++++------------- src/rcore_desktop.c | 21 ++++++++++++--------- src/rcore_drm.c | 31 +++++++++++++++++-------------- src/rcore_web.c | 17 ++++++++--------- src/rmodels.c | 8 ++++---- 7 files changed, 70 insertions(+), 61 deletions(-) diff --git a/src/rcore.h b/src/rcore.h index 132c8e007..7fb35915a 100644 --- a/src/rcore.h +++ b/src/rcore.h @@ -133,18 +133,18 @@ typedef struct CoreData { char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) unsigned int dropFileCount; // Count dropped files strings - + } Window; struct { const char *basePath; // Base path for data storage - + } Storage; struct { struct { int exitKey; // Default exit key char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state - + // NOTE: Since key press logic involves comparing prev vs cur key state, we need to handle key repeats specially char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame. @@ -177,7 +177,7 @@ typedef struct CoreData { Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen char currentTouchState[MAX_TOUCH_POINTS]; // Registers current touch state char previousTouchState[MAX_TOUCH_POINTS]; // Registers previous touch state - + } Touch; struct { int lastButtonPressed; // Register last gamepad button pressed @@ -199,7 +199,7 @@ typedef struct CoreData { double target; // Desired time for one frame, if 0 not applied unsigned long long int base; // Base time measure for hi-res timer (PLATFORM_ANDROID, PLATFORM_DRM) unsigned int frameCounter; // Frame counter - + } Time; } CoreData; diff --git a/src/rcore_android.c b/src/rcore_android.c index bd0674b29..a3db7e5fa 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rcore_android - Functions to manage window, graphics device and inputs +* rcore_android - Functions to manage window, graphics device and inputs * * PLATFORM: ANDROID * - Android (ARM, ARM64) @@ -48,13 +48,13 @@ #include "rcore.h" -//#include // Required for: Android sensors functions (accelerometer, gyroscope, light...) -#include // Required for: AWINDOW_FLAG_FULLSCREEN definition and others #include // Required for: android_app struct and activity management +#include // Required for: AWINDOW_FLAG_FULLSCREEN definition and others +//#include // Required for: Android sensors functions (accelerometer, gyroscope, light...) #include // Required for: JNIEnv and JavaVM [Used in OpenURL()] #include // Native platform windowing system interface - + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- @@ -64,7 +64,7 @@ typedef struct { struct android_poll_source *source; // Android events polling source bool appEnabled; // Flag to detect if app is active ** = true bool contextRebindRequired; // Used to know context rebind required - + // Display data EGLDisplay device; // Native display device (physical screen connection) EGLSurface surface; // Surface to draw on, framebuffers (connected to context) @@ -94,7 +94,7 @@ static GamepadButton AndroidTranslateGamepadButton(int button); // NOTE: Functions declaration is provided by raylib.h //---------------------------------------------------------------------------------- -// Module Functions Definition +// Module Functions Definition: Application //---------------------------------------------------------------------------------- // To allow easier porting to android, we allow the user to define a @@ -188,6 +188,8 @@ void InitWindow(int width, int height, const char *title) CORE.Window.currentFbo.width = width; CORE.Window.currentFbo.height = height; + // Platform specific init window + //-------------------------------------------------------------- // Set desired windows flags before initializing anything ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER @@ -269,6 +271,8 @@ void CloseWindow(void) timeEndPeriod(1); // Restore time period #endif + // Platform specific close window + //-------------------------------------------------------------- // Close surface, context and display if (platform.device != EGL_NO_DISPLAY) { @@ -289,6 +293,7 @@ void CloseWindow(void) eglTerminate(platform.device); platform.device = EGL_NO_DISPLAY; } + //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) RL_FREE(events); @@ -586,7 +591,7 @@ double GetTime(void) unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer() - + return time; } diff --git a/src/rcore_custom.c b/src/rcore_custom.c index 2d35a3b4f..799c6054b 100644 --- a/src/rcore_custom.c +++ b/src/rcore_custom.c @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rcore_ - Functions to manage window, graphics device and inputs +* rcore_ - Functions to manage window, graphics device and inputs * * PLATFORM: * - TODO: Define the target platform for the core @@ -48,14 +48,14 @@ #include "rcore.h" -// TODO: Include the platform specific libraries - +// TODO: Include the platform specific libraries + //---------------------------------------------------------------------------------- // Types and Structures Definition //---------------------------------------------------------------------------------- typedef struct { // TODO: Define the platform specific variables required - + // Display data EGLDisplay device; // Native display device (physical screen connection) EGLSurface surface; // Surface to draw on, framebuffers (connected to context) @@ -128,15 +128,15 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Window.eventWaiting = false; + CORE.Window.eventWaiting = false; CORE.Window.screen.width = width; CORE.Window.screen.height = height; CORE.Window.currentFbo.width = width; CORE.Window.currentFbo.height = height; // TODO: Initialize window/display system - + // TODO: Initialize input events system // TODO: Initialize assets manager @@ -165,11 +165,10 @@ void CloseWindow(void) rlglClose(); // De-init rlgl -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - timeEndPeriod(1); // Restore time period -#endif - - // TODO: Close surface, context and display + // Platform specific close window + //-------------------------------------------------------------- + // TODO. + //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) RL_FREE(events); @@ -467,7 +466,7 @@ double GetTime(void) unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer() - + return time; } @@ -610,7 +609,7 @@ void PollInputEvents(void) // Reset keys/chars pressed registered CORE.Input.Keyboard.keyPressedQueueCount = 0; CORE.Input.Keyboard.charPressedQueueCount = 0; - + // Reset key repeats for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 2c9872a34..2cf509507 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rcore_desktop - Functions to manage window, graphics device and inputs +* rcore_desktop - Functions to manage window, graphics device and inputs * * PLATFORM: DESKTOP * - Windows (Win32, Win64) @@ -268,12 +268,15 @@ void CloseWindow(void) rlglClose(); // De-init rlgl + // Platform specific close window + //-------------------------------------------------------------- glfwDestroyWindow(platform.handle); glfwTerminate(); #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) timeEndPeriod(1); // Restore time period #endif + //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) RL_FREE(events); @@ -422,11 +425,11 @@ void ToggleBorderlessWindowed(void) const int monitor = GetCurrentMonitor(); int monitorCount; GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); - + if ((monitor >= 0) && (monitor < monitorCount)) { const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - + if (mode) { if (!IsWindowState(FLAG_BORDERLESS_WINDOWED_MODE)) @@ -1016,7 +1019,7 @@ int GetMonitorHeight(int monitor) else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); } else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); - + return height; } @@ -1029,7 +1032,7 @@ int GetMonitorPhysicalWidth(int monitor) if ((monitor >= 0) && (monitor < monitorCount)) glfwGetMonitorPhysicalSize(monitors[monitor], &width, NULL); else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); - + return width; } @@ -1042,7 +1045,7 @@ int GetMonitorPhysicalHeight(int monitor) if ((monitor >= 0) && (monitor < monitorCount)) glfwGetMonitorPhysicalSize(monitors[monitor], NULL, &height); else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); - + return height; } @@ -1059,7 +1062,7 @@ int GetMonitorRefreshRate(int monitor) refresh = vidmode->refreshRate; } else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); - + return refresh; } @@ -1082,9 +1085,9 @@ Vector2 GetWindowPosition(void) { int x = 0; int y = 0; - + glfwGetWindowPos(platform.handle, &x, &y); - + return (Vector2){ (float)x, (float)y }; } diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 50e45c4ae..4a0d805f8 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rcore_drm - Functions to manage window, graphics device and inputs +* rcore_drm - Functions to manage window, graphics device and inputs * * PLATFORM: DRM * - Raspberry Pi 0-5 @@ -81,7 +81,7 @@ typedef struct { pthread_t threadId; // Event reading thread id - + int fd; // File descriptor to the device it is assigned to int eventNum; // Number of 'event' device Rectangle absRange; // Range of values for absolute pointing devices (touchscreens) @@ -111,7 +111,7 @@ typedef struct { // Input data InputEventWorker eventWorker[10]; // List of worker threads for every monitored "/dev/input/event" - + // Keyboard data int defaultKeyboardMode; // Default keyboard mode bool eventKeyboardMode; // Keyboard in event mode @@ -307,6 +307,8 @@ void CloseWindow(void) timeEndPeriod(1); // Restore time period #endif + // Platform specific close window + //-------------------------------------------------------------- if (platform.prevFB) { drmModeRmFB(platform.fd, platform.prevFB); @@ -392,6 +394,7 @@ void CloseWindow(void) } if (platform.gamepadThreadId) pthread_join(platform.gamepadThreadId, NULL); + //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) RL_FREE(events); @@ -603,12 +606,12 @@ int GetMonitorPhysicalHeight(int monitor) int GetMonitorRefreshRate(int monitor) { int refresh = 0; - + if ((platform.connector) && (platform.modeIndex >= 0)) { refresh = platform.connector->modes[platform.modeIndex].vrefresh; } - + return refresh; } @@ -718,7 +721,7 @@ double GetTime(void) unsigned long long int nanoSeconds = (unsigned long long int)ts.tv_sec*1000000000LLU + (unsigned long long int)ts.tv_nsec; time = (double)(nanoSeconds - CORE.Time.base)*1e-9; // Elapsed time since InitTimer() - + return time; } @@ -763,7 +766,7 @@ int GetGamepadAxisCount(int gamepad) int axisCount = 0; if (CORE.Input.Gamepad.ready[gamepad]) ioctl(platform.gamepadStreamFd[gamepad], JSIOCGAXES, &axisCount); CORE.Input.Gamepad.axisCount = axisCount; - + return CORE.Input.Gamepad.axisCount; } @@ -837,10 +840,10 @@ int GetTouchY(void) Vector2 GetTouchPosition(int index) { Vector2 position = { -1.0f, -1.0f }; - + if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index]; else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS); - + return position; } @@ -856,7 +859,7 @@ void PollInputEvents(void) // Reset keys/chars pressed registered CORE.Input.Keyboard.keyPressedQueueCount = 0; CORE.Input.Keyboard.charPressedQueueCount = 0; - + // Reset key repeats for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; @@ -979,14 +982,14 @@ static bool InitGraphicsDevice(int width, int height) } TRACELOG(LOG_TRACE, "DISPLAY: Connectors found: %i", res->count_connectors); - + for (size_t i = 0; i < res->count_connectors; i++) { TRACELOG(LOG_TRACE, "DISPLAY: Connector index %i", i); - + drmModeConnector *con = drmModeGetConnector(platform.fd, res->connectors[i]); TRACELOG(LOG_TRACE, "DISPLAY: Connector modes detected: %i", con->count_modes); - + if ((con->connection == DRM_MODE_CONNECTED) && (con->encoder_id)) { TRACELOG(LOG_TRACE, "DISPLAY: DRM mode connected"); @@ -1440,7 +1443,7 @@ static void ProcessKeyboard(void) } #endif // SUPPORT_SSH_KEYBOARD_RPI -// Initialise user input from evdev(/dev/input/event) +// Initialise user input from evdev(/dev/input/event) // this means mouse, keyboard or gamepad devices static void InitEvdevInput(void) { diff --git a/src/rcore_web.c b/src/rcore_web.c index 111666045..f4f412f2a 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rcore_web - Functions to manage window, graphics device and inputs +* rcore_web - Functions to manage window, graphics device and inputs * * PLATFORM: WEB * - HTML5 (WebAssembly) @@ -237,7 +237,7 @@ void InitWindow(int width, int height, const char *title) // emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); // Check Resize event (note this is done on the window since most browsers don't support this on #canvas) emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - + // Trigger this once to get initial window sizing EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); @@ -282,12 +282,11 @@ void CloseWindow(void) rlglClose(); // De-init rlgl + // Platform specific close window + //-------------------------------------------------------------- glfwDestroyWindow(platform.handle); glfwTerminate(); - -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - timeEndPeriod(1); // Restore time period -#endif + //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) RL_FREE(events); @@ -482,7 +481,7 @@ void SetWindowMinSize(int width, int height) { CORE.Window.screenMin.width = width; CORE.Window.screenMin.height = height; - + // Trigger the resize event once to update the window minimum width and height if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) != 0) EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); } @@ -1090,7 +1089,7 @@ static bool InitGraphicsDevice(int width, int height) } } } - + TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height); // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example, @@ -1229,7 +1228,7 @@ static void WindowIconifyCallback(GLFWwindow *window, int iconified) // GLFW3 Window Maximize Callback, runs when window is maximized static void WindowMaximizeCallback(GLFWwindow *window, int maximized) { - + } // GLFW3 WindowFocus Callback, runs when window get/lose focus diff --git a/src/rmodels.c b/src/rmodels.c index 0809fce80..a11ecf799 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5466,11 +5466,11 @@ static Model LoadVOX(const char *fileName) int nbvertices = 0; int meshescount = 0; - + // Read vox file into buffer int dataSize = 0; unsigned char *fileData = LoadFileData(fileName, &dataSize); - + if (fileData == 0) { TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load VOX file", fileName); @@ -5575,7 +5575,7 @@ static Model LoadM3D(const char *fileName) m3d_t *m3d = NULL; m3dp_t *prop = NULL; int i, j, k, l, n, mi = -2, vcolor = 0; - + int dataSize = 0; unsigned char *fileData = LoadFileData(fileName, &dataSize); @@ -5905,7 +5905,7 @@ static Model LoadM3D(const char *fileName) static ModelAnimation *LoadModelAnimationsM3D(const char *fileName, int *animCount) { ModelAnimation *animations = NULL; - + m3d_t *m3d = NULL; int i = 0, j = 0; *animCount = 0; From f93d0ff9bc72749e66b99e8813fb41fd7b8bb813 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 11:17:09 +0200 Subject: [PATCH 0328/1350] Update raudio.c --- src/raudio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index 61e00a386..c2590e302 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1906,7 +1906,7 @@ void UpdateMusicStream(Music music) { while (true) { - int frameCountRead = drflac_read_pcm_frames_s16((drflac *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize)); + int frameCountRead = (int)drflac_read_pcm_frames_s16((drflac *)music.ctxData, frameCountStillNeeded, (short *)((char *)AUDIO.System.pcmBuffer + frameCountReadTotal*frameSize)); frameCountReadTotal += frameCountRead; frameCountStillNeeded -= frameCountRead; if (frameCountStillNeeded == 0) break; From cfffa74f96c06231f6fa327e9cc52863b18d52b2 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 9 Oct 2023 11:17:22 +0200 Subject: [PATCH 0329/1350] REVIEWED: Libs include order --- src/rcore.h | 8 ++++---- src/utils.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/rcore.h b/src/rcore.h index 7fb35915a..4d40c9567 100644 --- a/src/rcore.h +++ b/src/rcore.h @@ -37,12 +37,12 @@ #include "raylib.h" -#include "rlgl.h" +#include "utils.h" // Required for: TRACELOG() macros + +#include "rlgl.h" // Required for: graphics layer functionality #define RAYMATH_IMPLEMENTATION -#include "raymath.h" - -#include "utils.h" // Required for: TRACELOG() macros +#include "raymath.h" // Required for: Vector2/Vector3/Matrix functionality #include // Required for: srand(), rand(), atexit() #include // Required for: sprintf() [Used in OpenURL()] diff --git a/src/utils.h b/src/utils.h index 6ec0f7f1b..ff8246a7b 100644 --- a/src/utils.h +++ b/src/utils.h @@ -32,7 +32,7 @@ #include // Required for: AAssetManager #endif -#if defined(SUPPORT_TRACELOG) && !defined(TRACELOG) +#if defined(SUPPORT_TRACELOG) #define TRACELOG(level, ...) TraceLog(level, __VA_ARGS__) #if defined(SUPPORT_TRACELOG_DEBUG) From 540ad9944205235cd9ccbd716c5a667daf929616 Mon Sep 17 00:00:00 2001 From: Purple4pur <49893724+purple4pur@users.noreply.github.com> Date: Mon, 9 Oct 2023 19:05:19 +0800 Subject: [PATCH 0330/1350] Update zig build system to zig version 0.11.0 (#3393) * update build.zig for zig 0.11.0 * fix build.zig in examples to install executable correctly * discard build.zig, only use src/build.zig, to avoid annoying zig-out path problem * update zig version note --- build.zig | 7 ------- examples/build.zig | 36 ++++++++++++++++++++---------------- src/build.zig | 10 +++++----- 3 files changed, 25 insertions(+), 28 deletions(-) delete mode 100644 build.zig diff --git a/build.zig b/build.zig deleted file mode 100644 index 12c0513f6..000000000 --- a/build.zig +++ /dev/null @@ -1,7 +0,0 @@ -const std = @import("std"); -const raylib = @import("src/build.zig"); - -// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 -pub fn build(b: *std.Build) void { - raylib.build(b); -} diff --git a/examples/build.zig b/examples/build.zig index 6e13ab3da..383d9f651 100644 --- a/examples/build.zig +++ b/examples/build.zig @@ -1,7 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); -// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) !*std.Build.Step { if (target.getOsTag() == .emscripten) { @panic("Emscripten building via Zig unsupported"); @@ -11,7 +11,7 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT const dir = try std.fs.cwd().openIterableDir(module, .{}); var iter = dir.iterate(); while (try iter.next()) |entry| { - if (entry.kind != .File) continue; + if (entry.kind != .file) continue; const extension_idx = std.mem.lastIndexOf(u8, entry.name, ".c") orelse continue; const name = entry.name[0..extension_idx]; const path = try std.fs.path.join(b.allocator, &.{ module, entry.name }); @@ -24,26 +24,26 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT .target = target, .optimize = optimize, }); - exe.addCSourceFile(path, &[_][]const u8{}); + exe.addCSourceFile(.{ .file = .{ .path = path }, .flags = &.{} }); exe.linkLibC(); exe.addObjectFile(switch (target.getOsTag()) { - .windows => "../src/zig-out/lib/raylib.lib", - .linux => "../src/zig-out/lib/libraylib.a", - .macos => "../src/zig-out/lib/libraylib.a", - .emscripten => "../src/zig-out/lib/libraylib.a", + .windows => .{ .path = "../src/zig-out/lib/raylib.lib" }, + .linux => .{ .path = "../src/zig-out/lib/libraylib.a" }, + .macos => .{ .path = "../src/zig-out/lib/libraylib.a" }, + .emscripten => .{ .path = "../src/zig-out/lib/libraylib.a" }, else => @panic("Unsupported OS"), }); - exe.addIncludePath("../src"); - exe.addIncludePath("../src/external"); - exe.addIncludePath("../src/external/glfw/include"); + exe.addIncludePath(.{ .path = "../src" }); + exe.addIncludePath(.{ .path = "../src/external" }); + exe.addIncludePath(.{ .path = "../src/external/glfw/include" }); switch (target.getOsTag()) { .windows => { exe.linkSystemLibrary("winmm"); exe.linkSystemLibrary("gdi32"); exe.linkSystemLibrary("opengl32"); - exe.addIncludePath("external/glfw/deps/mingw"); + exe.addIncludePath(.{ .path = "external/glfw/deps/mingw" }); exe.defineCMacro("PLATFORM_DESKTOP", null); }, @@ -71,11 +71,15 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT }, } - b.installArtifact(exe); - var run = b.addRunArtifact(exe); - run.cwd = module; - b.step(name, name).dependOn(&run.step); - all.dependOn(&exe.step); + const install_cmd = b.addInstallArtifact(exe, .{}); + + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(&install_cmd.step); + + const run_step = b.step(name, name); + run_step.dependOn(&run_cmd.step); + + all.dependOn(&install_cmd.step); } return all; } diff --git a/src/build.zig b/src/build.zig index 27250f5ff..3bb4f4213 100644 --- a/src/build.zig +++ b/src/build.zig @@ -1,6 +1,6 @@ const std = @import("std"); -// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode, options: Options) *std.Build.CompileStep { const raylib_flags = &[_][]const u8{ "-std=gnu99", @@ -192,12 +192,12 @@ pub fn build(b: *std.Build) void { const lib = addRaylib(b, target, optimize, options); - lib.installHeader("src/raylib.h", "raylib.h"); - lib.installHeader("src/raymath.h", "raymath.h"); - lib.installHeader("src/rlgl.h", "rlgl.h"); + lib.installHeader("raylib.h", "raylib.h"); + lib.installHeader("raymath.h", "raymath.h"); + lib.installHeader("rlgl.h", "rlgl.h"); if (options.raygui) { - lib.installHeader("../raygui/src/raygui.h", "raygui.h"); + lib.installHeader("../../raygui/src/raygui.h", "raygui.h"); } b.installArtifact(lib); From 0d8a6cfbfa474be123cedff6a4faddfe5443fcec Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 10 Oct 2023 08:48:55 +0200 Subject: [PATCH 0331/1350] Revert "Update zig build system to zig version 0.11.0 (#3393)" This reverts commit 540ad9944205235cd9ccbd716c5a667daf929616. --- build.zig | 7 +++++++ examples/build.zig | 36 ++++++++++++++++-------------------- src/build.zig | 10 +++++----- 3 files changed, 28 insertions(+), 25 deletions(-) create mode 100644 build.zig diff --git a/build.zig b/build.zig new file mode 100644 index 000000000..12c0513f6 --- /dev/null +++ b/build.zig @@ -0,0 +1,7 @@ +const std = @import("std"); +const raylib = @import("src/build.zig"); + +// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +pub fn build(b: *std.Build) void { + raylib.build(b); +} diff --git a/examples/build.zig b/examples/build.zig index 383d9f651..6e13ab3da 100644 --- a/examples/build.zig +++ b/examples/build.zig @@ -1,7 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); -// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) +// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) !*std.Build.Step { if (target.getOsTag() == .emscripten) { @panic("Emscripten building via Zig unsupported"); @@ -11,7 +11,7 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT const dir = try std.fs.cwd().openIterableDir(module, .{}); var iter = dir.iterate(); while (try iter.next()) |entry| { - if (entry.kind != .file) continue; + if (entry.kind != .File) continue; const extension_idx = std.mem.lastIndexOf(u8, entry.name, ".c") orelse continue; const name = entry.name[0..extension_idx]; const path = try std.fs.path.join(b.allocator, &.{ module, entry.name }); @@ -24,26 +24,26 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT .target = target, .optimize = optimize, }); - exe.addCSourceFile(.{ .file = .{ .path = path }, .flags = &.{} }); + exe.addCSourceFile(path, &[_][]const u8{}); exe.linkLibC(); exe.addObjectFile(switch (target.getOsTag()) { - .windows => .{ .path = "../src/zig-out/lib/raylib.lib" }, - .linux => .{ .path = "../src/zig-out/lib/libraylib.a" }, - .macos => .{ .path = "../src/zig-out/lib/libraylib.a" }, - .emscripten => .{ .path = "../src/zig-out/lib/libraylib.a" }, + .windows => "../src/zig-out/lib/raylib.lib", + .linux => "../src/zig-out/lib/libraylib.a", + .macos => "../src/zig-out/lib/libraylib.a", + .emscripten => "../src/zig-out/lib/libraylib.a", else => @panic("Unsupported OS"), }); - exe.addIncludePath(.{ .path = "../src" }); - exe.addIncludePath(.{ .path = "../src/external" }); - exe.addIncludePath(.{ .path = "../src/external/glfw/include" }); + exe.addIncludePath("../src"); + exe.addIncludePath("../src/external"); + exe.addIncludePath("../src/external/glfw/include"); switch (target.getOsTag()) { .windows => { exe.linkSystemLibrary("winmm"); exe.linkSystemLibrary("gdi32"); exe.linkSystemLibrary("opengl32"); - exe.addIncludePath(.{ .path = "external/glfw/deps/mingw" }); + exe.addIncludePath("external/glfw/deps/mingw"); exe.defineCMacro("PLATFORM_DESKTOP", null); }, @@ -71,15 +71,11 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT }, } - const install_cmd = b.addInstallArtifact(exe, .{}); - - const run_cmd = b.addRunArtifact(exe); - run_cmd.step.dependOn(&install_cmd.step); - - const run_step = b.step(name, name); - run_step.dependOn(&run_cmd.step); - - all.dependOn(&install_cmd.step); + b.installArtifact(exe); + var run = b.addRunArtifact(exe); + run.cwd = module; + b.step(name, name).dependOn(&run.step); + all.dependOn(&exe.step); } return all; } diff --git a/src/build.zig b/src/build.zig index 3bb4f4213..27250f5ff 100644 --- a/src/build.zig +++ b/src/build.zig @@ -1,6 +1,6 @@ const std = @import("std"); -// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) +// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode, options: Options) *std.Build.CompileStep { const raylib_flags = &[_][]const u8{ "-std=gnu99", @@ -192,12 +192,12 @@ pub fn build(b: *std.Build) void { const lib = addRaylib(b, target, optimize, options); - lib.installHeader("raylib.h", "raylib.h"); - lib.installHeader("raymath.h", "raymath.h"); - lib.installHeader("rlgl.h", "rlgl.h"); + lib.installHeader("src/raylib.h", "raylib.h"); + lib.installHeader("src/raymath.h", "raymath.h"); + lib.installHeader("src/rlgl.h", "rlgl.h"); if (options.raygui) { - lib.installHeader("../../raygui/src/raygui.h", "raygui.h"); + lib.installHeader("../raygui/src/raygui.h", "raygui.h"); } b.installArtifact(lib); From f0d949f931e286773aa0d0b87af4f58214ef611b Mon Sep 17 00:00:00 2001 From: Murlocohol Date: Tue, 10 Oct 2023 02:59:09 -0400 Subject: [PATCH 0332/1350] Hotfix for Vector2LineAngle(), should probably be reviewed along with the rest of raylib angle functions to determine what coordinate system we want. (#3394) --- examples/others/raymath_vector_angle.c | 56 +++++++++++++++++--------- src/raymath.h | 5 ++- 2 files changed, 40 insertions(+), 21 deletions(-) diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c index b193648fe..ab8ccf571 100644 --- a/examples/others/raymath_vector_angle.c +++ b/examples/others/raymath_vector_angle.c @@ -2,7 +2,7 @@ * * raylib [shapes] example - Vector Angle * -* Example originally created with raylib 1.0, last time updated with raylib 4.2 +* Example originally created with raylib 1.0, last time updated with raylib 4.6 * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software @@ -10,7 +10,7 @@ * Copyright (c) 2023 Ramon Santamaria (@raysan5) * ********************************************************************************************/ - + #include "raylib.h" #include "raymath.h" @@ -28,7 +28,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [math] example - vector angle"); Vector2 v0 = { screenWidth/2, screenHeight/2 }; - Vector2 v1 = { 100.0f, 80.0f }; + Vector2 v1 = Vector2Add(v0, (Vector2){ 100.0f, 80.0f }); Vector2 v2 = { 0 }; // Updated with mouse position float angle = 0.0f; // Angle in degrees @@ -42,21 +42,29 @@ int main(void) { // Update //---------------------------------------------------------------------------------- + float startangle; + + if (angleMode == 0) startangle = -Vector2LineAngle(v0, v1)*RAD2DEG; + if (angleMode == 1) startangle = 0.0f; + + v2 = GetMousePosition(); + if (IsKeyPressed(KEY_SPACE)) angleMode = !angleMode; + if(angleMode == 0 && IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) v1 = GetMousePosition(); + if (angleMode == 0) { // Calculate angle between two vectors, considering a common origin (v0) - v1 = Vector2Add(v0, (Vector2){ 100.0f, 80.0f }); - v2 = GetMousePosition(); - angle = Vector2Angle(Vector2Normalize(Vector2Subtract(v1, v0)), Vector2Normalize(Vector2Subtract(v2, v0)))*RAD2DEG; + Vector2 v1Normal = Vector2Normalize(Vector2Subtract(v1, v0)); + Vector2 v2Normal = Vector2Normalize(Vector2Subtract(v2, v0)); + + angle = Vector2Angle(v1Normal, v2Normal)*RAD2DEG; } else if (angleMode == 1) { // Calculate angle defined by a two vectors line, in reference to horizontal line - v1 = (Vector2){ screenWidth/2, screenHeight/2 }; - v2 = GetMousePosition(); - angle = Vector2LineAngle(v1, v2)*RAD2DEG; + angle = Vector2LineAngle(v0, v2)*RAD2DEG; } //---------------------------------------------------------------------------------- @@ -66,32 +74,40 @@ int main(void) ClearBackground(RAYWHITE); - if (angleMode == 0) DrawText("v0", v0.x, v0.y, 10, DARKGRAY); - DrawText("v1", v1.x, v1.y, 10, DARKGRAY); - DrawText("v2", v2.x, v2.y, 10, DARKGRAY); - if (angleMode == 0) { - DrawText("MODE: Angle between V1 and V2", 10, 10, 20, BLACK); + DrawText("MODE 0: Angle between V1 and V2", 10, 10, 20, BLACK); + DrawText("Right Click to Move V2", 10, 30, 20, DARKGRAY); DrawLineEx(v0, v1, 2.0f, BLACK); DrawLineEx(v0, v2, 2.0f, RED); - float startangle = 90 - Vector2LineAngle(v0, v1)*RAD2DEG; - DrawCircleSector(v0, 40.0f, startangle, startangle + angle - 360.0f*(angle > 180.0f), 32, Fade(GREEN, 0.6f)); + DrawCircleSector(v0, 40.0f, startangle, startangle - angle, 32, Fade(GREEN, 0.6f)); } else if (angleMode == 1) { - DrawText("MODE: Angle formed by line V1 to V2", 10, 10, 20, BLACK); + DrawText("MODE 1: Angle formed by line V1 to V2", 10, 10, 20, BLACK); DrawLine(0, screenHeight/2, screenWidth, screenHeight/2, LIGHTGRAY); - DrawLineEx(v1, v2, 2.0f, RED); + DrawLineEx(v0, v2, 2.0f, RED); - DrawCircleSector(v1, 40.0f, 90.0f, 180 - angle - 90, 32, Fade(GREEN, 0.6f)); + DrawCircleSector(v0, 40.0f, startangle, startangle - angle, 32, Fade(GREEN, 0.6f)); } + DrawText("v0", v0.x, v0.y, 10, DARKGRAY); + + // If the line from v0 to v1 would overlap the text, move it's position up 10 + if (angleMode == 0 && Vector2Subtract(v0, v1).y > 0.0f) DrawText("v1", v1.x, v1.y-10.0f, 10, DARKGRAY); + if (angleMode == 0 && Vector2Subtract(v0, v1).y < 0.0f) DrawText("v1", v1.x, v1.y, 10, DARKGRAY); + + // If angle mode 1, use v1 to emphasize the horizontal line + if (angleMode == 1) DrawText("v1", v0.x + 40.0f, v0.y, 10, DARKGRAY); + + // position adjusted by -10 so it isn't hidden by cursor + DrawText("v2", v2.x-10.0f, v2.y-10.0f, 10, DARKGRAY); + DrawText("Press SPACE to change MODE", 460, 10, 20, DARKGRAY); - DrawText(TextFormat("ANGLE: %2.2f", angle), 10, 40, 20, LIME); + DrawText(TextFormat("ANGLE: %2.2f", angle), 10, 70, 20, LIME); EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/src/raymath.h b/src/raymath.h index db04c51ee..b01b0b22b 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -323,6 +323,8 @@ RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) float dot = v1.x*v2.x + v1.y*v2.y; float det = v1.x*v2.y - v1.y*v2.x; + + // TODO(10/9/2023): Currently angles move clockwise, determine if this is wanted behavior result = -atan2f(det, dot); return result; @@ -335,7 +337,8 @@ RMAPI float Vector2LineAngle(Vector2 start, Vector2 end) { float result = 0.0f; - result = atan2f(end.y - start.y, end.x - start.x); + // TODO(10/9/2023): Currently angles move clockwise, determine if this is wanted behavior + result = -atan2f(end.y - start.y, end.x - start.x); return result; } From 9702a17152d137766d85cd02060219787e3e228b Mon Sep 17 00:00:00 2001 From: Murlocohol Date: Tue, 10 Oct 2023 04:42:11 -0400 Subject: [PATCH 0333/1350] [raymath] Hotfix for Vector2Angle() and Vector2LineAngle() (#3396) * Hotfix for Vector2LineAngle(), should probably be reviewed along with the rest of raylib angle functions to determine what coordinate system we want. * Hotfix for Vector2LineAngle(), should probably be reviewed along with the rest of raylib angle functions to determine what coordinate system we want. * [raymath] Hotfix for Vector2Angle and corresponding example * [raymath] Hotfix for Vector2Angle and corresponding example --------- Co-authored-by: Ray --- examples/others/raymath_vector_angle.c | 10 +++++----- src/raymath.h | 3 +-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c index ab8ccf571..dc6887a41 100644 --- a/examples/others/raymath_vector_angle.c +++ b/examples/others/raymath_vector_angle.c @@ -44,7 +44,7 @@ int main(void) //---------------------------------------------------------------------------------- float startangle; - if (angleMode == 0) startangle = -Vector2LineAngle(v0, v1)*RAD2DEG; + if (angleMode == 0) startangle = Vector2LineAngle(v0, v1)*RAD2DEG; if (angleMode == 1) startangle = 0.0f; v2 = GetMousePosition(); @@ -81,8 +81,8 @@ int main(void) DrawLineEx(v0, v1, 2.0f, BLACK); DrawLineEx(v0, v2, 2.0f, RED); - - DrawCircleSector(v0, 40.0f, startangle, startangle - angle, 32, Fade(GREEN, 0.6f)); + + DrawCircleSector(v0, 40.0f, startangle, startangle + angle, 32, Fade(GREEN, 0.6f)); } else if (angleMode == 1) { @@ -90,8 +90,8 @@ int main(void) DrawLine(0, screenHeight/2, screenWidth, screenHeight/2, LIGHTGRAY); DrawLineEx(v0, v2, 2.0f, RED); - - DrawCircleSector(v0, 40.0f, startangle, startangle - angle, 32, Fade(GREEN, 0.6f)); + + DrawCircleSector(v0, 40.0f, startangle, startangle + angle, 32, Fade(GREEN, 0.6f)); } DrawText("v0", v0.x, v0.y, 10, DARKGRAY); diff --git a/src/raymath.h b/src/raymath.h index b01b0b22b..ff6017039 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -324,8 +324,7 @@ RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) float dot = v1.x*v2.x + v1.y*v2.y; float det = v1.x*v2.y - v1.y*v2.x; - // TODO(10/9/2023): Currently angles move clockwise, determine if this is wanted behavior - result = -atan2f(det, dot); + result = atan2f(det, dot); return result; } From cb571659565f4555fafd18108e369b706a83775e Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 10 Oct 2023 10:48:30 +0200 Subject: [PATCH 0334/1350] REVIEWED: Fix #3387 --- examples/others/raymath_vector_angle.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c index dc6887a41..ad573f54d 100644 --- a/examples/others/raymath_vector_angle.c +++ b/examples/others/raymath_vector_angle.c @@ -44,7 +44,7 @@ int main(void) //---------------------------------------------------------------------------------- float startangle; - if (angleMode == 0) startangle = Vector2LineAngle(v0, v1)*RAD2DEG; + if (angleMode == 0) startangle = -Vector2LineAngle(v0, v1)*RAD2DEG; if (angleMode == 1) startangle = 0.0f; v2 = GetMousePosition(); @@ -91,7 +91,7 @@ int main(void) DrawLine(0, screenHeight/2, screenWidth, screenHeight/2, LIGHTGRAY); DrawLineEx(v0, v2, 2.0f, RED); - DrawCircleSector(v0, 40.0f, startangle, startangle + angle, 32, Fade(GREEN, 0.6f)); + DrawCircleSector(v0, 40.0f, startangle, startangle - angle, 32, Fade(GREEN, 0.6f)); } DrawText("v0", v0.x, v0.y, 10, DARKGRAY); From 67a1e1ffaeb6d12004028ce50d687ffb4be1cbf7 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 10 Oct 2023 10:48:46 +0200 Subject: [PATCH 0335/1350] Update rtextures.c --- src/rtextures.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index dc7f4af05..8624bbd48 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -664,9 +664,9 @@ void UnloadImage(Image image) // NOTE: File format depends on fileName extension bool ExportImage(Image image, const char *fileName) { - int success = 0; + int result = 0; - if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return success; + if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return result; #if defined(SUPPORT_IMAGE_EXPORT) int channels = 4; @@ -689,21 +689,21 @@ bool ExportImage(Image image, const char *fileName) { int dataSize = 0; unsigned char *fileData = stbi_write_png_to_mem((const unsigned char *)imgData, image.width*channels, image.width, image.height, channels, &dataSize); - success = SaveFileData(fileName, fileData, dataSize); + result = SaveFileData(fileName, fileData, dataSize); RL_FREE(fileData); } #else if (false) { } #endif #if defined(SUPPORT_FILEFORMAT_BMP) - else if (IsFileExtension(fileName, ".bmp")) success = stbi_write_bmp(fileName, image.width, image.height, channels, imgData); + else if (IsFileExtension(fileName, ".bmp")) result = stbi_write_bmp(fileName, image.width, image.height, channels, imgData); #endif #if defined(SUPPORT_FILEFORMAT_TGA) - else if (IsFileExtension(fileName, ".tga")) success = stbi_write_tga(fileName, image.width, image.height, channels, imgData); + else if (IsFileExtension(fileName, ".tga")) result = stbi_write_tga(fileName, image.width, image.height, channels, imgData); #endif #if defined(SUPPORT_FILEFORMAT_JPG) else if (IsFileExtension(fileName, ".jpg") || - IsFileExtension(fileName, ".jpeg")) success = stbi_write_jpg(fileName, image.width, image.height, channels, imgData, 90); // JPG quality: between 1 and 100 + IsFileExtension(fileName, ".jpeg")) result = stbi_write_jpg(fileName, image.width, image.height, channels, imgData, 90); // JPG quality: between 1 and 100 #endif #if defined(SUPPORT_FILEFORMAT_QOI) else if (IsFileExtension(fileName, ".qoi")) @@ -721,30 +721,30 @@ bool ExportImage(Image image, const char *fileName) desc.channels = channels; desc.colorspace = QOI_SRGB; - success = qoi_write(fileName, imgData, &desc); + result = qoi_write(fileName, imgData, &desc); } } #endif #if defined(SUPPORT_FILEFORMAT_KTX) else if (IsFileExtension(fileName, ".ktx")) { - success = rl_save_ktx(fileName, image.data, image.width, image.height, image.format, image.mipmaps); + result = rl_save_ktx(fileName, image.data, image.width, image.height, image.format, image.mipmaps); } #endif else if (IsFileExtension(fileName, ".raw")) { // Export raw pixel data (without header) // NOTE: It's up to the user to track image parameters - success = SaveFileData(fileName, image.data, GetPixelDataSize(image.width, image.height, image.format)); + result = SaveFileData(fileName, image.data, GetPixelDataSize(image.width, image.height, image.format)); } if (allocatedData) RL_FREE(imgData); #endif // SUPPORT_IMAGE_EXPORT - if (success != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image exported successfully", fileName); + if (result != 0) TRACELOG(LOG_INFO, "FILEIO: [%s] Image exported successfully", fileName); else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to export image", fileName); - return success; + return result; } // Export image to memory buffer From b94e6290a4e5e96196e23d66a480cdf9d707c380 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 10 Oct 2023 10:50:09 +0200 Subject: [PATCH 0336/1350] Added some comments and tweaks #3313 --- src/rcore_android.c | 3 ++- src/rcore_custom.c | 66 ++++++++++++++++++++++++++++++++++++++++----- src/rcore_desktop.c | 32 +++++++++++----------- src/rcore_drm.c | 26 +++++++++--------- src/rcore_web.c | 21 ++++++++------- 5 files changed, 103 insertions(+), 45 deletions(-) diff --git a/src/rcore_android.c b/src/rcore_android.c index a3db7e5fa..6b43b78b4 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -228,7 +228,7 @@ void InitWindow(int width, int height, const char *title) // Initialize base path for storage CORE.Storage.basePath = platform.app->activity->internalDataPath; - TRACELOG(LOG_INFO, "ANDROID: App initialized successfully"); + TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Application initialized successfully"); // Android ALooper_pollAll() variables int pollResult = 0; @@ -247,6 +247,7 @@ void InitWindow(int width, int height, const char *title) //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true; } } + //-------------------------------------------------------------- } // Close window and unload OpenGL context diff --git a/src/rcore_custom.c b/src/rcore_custom.c index 799c6054b..933f222fb 100644 --- a/src/rcore_custom.c +++ b/src/rcore_custom.c @@ -135,16 +135,70 @@ void InitWindow(int width, int height, const char *title) CORE.Window.currentFbo.width = width; CORE.Window.currentFbo.height = height; - // TODO: Initialize window/display system + // Initialize graphics device + // NOTE: returns true if window and graphic device has been initialized successfully + CORE.Window.ready = InitGraphicsDevice(width, height); - // TODO: Initialize input events system + // If graphic device is no properly initialized, we end program + if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } + + // Initialize hi-res timer + InitTimer(); - // TODO: Initialize assets manager + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); - // TODO: Initialize base path for storage - //CORE.Storage.basePath = platform.app->activity->internalDataPath; + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + // Load default font + // WARNING: External function: Module required: rtext + LoadFontDefault(); + #if defined(SUPPORT_MODULE_RSHAPES) + // Set font white rectangle for shapes drawing, so shapes and text can be batched together + // WARNING: rshapes module is required, if not available, default internal white rectangle is used + Rectangle rec = GetFontDefault().recs[95]; + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); + } + else + { + // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); + } + #endif +#else + #if defined(SUPPORT_MODULE_RSHAPES) + // Set default texture and rectangle to be used for shapes drawing + // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 + Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes + #endif +#endif +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // Set default font texture filter for HighDPI (blurry) + // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); + } +#endif - TRACELOG(LOG_INFO, "PLATFORM: Application initialized successfully"); +#if defined(SUPPORT_EVENTS_AUTOMATION) + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + CORE.Time.frameCounter = 0; +#endif + + // TODO: Platform specific init window + //-------------------------------------------------------------- + // ... + //-------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Application initialized successfully"); } // Close window and unload OpenGL context diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 2cf509507..4fd86d93e 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -186,23 +186,19 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN CORE.Window.eventWaiting = false; - // Initialize graphics device (display device and OpenGL context) + // Initialize graphics device // NOTE: returns true if window and graphic device has been initialized successfully CORE.Window.ready = InitGraphicsDevice(width, height); // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) - { - TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); - return; - } + if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); // Initialize hi-res timer InitTimer(); // Initialize random seed - srand((unsigned int)time(NULL)); + SetRandomSeed((unsigned int)time(NULL)); // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); @@ -248,6 +244,8 @@ void InitWindow(int width, int height, const char *title) events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); CORE.Time.frameCounter = 0; #endif + + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP: Application initialized successfully"); } // Close window and unload OpenGL context @@ -834,10 +832,12 @@ void SetWindowMinSize(int width, int height) { CORE.Window.screenMin.width = width; CORE.Window.screenMin.height = height; - int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.width; - int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.height; - int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.width; - int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.height; + + int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.width; + int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.height; + int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.width; + int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.height; + glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight); } @@ -846,10 +846,12 @@ void SetWindowMaxSize(int width, int height) { CORE.Window.screenMax.width = width; CORE.Window.screenMax.height = height; - int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.width; - int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMin.height; - int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.width; - int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : CORE.Window.screenMax.height; + + int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.width; + int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.height; + int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.width; + int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.height; + glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight); } diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 4a0d805f8..c7823f820 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -220,19 +220,14 @@ void InitWindow(int width, int height, const char *title) CORE.Window.ready = InitGraphicsDevice(width, height); // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) - { - TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); - return; - } - else - SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); + if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } + else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); // Initialize hi-res timer InitTimer(); // Initialize random seed - srand((unsigned int)time(NULL)); + SetRandomSeed((unsigned int)time(NULL)); // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); @@ -274,15 +269,20 @@ void InitWindow(int width, int height, const char *title) } #endif - // Initialize raw input system - InitEvdevInput(); // Evdev inputs initialization - InitGamepad(); // Gamepad init - InitKeyboard(); // Keyboard init (stdin) - #if defined(SUPPORT_EVENTS_AUTOMATION) events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); CORE.Time.frameCounter = 0; #endif + + // Platform specific init window + //-------------------------------------------------------------- + // Initialize raw input system + InitEvdevInput(); // Evdev inputs initialization + InitGamepad(); // Gamepad init + InitKeyboard(); // Keyboard init (stdin) + //-------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: DRM: Application initialized successfully"); } // Close window and unload OpenGL context diff --git a/src/rcore_web.c b/src/rcore_web.c index f4f412f2a..83acae02f 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -176,18 +176,14 @@ void InitWindow(int width, int height, const char *title) CORE.Window.ready = InitGraphicsDevice(width, height); // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) - { - TRACELOG(LOG_FATAL, "Failed to initialize Graphic Device"); - return; - } + if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); // Initialize hi-res timer InitTimer(); // Initialize random seed - srand((unsigned int)time(NULL)); + SetRandomSeed((unsigned int)time(NULL)); // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); @@ -229,6 +225,13 @@ void InitWindow(int width, int height, const char *title) } #endif +#if defined(SUPPORT_EVENTS_AUTOMATION) + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + CORE.Time.frameCounter = 0; +#endif + + // Platform specific init window + //-------------------------------------------------------------- // Setup callback functions for the DOM events emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); @@ -257,11 +260,9 @@ void InitWindow(int width, int height, const char *title) // Support gamepad events (not provided by GLFW3 on emscripten) emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); + //-------------------------------------------------------------- -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; -#endif + TRACELOG(LOG_INFO, "PLATFORM: WEB: Application initialized successfully"); } // Close window and unload OpenGL context From 101a9b04458a38413a29a11f22e6c8e0a472a75b Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 10 Oct 2023 11:59:41 +0200 Subject: [PATCH 0337/1350] Added comments and review some functions #3313 --- src/raylib.h | 10 +- src/rcore.c | 244 ++++++++++++++++++++++++++++---------------- src/rcore_android.c | 9 +- src/rcore_custom.c | 21 ++-- src/rcore_desktop.c | 29 +++--- src/rcore_drm.c | 8 +- src/rcore_web.c | 13 +-- 7 files changed, 191 insertions(+), 143 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 5fa66bd03..5d63d4b62 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1058,15 +1058,16 @@ RLAPI int GetRandomValue(int min, int max); // Get a rando RLAPI void SetRandomSeed(unsigned int seed); // Set the seed for the random number generator RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (filename extension defines format) RLAPI void SetConfigFlags(unsigned int flags); // Setup init configuration flags (view FLAGS) +RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available) +// NOTE: Following functions implemented in module [utils] +//------------------------------------------------------------------ RLAPI void TraceLog(int logLevel, const char *text, ...); // Show trace log messages (LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR...) RLAPI void SetTraceLogLevel(int logLevel); // Set the current threshold (minimum) log level RLAPI void *MemAlloc(unsigned int size); // Internal memory allocator RLAPI void *MemRealloc(void *ptr, unsigned int size); // Internal memory reallocator RLAPI void MemFree(void *ptr); // Internal memory free -RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available) - // Set custom callbacks // WARNING: Callbacks setup is intended for advance users RLAPI void SetTraceLogCallback(TraceLogCallback callback); // Set custom trace log @@ -1083,6 +1084,9 @@ RLAPI bool ExportDataAsCode(const unsigned char *data, int dataSize, const char RLAPI char *LoadFileText(const char *fileName); // Load text data from file (read), returns a '\0' terminated string RLAPI void UnloadFileText(char *text); // Unload file text data allocated by LoadFileText() RLAPI bool SaveFileText(const char *fileName, char *text); // Save text data to file (write), string must be '\0' terminated, returns true on success +//------------------------------------------------------------------ + +// File system functions RLAPI bool FileExists(const char *fileName); // Check if file exists RLAPI bool DirectoryExists(const char *dirPath); // Check if a directory path exists RLAPI bool IsFileExtension(const char *fileName, const char *ext); // Check file extension (including point: .png, .wav) @@ -1120,9 +1124,9 @@ RLAPI bool IsKeyPressedRepeat(int key); // Check if a key RLAPI bool IsKeyDown(int key); // Check if a key is being pressed RLAPI bool IsKeyReleased(int key); // Check if a key has been released once RLAPI bool IsKeyUp(int key); // Check if a key is NOT being pressed -RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC) RLAPI int GetKeyPressed(void); // Get key pressed (keycode), call it multiple times for keys queued, returns 0 when the queue is empty RLAPI int GetCharPressed(void); // Get char pressed (unicode), call it multiple times for chars queued, returns 0 when the queue is empty +RLAPI void SetExitKey(int key); // Set a custom key to exit program (default is ESC) // Input-related functions: gamepads RLAPI bool IsGamepadAvailable(int gamepad); // Check if a gamepad is available diff --git a/src/rcore.c b/src/rcore.c index 29881198a..fa8f50d86 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -315,14 +315,12 @@ const char *TextFormat(const char *text, ...); // Formatting of text with #endif //---------------------------------------------------------------------------------- -// Module Functions Definition - Window and OpenGL Context Functions +// Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- -// NOTE: Multiple window/display/monitor management functions have been moved to platform-specific modules - -// Platform-specific functions: -//void InitWindow(int width, int height, const char *title); -//void CloseWindow(void); +// NOTE: Functions with a platform-specific implementation on rcore_.c +//void InitWindow(int width, int height, const char *title) +//void CloseWindow(void) //bool WindowShouldClose(void) //bool IsWindowHidden(void) //bool IsWindowMinimized(void) @@ -364,9 +362,6 @@ const char *TextFormat(const char *text, ...); // Formatting of text with //void HideCursor(void) //void EnableCursor(void) //void DisableCursor(void) -//double GetTime(void) -//void TakeScreenshot(const char *fileName) -//void OpenURL(const char *url) // Check if window has been initialized successfully @@ -435,6 +430,19 @@ bool IsCursorOnScreen(void) return CORE.Input.Mouse.cursorOnScreen; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Custom frame control +//---------------------------------------------------------------------------------- + +// NOTE: Functions with a platform-specific implementation on rcore_.c +//void SwapScreenBuffer(void); +//void PollInputEvents(void); +//void WaitTime(double seconds); + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Screen Drawing +//---------------------------------------------------------------------------------- + // Set background color (framebuffer clear color) void ClearBackground(Color color) { @@ -741,6 +749,10 @@ void EndScissorMode(void) rlDisableScissorTest(); } +//---------------------------------------------------------------------------------- +// Module Functions Definition: VR Stereo Rendering +//---------------------------------------------------------------------------------- + // Begin VR drawing configuration void BeginVrStereoMode(VrStereoConfig config) { @@ -837,6 +849,10 @@ void UnloadVrStereoConfig(VrStereoConfig config) TRACELOG(LOG_INFO, "UnloadVrStereoConfig not implemented in rcore.c"); } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Shaders Management +//---------------------------------------------------------------------------------- + // Load shader from files and bind default locations // NOTE: If shader string is NULL, using default vertex/fragment shaders Shader LoadShader(const char *vsFileName, const char *fsFileName) @@ -1002,6 +1018,10 @@ void SetShaderValueTexture(Shader shader, int locIndex, Texture2D texture) } } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Screen-space Queries +//---------------------------------------------------------------------------------- + // Get a ray trace from mouse position Ray GetMouseRay(Vector2 mouse, Camera camera) { @@ -1161,6 +1181,13 @@ Vector2 GetScreenToWorld2D(Vector2 position, Camera2D camera) return (Vector2){ transform.x, transform.y }; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Timming +//---------------------------------------------------------------------------------- + +// NOTE: Functions with a platform-specific implementation on rcore_.c +//double GetTime(void) + // Set target FPS (maximum) void SetTargetFPS(int fps) { @@ -1209,6 +1236,63 @@ float GetFrameTime(void) return (float)CORE.Time.frame; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + +// NOTE: Functions with a platform-specific implementation on rcore_.c +//void OpenURL(const char *url) + +// Get a random value between min and max (both included) +// WARNING: Ranges higher than RAND_MAX will return invalid results +// More specifically, if (max - min) > INT_MAX there will be an overflow, +// and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold +int GetRandomValue(int min, int max) +{ + if (min > max) + { + int tmp = max; + max = min; + min = tmp; + } + + if ((unsigned int)(max - min) > (unsigned int)RAND_MAX) + { + TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); + } + + return (rand()%(abs(max - min) + 1) + min); +} + +// Set the seed for the random number generator +void SetRandomSeed(unsigned int seed) +{ + srand(seed); +} + +// Takes a screenshot of current screen (saved a .png) +void TakeScreenshot(const char *fileName) +{ +#if defined(SUPPORT_MODULE_RTEXTURES) + // Security check to (partially) avoid malicious code + if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } + + Vector2 scale = GetWindowScaleDPI(); + unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); + Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + + char path[512] = { 0 }; + strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); + + ExportImage(image, path); // WARNING: Module required: rtextures + RL_FREE(imgData); + + TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); +#else + TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); +#endif +} + // Setup window configuration flags (view FLAGS) // NOTE: This function is expected to be called before window creation, // because it sets up some flags for the window creation process. @@ -1221,7 +1305,7 @@ void SetConfigFlags(unsigned int flags) } //---------------------------------------------------------------------------------- -// Module Functions Definition: FileSystem +// Module Functions Definition: File system //---------------------------------------------------------------------------------- // Check if the file exists @@ -1669,36 +1753,9 @@ long GetFileModTime(const char *fileName) } //---------------------------------------------------------------------------------- -// Module Functions Definition: Misc +// Module Functions Definition: Compression and Encoding //---------------------------------------------------------------------------------- -// Get a random value between min and max (both included) -// WARNING: Ranges higher than RAND_MAX will return invalid results -// More specifically, if (max - min) > INT_MAX there will be an overflow, -// and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold -int GetRandomValue(int min, int max) -{ - if (min > max) - { - int tmp = max; - max = min; - min = tmp; - } - - if ((unsigned int)(max - min) > (unsigned int)RAND_MAX) - { - TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); - } - - return (rand()%(abs(max - min) + 1) + min); -} - -// Set the seed for the random number generator -void SetRandomSeed(unsigned int seed) -{ - srand(seed); -} - // Compress data (DEFLATE algorithm) unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDataSize) { @@ -1841,26 +1898,9 @@ unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize) } //---------------------------------------------------------------------------------- -// Module Functions Definition: Inputs +// Module Functions Definition: Input Handling: Keyboard //---------------------------------------------------------------------------------- -// Platform-specific functions -//void SetExitKey(int key) -//const char *GetGamepadName(int gamepad) -//int GetGamepadAxisCount(int gamepad) -//int SetGamepadMappings(const char *mappings) -//int GetMouseX(void) -//int GetMouseY(void) -//Vector2 GetMousePosition(void) -//void SetMousePosition(int x, int y) -//float GetMouseWheelMove(void) -//void SetMouseCursor(int cursor) -//int GetTouchX(void) -//int GetTouchY(void) -//Vector2 GetTouchPosition(int index) -//void SwapScreenBuffer(void) -//void PollInputEvents(void) - // Check if a key has been pressed once bool IsKeyPressed(int key) { @@ -1971,6 +2011,22 @@ int GetCharPressed(void) return value; } +// Set a custom key to exit program +// NOTE: default exitKey is set to ESCAPE +void SetExitKey(int key) +{ + CORE.Input.Keyboard.exitKey = key; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Input Handling: Gamepad +//---------------------------------------------------------------------------------- + +// NOTE: Functions with a platform-specific implementation on rcore_.c +//int GetGamepadAxisCount(int gamepad) ** +//const char *GetGamepadName(int gamepad) ** +//int SetGamepadMappings(const char *mappings) + // Check if a gamepad is available bool IsGamepadAvailable(int gamepad) { @@ -1981,16 +2037,11 @@ bool IsGamepadAvailable(int gamepad) return result; } -// Get axis movement vector for a gamepad -float GetGamepadAxisMovement(int gamepad, int axis) -{ - float value = 0; - - if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXIS) && - (fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]) > 0.1f)) value = CORE.Input.Gamepad.axisState[gamepad][axis]; // 0.1f = GAMEPAD_AXIS_MINIMUM_DRIFT/DELTA - - return value; -} +// Get gamepad internal name id +//const char *GetGamepadName(int gamepad) +//{ +// return CORE.Input.Gamepad.ready[gamepad]; +//} // Check if a gamepad button has been pressed once bool IsGamepadButtonPressed(int gamepad, int button) @@ -2042,6 +2093,35 @@ int GetGamepadButtonPressed(void) return CORE.Input.Gamepad.lastButtonPressed; } +// Get gamepad axis count +//int GetGamepadAxisCount(int gamepad) +//{ +// return CORE.Input.Gamepad.axisCount; +//} + +// Get axis movement vector for a gamepad +float GetGamepadAxisMovement(int gamepad, int axis) +{ + float value = 0; + + if ((gamepad < MAX_GAMEPADS) && CORE.Input.Gamepad.ready[gamepad] && (axis < MAX_GAMEPAD_AXIS) && + (fabsf(CORE.Input.Gamepad.axisState[gamepad][axis]) > 0.1f)) value = CORE.Input.Gamepad.axisState[gamepad][axis]; // 0.1f = GAMEPAD_AXIS_MINIMUM_DRIFT/DELTA + + return value; +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Input Handling: Mouse +//---------------------------------------------------------------------------------- + +// NOTE: Functions with a platform-specific implementation on rcore_.c +//int GetMouseX(void) ** +//int GetMouseY(void) ** +//Vector2 GetMousePosition(void) ** +//void SetMousePosition(int x, int y) +//float GetMouseWheelMove(void) ** +//void SetMouseCursor(int cursor) + // Check if a mouse button has been pressed once bool IsMouseButtonPressed(int button) { @@ -2129,6 +2209,15 @@ Vector2 GetMouseWheelMoveV(void) return result; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Input Handling: Touch +//---------------------------------------------------------------------------------- + +// NOTE: Functions with a platform-specific implementation on rcore_.c +//int GetTouchX(void) +//int GetTouchY(void) +//Vector2 GetTouchPosition(int index) + // Get touch point identifier for given index int GetTouchPointId(int index) { @@ -2329,29 +2418,6 @@ void WaitTime(double seconds) #endif } -// Takes a screenshot of current screen (saved a .png) -void TakeScreenshot(const char *fileName) -{ -#if defined(SUPPORT_MODULE_RTEXTURES) - // Security check to (partially) avoid malicious code - if (strchr(fileName, '\'') != NULL) { TRACELOG(LOG_WARNING, "SYSTEM: Provided fileName could be potentially malicious, avoid [\'] character"); return; } - - Vector2 scale = GetWindowScaleDPI(); - unsigned char *imgData = rlReadScreenPixels((int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); - Image image = { imgData, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y), 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - - char path[512] = { 0 }; - strcpy(path, TextFormat("%s/%s", CORE.Storage.basePath, fileName)); - - ExportImage(image, path); // WARNING: Module required: rtextures - RL_FREE(imgData); - - TRACELOG(LOG_INFO, "SYSTEM: [%s] Screenshot taken successfully", path); -#else - TRACELOG(LOG_WARNING,"IMAGE: ExportImage() requires module: rtextures"); -#endif -} - // Scan all files and directories in a base path // WARNING: files.paths[] must be previously allocated and // contain enough space to store all required paths diff --git a/src/rcore_android.c b/src/rcore_android.c index 6b43b78b4..a9fc5fb9b 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -635,17 +635,10 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Set a custom key to exit program -void SetExitKey(int key) -{ - TRACELOG(LOG_WARNING, "SetExitKey() not implemented on target platform"); -} - // Get gamepad internal name id const char *GetGamepadName(int gamepad) { - TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on target platform"); - return NULL; + return CORE.Input.Gamepad.name[gamepad]; } // Get gamepad axis count diff --git a/src/rcore_custom.c b/src/rcore_custom.c index 933f222fb..e17aa96b1 100644 --- a/src/rcore_custom.c +++ b/src/rcore_custom.c @@ -563,17 +563,10 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Set a custom key to exit program -void SetExitKey(int key) -{ - TRACELOG(LOG_WARNING, "SetExitKey() not implemented on target platform"); -} - // Get gamepad internal name id const char *GetGamepadName(int gamepad) { - TRACELOG(LOG_WARNING, "GetGamepadName() not implemented on target platform"); - return NULL; + return CORE.Input.Gamepad.name[gamepad]; } // Get gamepad axis count @@ -592,19 +585,25 @@ int SetGamepadMappings(const char *mappings) // Get mouse position X int GetMouseX(void) { - return (int)CORE.Input.Touch.position[0].x; + return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); } // Get mouse position Y int GetMouseY(void) { - return (int)CORE.Input.Touch.position[0].y; + return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); } // Get mouse position XY Vector2 GetMousePosition(void) { - return GetTouchPosition(0); + Vector2 position = { 0 }; + + // NOTE: On canvas scaling, mouse position is proportionally returned + position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; + position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; + + return position; } // Set mouse position XY diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 4fd86d93e..c410b713b 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -130,6 +130,7 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); // GLFW3 Cursor Position Callback, runs on mouse move static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area +static void JoystickCallback(int jid, int event); // GLFW3 Joystick Connected/Disconnected Callback //---------------------------------------------------------------------------------- // Module Functions Declaration @@ -1221,21 +1222,10 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Set a custom key to exit program -// NOTE: default exitKey is ESCAPE -void SetExitKey(int key) -{ - CORE.Input.Keyboard.exitKey = key; -} - // Get gamepad internal name id const char *GetGamepadName(int gamepad) { - const char *name = NULL; - - if (CORE.Input.Gamepad.ready[gamepad]) name = glfwGetJoystickName(gamepad); - - return name; + return CORE.Input.Gamepad.name[gamepad]; } // Get gamepad axis count @@ -1731,6 +1721,7 @@ static bool InitGraphicsDevice(int width, int height) glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes glfwSetScrollCallback(platform.handle, MouseScrollCallback); glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); + glfwSetJoystickCallback(JoystickCallback); glfwMakeContextCurrent(platform.handle); @@ -2066,5 +2057,17 @@ static void CursorEnterCallback(GLFWwindow *window, int enter) else CORE.Input.Mouse.cursorOnScreen = false; } -// EOF +// GLFW3 Joystick Connected/Disconnected Callback +static void JoystickCallback(int jid, int event) +{ + if (event == GLFW_CONNECTED) + { + strcpy(CORE.Input.Gamepad.name[jid], glfwGetJoystickName(jid)); + } + else if (event == GLFW_DISCONNECTED) + { + memset(CORE.Input.Gamepad.name[jid], 0, 64); + } +} +// EOF diff --git a/src/rcore_drm.c b/src/rcore_drm.c index c7823f820..ccf44feb3 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -739,13 +739,6 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Set a custom key to exit program -// NOTE: default exitKey is ESCAPE -void SetExitKey(int key) -{ - CORE.Input.Keyboard.exitKey = key; -} - // Get gamepad internal name id const char *GetGamepadName(int gamepad) { @@ -764,6 +757,7 @@ const char *GetGamepadName(int gamepad) int GetGamepadAxisCount(int gamepad) { int axisCount = 0; + if (CORE.Input.Gamepad.ready[gamepad]) ioctl(platform.gamepadStreamFd[gamepad], JSIOCGAXES, &axisCount); CORE.Input.Gamepad.axisCount = axisCount; diff --git a/src/rcore_web.c b/src/rcore_web.c index 83acae02f..ed7dc2657 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -697,21 +697,10 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Set a custom key to exit program -// NOTE: default exitKey is ESCAPE -void SetExitKey(int key) -{ - CORE.Input.Keyboard.exitKey = key; -} - // Get gamepad internal name id const char *GetGamepadName(int gamepad) { - const char *name = NULL; - - name = CORE.Input.Gamepad.name[gamepad]; - - return name; + return CORE.Input.Gamepad.name[gamepad]; } // Get gamepad axis count From daba1a27945d6be914e71f71fe8e7cfaef1eca5e Mon Sep 17 00:00:00 2001 From: MichaelFiber <42419558+michaelfiber@users.noreply.github.com> Date: Wed, 11 Oct 2023 04:30:51 -0400 Subject: [PATCH 0338/1350] Split drm update input (#3397) * Update `PLATFORM_DRM` implementation of `GetGamepadAxisCount` * Update * Update `PLATFORM_DRM` implementation of `GetGamepadName` * Add example to test gamepad info functions Fix typo * Update new gamepad info example * Move axis count update out of GamepadThread - race condition * Remove pointless if statement --- examples/Makefile | 1 + examples/core/core_input_gamepad_info.c | 60 +++++++++++++++++++++++++ src/rcore_drm.c | 18 ++------ 3 files changed, 65 insertions(+), 14 deletions(-) create mode 100644 examples/core/core_input_gamepad_info.c diff --git a/examples/Makefile b/examples/Makefile index 9404d39cc..6031f05e9 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -365,6 +365,7 @@ CORE = \ core/core_input_mouse \ core/core_input_mouse_wheel \ core/core_input_gamepad \ + core/core_input_gamepad_info \ core/core_input_multitouch \ core/core_input_gestures \ core/core_input_gestures_web \ diff --git a/examples/core/core_input_gamepad_info.c b/examples/core/core_input_gamepad_info.c new file mode 100644 index 000000000..84a687cd8 --- /dev/null +++ b/examples/core/core_input_gamepad_info.c @@ -0,0 +1,60 @@ +/******************************************************************************************* +* +* raylib [core] example - Gamepad information +* +* NOTE: This example requires a Gamepad connected to the system +* Check raylib.h for buttons configuration +* +* Example originally created with raylib 4.6, last time updated with raylib 4.6 +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + SetConfigFlags(FLAG_MSAA_4X_HINT); // Set MSAA 4X hint before windows creation + + InitWindow(screenWidth, screenHeight, "raylib [core] example - gamepad information"); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + int y = 10; + + BeginDrawing(); + + ClearBackground(RAYWHITE); + + for (int i = 0; i < 4; i++) // by default rcore.h has a MAX_GAMEPADS of 4 so mimmic that here. + { + if (IsGamepadAvailable(i)) + { + DrawText(TextFormat("Gamepad:\n\tName: %s\n\tAxes: %d", GetGamepadName(i), GetGamepadAxisCount(i)), 10, y, 20, BLACK); + y += 40; + } + } + + EndDrawing(); + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- +} \ No newline at end of file diff --git a/src/rcore_drm.c b/src/rcore_drm.c index ccf44feb3..3b688b8a1 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -742,25 +742,12 @@ void OpenURL(const char *url) // Get gamepad internal name id const char *GetGamepadName(int gamepad) { - const char *name = NULL; - - if (CORE.Input.Gamepad.ready[gamepad]) - { - ioctl(platform.gamepadStreamFd[gamepad], JSIOCGNAME(64), &CORE.Input.Gamepad.name[gamepad]); - name = CORE.Input.Gamepad.name[gamepad]; - } - - return name; + return CORE.Input.Gamepad.name[gamepad]; } // Get gamepad axis count int GetGamepadAxisCount(int gamepad) { - int axisCount = 0; - - if (CORE.Input.Gamepad.ready[gamepad]) ioctl(platform.gamepadStreamFd[gamepad], JSIOCGAXES, &axisCount); - CORE.Input.Gamepad.axisCount = axisCount; - return CORE.Input.Gamepad.axisCount; } @@ -1959,6 +1946,9 @@ static void InitGamepad(void) if (error != 0) TRACELOG(LOG_WARNING, "RPI: Failed to create gamepad input event thread"); else TRACELOG(LOG_INFO, "RPI: Gamepad device initialized successfully"); } + + ioctl(platform.gamepadStreamFd[i], JSIOCGNAME(64), &CORE.Input.Gamepad.name[i]); + ioctl(platform.gamepadStreamFd[i], JSIOCGAXES, &CORE.Input.Gamepad.axisCount); } } } From ddca5251321ba5542fe59369765b48c1d4c6e5c9 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Oct 2023 11:11:09 +0200 Subject: [PATCH 0339/1350] RENAMED: `rcore_custom` to `rcore_template` --- src/{rcore_custom.c => rcore_template.c} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/{rcore_custom.c => rcore_template.c} (99%) diff --git a/src/rcore_custom.c b/src/rcore_template.c similarity index 99% rename from src/rcore_custom.c rename to src/rcore_template.c index e17aa96b1..3007e4f54 100644 --- a/src/rcore_custom.c +++ b/src/rcore_template.c @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rcore_ - Functions to manage window, graphics device and inputs +* rcore_ template - Functions to manage window, graphics device and inputs * * PLATFORM: * - TODO: Define the target platform for the core From 6ebfec99c50ef0b00bb693ac25ddebc01457153a Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Oct 2023 11:14:03 +0200 Subject: [PATCH 0340/1350] Added gamepad functions as generic for all platforms --- src/rcore.c | 18 ++++++++---------- src/rcore_android.c | 12 ------------ src/rcore_desktop.c | 12 ------------ src/rcore_drm.c | 12 ------------ src/rcore_template.c | 12 ------------ src/rcore_web.c | 12 ------------ 6 files changed, 8 insertions(+), 70 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index fa8f50d86..2e6d32132 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2023,8 +2023,6 @@ void SetExitKey(int key) //---------------------------------------------------------------------------------- // NOTE: Functions with a platform-specific implementation on rcore_.c -//int GetGamepadAxisCount(int gamepad) ** -//const char *GetGamepadName(int gamepad) ** //int SetGamepadMappings(const char *mappings) // Check if a gamepad is available @@ -2038,10 +2036,10 @@ bool IsGamepadAvailable(int gamepad) } // Get gamepad internal name id -//const char *GetGamepadName(int gamepad) -//{ -// return CORE.Input.Gamepad.ready[gamepad]; -//} +const char *GetGamepadName(int gamepad) +{ + return CORE.Input.Gamepad.name[gamepad]; +} // Check if a gamepad button has been pressed once bool IsGamepadButtonPressed(int gamepad, int button) @@ -2094,10 +2092,10 @@ int GetGamepadButtonPressed(void) } // Get gamepad axis count -//int GetGamepadAxisCount(int gamepad) -//{ -// return CORE.Input.Gamepad.axisCount; -//} +int GetGamepadAxisCount(int gamepad) +{ + return CORE.Input.Gamepad.axisCount; +} // Get axis movement vector for a gamepad float GetGamepadAxisMovement(int gamepad, int axis) diff --git a/src/rcore_android.c b/src/rcore_android.c index a9fc5fb9b..d9f72894a 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -635,18 +635,6 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Get gamepad internal name id -const char *GetGamepadName(int gamepad) -{ - return CORE.Input.Gamepad.name[gamepad]; -} - -// Get gamepad axis count -int GetGamepadAxisCount(int gamepad) -{ - return CORE.Input.Gamepad.axisCount; -} - // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index c410b713b..eaef9517b 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -1222,18 +1222,6 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Get gamepad internal name id -const char *GetGamepadName(int gamepad) -{ - return CORE.Input.Gamepad.name[gamepad]; -} - -// Get gamepad axis count -int GetGamepadAxisCount(int gamepad) -{ - return CORE.Input.Gamepad.axisCount; -} - // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 3b688b8a1..13d41bbbd 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -739,18 +739,6 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Get gamepad internal name id -const char *GetGamepadName(int gamepad) -{ - return CORE.Input.Gamepad.name[gamepad]; -} - -// Get gamepad axis count -int GetGamepadAxisCount(int gamepad) -{ - return CORE.Input.Gamepad.axisCount; -} - // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { diff --git a/src/rcore_template.c b/src/rcore_template.c index 3007e4f54..68a43b2cb 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -563,18 +563,6 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Get gamepad internal name id -const char *GetGamepadName(int gamepad) -{ - return CORE.Input.Gamepad.name[gamepad]; -} - -// Get gamepad axis count -int GetGamepadAxisCount(int gamepad) -{ - return CORE.Input.Gamepad.axisCount; -} - // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { diff --git a/src/rcore_web.c b/src/rcore_web.c index ed7dc2657..00691e430 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -697,18 +697,6 @@ void OpenURL(const char *url) // Module Functions Definition: Inputs //---------------------------------------------------------------------------------- -// Get gamepad internal name id -const char *GetGamepadName(int gamepad) -{ - return CORE.Input.Gamepad.name[gamepad]; -} - -// Get gamepad axis count -int GetGamepadAxisCount(int gamepad) -{ - return CORE.Input.Gamepad.axisCount; -} - // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { From 0d175a69ae1e7072e05587b5f7505bac0c07b4ce Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Oct 2023 11:36:44 +0200 Subject: [PATCH 0341/1350] REVIEWED: Mouse and Touch functions generic to all platforms #3313 --- src/rcore.c | 65 ++++++++++++++++++++++++++++++++++++------ src/rcore_android.c | 53 +++-------------------------------- src/rcore_desktop.c | 67 +++++--------------------------------------- src/rcore_drm.c | 57 ------------------------------------- src/rcore_template.c | 55 ------------------------------------ src/rcore_web.c | 59 -------------------------------------- 6 files changed, 68 insertions(+), 288 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 2e6d32132..595cbfe02 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2113,11 +2113,7 @@ float GetGamepadAxisMovement(int gamepad, int axis) //---------------------------------------------------------------------------------- // NOTE: Functions with a platform-specific implementation on rcore_.c -//int GetMouseX(void) ** -//int GetMouseY(void) ** -//Vector2 GetMousePosition(void) ** //void SetMousePosition(int x, int y) -//float GetMouseWheelMove(void) ** //void SetMouseCursor(int cursor) // Check if a mouse button has been pressed once @@ -2172,6 +2168,29 @@ bool IsMouseButtonUp(int button) return up; } +// Get mouse position X +int GetMouseX(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); +} + +// Get mouse position Y +int GetMouseY(void) +{ + return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); +} + +// Get mouse position XY +Vector2 GetMousePosition(void) +{ + Vector2 position = { 0 }; + + position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; + position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; + + return position; +} + // Get mouse delta between frames Vector2 GetMouseDelta(void) { @@ -2197,6 +2216,17 @@ void SetMouseScale(float scaleX, float scaleY) CORE.Input.Mouse.scale = (Vector2){ scaleX, scaleY }; } +// Get mouse wheel movement Y +float GetMouseWheelMove(void) +{ + float result = 0.0f; + + if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x; + else result = (float)CORE.Input.Mouse.currentWheelMove.y; + + return result; +} + // Get mouse wheel movement X/Y as a vector Vector2 GetMouseWheelMoveV(void) { @@ -2211,10 +2241,29 @@ Vector2 GetMouseWheelMoveV(void) // Module Functions Definition: Input Handling: Touch //---------------------------------------------------------------------------------- -// NOTE: Functions with a platform-specific implementation on rcore_.c -//int GetTouchX(void) -//int GetTouchY(void) -//Vector2 GetTouchPosition(int index) +// Get touch position X for touch point 0 (relative to screen size) +int GetTouchX(void) +{ + return (int)CORE.Input.Touch.position[0].x; +} + +// Get touch position Y for touch point 0 (relative to screen size) +int GetTouchY(void) +{ + return (int)CORE.Input.Touch.position[0].y; +} + +// Get touch position XY for a touch point index (relative to screen size) +// TODO: Touch position should be scaled depending on display size and render size +Vector2 GetTouchPosition(int index) +{ + Vector2 position = { -1.0f, -1.0f }; + + if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index]; + else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS); + + return position; +} // Get touch point identifier for given index int GetTouchPointId(int index) diff --git a/src/rcore_android.c b/src/rcore_android.c index d9f72894a..1921ed79b 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -642,24 +642,6 @@ int SetGamepadMappings(const char *mappings) return 0; } -// Get mouse position X -int GetMouseX(void) -{ - return (int)CORE.Input.Touch.position[0].x; -} - -// Get mouse position Y -int GetMouseY(void) -{ - return (int)CORE.Input.Touch.position[0].y; -} - -// Get mouse position XY -Vector2 GetMousePosition(void) -{ - return GetTouchPosition(0); -} - // Set mouse position XY void SetMousePosition(int x, int y) { @@ -667,43 +649,12 @@ void SetMousePosition(int x, int y) CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; } -// Get mouse wheel movement Y -float GetMouseWheelMove(void) -{ - TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on target platform"); - return 0.0f; -} - // Set mouse cursor void SetMouseCursor(int cursor) { TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); } -// Get touch position X for touch point 0 (relative to screen size) -int GetTouchX(void) -{ - return (int)CORE.Input.Touch.position[0].x; -} - -// Get touch position Y for touch point 0 (relative to screen size) -int GetTouchY(void) -{ - return (int)CORE.Input.Touch.position[0].y; -} - -// Get touch position XY for a touch point index (relative to screen size) -// TODO: Touch position should be scaled depending on display size and render size -Vector2 GetTouchPosition(int index) -{ - Vector2 position = { -1.0f, -1.0f }; - - if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index]; - else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS); - - return position; -} - // Register all input events void PollInputEvents(void) { @@ -1247,6 +1198,10 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1; else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0; + + // Map touch[0] as mouse input for convenience + CORE.Input.Mouse.currentPosition = CORE.Input.Touch.position[0]; + CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f }; return 0; } diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index eaef9517b..8c4b73c8c 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -1228,29 +1228,6 @@ int SetGamepadMappings(const char *mappings) return glfwUpdateGamepadMappings(mappings); } -// Get mouse position X -int GetMouseX(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); -} - -// Get mouse position Y -int GetMouseY(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); -} - -// Get mouse position XY -Vector2 GetMousePosition(void) -{ - Vector2 position = { 0 }; - - position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; - position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; - - return position; -} - // Set mouse position XY void SetMousePosition(int x, int y) { @@ -1261,17 +1238,6 @@ void SetMousePosition(int x, int y) glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); } -// Get mouse wheel movement Y -float GetMouseWheelMove(void) -{ - float result = 0.0f; - - if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x; - else result = (float)CORE.Input.Mouse.currentWheelMove.y; - - return result; -} - // Set mouse cursor void SetMouseCursor(int cursor) { @@ -1284,32 +1250,6 @@ void SetMouseCursor(int cursor) } } -// Get touch position X for touch point 0 (relative to screen size) -int GetTouchX(void) -{ - return GetMouseX(); -} - -// Get touch position Y for touch point 0 (relative to screen size) -int GetTouchY(void) -{ - return GetMouseY(); -} - -// Get touch position XY for a touch point index (relative to screen size) -// TODO: Touch position should be scaled depending on display size and render size -Vector2 GetTouchPosition(int index) -{ - Vector2 position = { -1.0f, -1.0f }; - - // TODO: GLFW does not support multi-touch input just yet - // https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch - // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages - if (index == 0) position = GetMousePosition(); - - return position; -} - // Register all input events void PollInputEvents(void) { @@ -1352,6 +1292,13 @@ void PollInputEvents(void) // Reset touch positions //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; + + // Map touch position to mouse position for convenience + // WARNING: If the target desktop device supports touch screen, this behavious should be reviewed! + // TODO: GLFW does not support multi-touch input just yet + // https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch + // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages + CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; // Check if gamepads are ready // NOTE: We do it here in case of disconnection diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 13d41bbbd..839edeb47 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -746,29 +746,6 @@ int SetGamepadMappings(const char *mappings) return 0; } -// Get mouse position X -int GetMouseX(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); -} - -// Get mouse position Y -int GetMouseY(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); -} - -// Get mouse position XY -Vector2 GetMousePosition(void) -{ - Vector2 position = { 0 }; - - position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; - position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; - - return position; -} - // Set mouse position XY void SetMousePosition(int x, int y) { @@ -776,46 +753,12 @@ void SetMousePosition(int x, int y) CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; } -// Get mouse wheel movement Y -float GetMouseWheelMove(void) -{ - float result = 0.0f; - - if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x; - else result = (float)CORE.Input.Mouse.currentWheelMove.y; - - return result; -} - // Set mouse cursor void SetMouseCursor(int cursor) { TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); } -// Get touch position X for touch point 0 (relative to screen size) -int GetTouchX(void) -{ - return GetMouseX(); -} - -// Get touch position Y for touch point 0 (relative to screen size) -int GetTouchY(void) -{ - return GetMouseY(); -} - -// Get touch position XY for a touch point index (relative to screen size) -Vector2 GetTouchPosition(int index) -{ - Vector2 position = { -1.0f, -1.0f }; - - if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index]; - else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS); - - return position; -} - // Register all input events void PollInputEvents(void) { diff --git a/src/rcore_template.c b/src/rcore_template.c index 68a43b2cb..b6f0ff97e 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -570,30 +570,6 @@ int SetGamepadMappings(const char *mappings) return 0; } -// Get mouse position X -int GetMouseX(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); -} - -// Get mouse position Y -int GetMouseY(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); -} - -// Get mouse position XY -Vector2 GetMousePosition(void) -{ - Vector2 position = { 0 }; - - // NOTE: On canvas scaling, mouse position is proportionally returned - position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; - position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; - - return position; -} - // Set mouse position XY void SetMousePosition(int x, int y) { @@ -601,43 +577,12 @@ void SetMousePosition(int x, int y) CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; } -// Get mouse wheel movement Y -float GetMouseWheelMove(void) -{ - TRACELOG(LOG_WARNING, "GetMouseWheelMove() not implemented on target platform"); - return 0.0f; -} - // Set mouse cursor void SetMouseCursor(int cursor) { TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); } -// Get touch position X for touch point 0 (relative to screen size) -int GetTouchX(void) -{ - return (int)CORE.Input.Touch.position[0].x; -} - -// Get touch position Y for touch point 0 (relative to screen size) -int GetTouchY(void) -{ - return (int)CORE.Input.Touch.position[0].y; -} - -// Get touch position XY for a touch point index (relative to screen size) -// TODO: Touch position should be scaled depending on display size and render size -Vector2 GetTouchPosition(int index) -{ - Vector2 position = { -1.0f, -1.0f }; - - if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index]; - else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS); - - return position; -} - // Register all input events void PollInputEvents(void) { diff --git a/src/rcore_web.c b/src/rcore_web.c index 00691e430..d6af5a6de 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -705,30 +705,6 @@ int SetGamepadMappings(const char *mappings) return 0; } -// Get mouse position X -int GetMouseX(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x); -} - -// Get mouse position Y -int GetMouseY(void) -{ - return (int)((CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y); -} - -// Get mouse position XY -Vector2 GetMousePosition(void) -{ - Vector2 position = { 0 }; - - // NOTE: On canvas scaling, mouse position is proportionally returned - position.x = (CORE.Input.Mouse.currentPosition.x + CORE.Input.Mouse.offset.x)*CORE.Input.Mouse.scale.x; - position.y = (CORE.Input.Mouse.currentPosition.y + CORE.Input.Mouse.offset.y)*CORE.Input.Mouse.scale.y; - - return position; -} - // Set mouse position XY void SetMousePosition(int x, int y) { @@ -739,47 +715,12 @@ void SetMousePosition(int x, int y) glfwSetCursorPos(platform.handle, CORE.Input.Mouse.currentPosition.x, CORE.Input.Mouse.currentPosition.y); } -// Get mouse wheel movement Y -float GetMouseWheelMove(void) -{ - float result = 0.0f; - - if (fabsf(CORE.Input.Mouse.currentWheelMove.x) > fabsf(CORE.Input.Mouse.currentWheelMove.y)) result = (float)CORE.Input.Mouse.currentWheelMove.x; - else result = (float)CORE.Input.Mouse.currentWheelMove.y; - - return result; -} - // Set mouse cursor void SetMouseCursor(int cursor) { TRACELOG(LOG_INFO, "SetMouseCursor not implemented in rcore_web.c"); } -// Get touch position X for touch point 0 (relative to screen size) -int GetTouchX(void) -{ - return (int)CORE.Input.Touch.position[0].x; -} - -// Get touch position Y for touch point 0 (relative to screen size) -int GetTouchY(void) -{ - return (int)CORE.Input.Touch.position[0].y; -} - -// Get touch position XY for a touch point index (relative to screen size) -// TODO: Touch position should be scaled depending on display size and render size -Vector2 GetTouchPosition(int index) -{ - Vector2 position = { -1.0f, -1.0f }; - - if (index < MAX_TOUCH_POINTS) position = CORE.Input.Touch.position[index]; - else TRACELOG(LOG_WARNING, "INPUT: Required touch point out of range (Max touch points: %i)", MAX_TOUCH_POINTS); - - return position; -} - // Register all input events void PollInputEvents(void) { From a2c5f01059e9c8efd951c06a87a37e39120daf00 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Oct 2023 11:55:12 +0200 Subject: [PATCH 0342/1350] Reordered one function --- src/rcore.c | 2 +- src/rcore_android.c | 12 ++++----- src/rcore_desktop.c | 58 ++++++++++++++++++++++---------------------- src/rcore_drm.c | 12 ++++----- src/rcore_template.c | 12 ++++----- src/rcore_web.c | 12 ++++----- 6 files changed, 54 insertions(+), 54 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 595cbfe02..8b5b3bf49 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2285,7 +2285,7 @@ int GetTouchPointCount(void) // Module Internal Functions Definition //---------------------------------------------------------------------------------- -// Platform-specific functions +// NOTE: Functions with a platform-specific implementation on rcore_.c //static bool InitGraphicsDevice(int width, int height) // Set viewport for a provided width and height diff --git a/src/rcore_android.c b/src/rcore_android.c index 1921ed79b..4413dfa87 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -347,6 +347,12 @@ void ToggleFullscreen(void) TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform"); } +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); +} + // Set window state: maximized, if resizable void MaximizeWindow(void) { @@ -365,12 +371,6 @@ void RestoreWindow(void) TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } -// Toggle borderless windowed mode -void ToggleBorderlessWindowed(void) -{ - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); -} - // Set window configuration state using flags void SetWindowState(unsigned int flags) { diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 8c4b73c8c..54b684a0c 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -380,35 +380,6 @@ void ToggleFullscreen(void) if (CORE.Window.flags & FLAG_VSYNC_HINT) glfwSwapInterval(1); } -// Set window state: maximized, if resizable -void MaximizeWindow(void) -{ - if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) - { - glfwMaximizeWindow(platform.handle); - CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; - } -} - -// Set window state: minimized -void MinimizeWindow(void) -{ - // NOTE: Following function launches callback that sets appropriate flag! - glfwIconifyWindow(platform.handle); -} - -// Set window state: not minimized/maximized -void RestoreWindow(void) -{ - if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) - { - // Restores the specified window if it was previously iconified (minimized) or maximized - glfwRestoreWindow(platform.handle); - CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; - CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; - } -} - // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { @@ -484,6 +455,35 @@ void ToggleBorderlessWindowed(void) else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); } +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) + { + glfwMaximizeWindow(platform.handle); + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; + } +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + // NOTE: Following function launches callback that sets appropriate flag! + glfwIconifyWindow(platform.handle); +} + +// Set window state: not minimized/maximized +void RestoreWindow(void) +{ + if (glfwGetWindowAttrib(platform.handle, GLFW_RESIZABLE) == GLFW_TRUE) + { + // Restores the specified window if it was previously iconified (minimized) or maximized + glfwRestoreWindow(platform.handle); + CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; + CORE.Window.flags &= ~FLAG_WINDOW_MAXIMIZED; + } +} + // Set window configuration state using flags void SetWindowState(unsigned int flags) { diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 839edeb47..459444ada 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -448,6 +448,12 @@ void ToggleFullscreen(void) TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform"); } +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); +} + // Set window state: maximized, if resizable void MaximizeWindow(void) { @@ -466,12 +472,6 @@ void RestoreWindow(void) TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } -// Toggle borderless windowed mode -void ToggleBorderlessWindowed(void) -{ - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); -} - // Set window configuration state using flags void SetWindowState(unsigned int flags) { diff --git a/src/rcore_template.c b/src/rcore_template.c index b6f0ff97e..5b6507eb8 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -275,6 +275,12 @@ void ToggleFullscreen(void) TRACELOG(LOG_WARNING, "ToggleFullscreen() not available on target platform"); } +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); +} + // Set window state: maximized, if resizable void MaximizeWindow(void) { @@ -293,12 +299,6 @@ void RestoreWindow(void) TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } -// Toggle borderless windowed mode -void ToggleBorderlessWindowed(void) -{ - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); -} - // Set window configuration state using flags void SetWindowState(unsigned int flags) { diff --git a/src/rcore_web.c b/src/rcore_web.c index d6af5a6de..d24ef1214 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -410,6 +410,12 @@ void ToggleFullscreen(void) CORE.Window.fullscreen = !CORE.Window.fullscreen; // Toggle fullscreen flag } +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); +} + // Set window state: maximized, if resizable void MaximizeWindow(void) { @@ -428,12 +434,6 @@ void RestoreWindow(void) TRACELOG(LOG_WARNING, "RestoreWindow() not available on target platform"); } -// Toggle borderless windowed mode -void ToggleBorderlessWindowed(void) -{ - TRACELOG(LOG_WARNING, "ToggleBorderlessWindowed() not available on target platform"); -} - // Set window configuration state using flags void SetWindowState(unsigned int flags) { From da9c2894feee6d64780d9cfe161255c4db91decc Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Oct 2023 12:10:38 +0200 Subject: [PATCH 0343/1350] Reorganized some functions, `WaitTime()` is common to all platforms --- src/raylib.h | 18 +++--- src/rcore.c | 157 +++++++++++++++++++++++++-------------------------- 2 files changed, 87 insertions(+), 88 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 5d63d4b62..c03e0a576 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -986,14 +986,6 @@ RLAPI const char *GetClipboardText(void); // Get clipboa RLAPI void EnableEventWaiting(void); // Enable waiting for events on EndDrawing(), no automatic event polling RLAPI void DisableEventWaiting(void); // Disable waiting for events on EndDrawing(), automatic events polling -// Custom frame control functions -// NOTE: Those functions are intended for advance users that want full control over the frame processing -// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents() -// To avoid that behaviour and control frame processes manually, enable in config.h: SUPPORT_CUSTOM_FRAME_CONTROL -RLAPI void SwapScreenBuffer(void); // Swap back buffer with front buffer (screen drawing) -RLAPI void PollInputEvents(void); // Register all input events -RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution) - // Cursor-related functions RLAPI void ShowCursor(void); // Shows cursor RLAPI void HideCursor(void); // Hides cursor @@ -1049,9 +1041,17 @@ RLAPI Vector2 GetWorldToScreen2D(Vector2 position, Camera2D camera); // Get the // Timing-related functions RLAPI void SetTargetFPS(int fps); // Set target FPS (maximum) -RLAPI int GetFPS(void); // Get current FPS RLAPI float GetFrameTime(void); // Get time in seconds for last frame drawn (delta time) RLAPI double GetTime(void); // Get elapsed time in seconds since InitWindow() +RLAPI int GetFPS(void); // Get current FPS + +// Custom frame control functions +// NOTE: Those functions are intended for advance users that want full control over the frame processing +// By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents() +// To avoid that behaviour and control frame processes manually, enable in config.h: SUPPORT_CUSTOM_FRAME_CONTROL +RLAPI void SwapScreenBuffer(void); // Swap back buffer with front buffer (screen drawing) +RLAPI void PollInputEvents(void); // Register all input events +RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution) // Misc. functions RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included) diff --git a/src/rcore.c b/src/rcore.c index 8b5b3bf49..18d2e2929 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -430,15 +430,6 @@ bool IsCursorOnScreen(void) return CORE.Input.Mouse.cursorOnScreen; } -//---------------------------------------------------------------------------------- -// Module Functions Definition: Custom frame control -//---------------------------------------------------------------------------------- - -// NOTE: Functions with a platform-specific implementation on rcore_.c -//void SwapScreenBuffer(void); -//void PollInputEvents(void); -//void WaitTime(double seconds); - //---------------------------------------------------------------------------------- // Module Functions Definition: Screen Drawing //---------------------------------------------------------------------------------- @@ -1236,6 +1227,60 @@ float GetFrameTime(void) return (float)CORE.Time.frame; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Custom frame control +//---------------------------------------------------------------------------------- + +// NOTE: Functions with a platform-specific implementation on rcore_.c +//void SwapScreenBuffer(void); +//void PollInputEvents(void); + +// Wait for some time (stop program execution) +// NOTE: Sleep() granularity could be around 10 ms, it means, Sleep() could +// take longer than expected... for that reason we use the busy wait loop +// Ref: http://stackoverflow.com/questions/43057578/c-programming-win32-games-sleep-taking-longer-than-expected +// Ref: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timing on Win32! +void WaitTime(double seconds) +{ + if (seconds < 0) return; + +#if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) + double destinationTime = GetTime() + seconds; +#endif + +#if defined(SUPPORT_BUSY_WAIT_LOOP) + while (GetTime() < destinationTime) { } +#else + #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) + double sleepSeconds = seconds - seconds*0.05; // NOTE: We reserve a percentage of the time for busy waiting + #else + double sleepSeconds = seconds; + #endif + + // System halt functions + #if defined(_WIN32) + Sleep((unsigned long)(sleepSeconds*1000.0)); + #endif + #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__) + struct timespec req = { 0 }; + time_t sec = sleepSeconds; + long nsec = (sleepSeconds - sec)*1000000000L; + req.tv_sec = sec; + req.tv_nsec = nsec; + + // NOTE: Use nanosleep() on Unix platforms... usleep() it's deprecated. + while (nanosleep(&req, &req) == -1) continue; + #endif + #if defined(__APPLE__) + usleep(sleepSeconds*1000000.0); + #endif + + #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) + while (GetTime() < destinationTime) { } + #endif +#endif +} + //---------------------------------------------------------------------------------- // Module Functions Definition: Misc //---------------------------------------------------------------------------------- @@ -2288,6 +2333,30 @@ int GetTouchPointCount(void) // NOTE: Functions with a platform-specific implementation on rcore_.c //static bool InitGraphicsDevice(int width, int height) +// Initialize hi-resolution timer +void InitTimer(void) +{ +// Setting a higher resolution can improve the accuracy of time-out intervals in wait functions. +// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often. +// High resolutions can also prevent the CPU power management system from entering power-saving modes. +// Setting a higher resolution does not improve the accuracy of the high-resolution performance counter. +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms) +#endif + +#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__) + struct timespec now = { 0 }; + + if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success + { + CORE.Time.base = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec; + } + else TRACELOG(LOG_WARNING, "TIMER: Hi-resolution timer not available"); +#endif + + CORE.Time.previous = GetTime(); // Get time as double +} + // Set viewport for a provided width and height void SetupViewport(int width, int height) { @@ -2395,76 +2464,6 @@ void SetupFramebuffer(int width, int height) } } -// Initialize hi-resolution timer -void InitTimer(void) -{ -// Setting a higher resolution can improve the accuracy of time-out intervals in wait functions. -// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often. -// High resolutions can also prevent the CPU power management system from entering power-saving modes. -// Setting a higher resolution does not improve the accuracy of the high-resolution performance counter. -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms) -#endif - -#if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__) - struct timespec now = { 0 }; - - if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) // Success - { - CORE.Time.base = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec; - } - else TRACELOG(LOG_WARNING, "TIMER: Hi-resolution timer not available"); -#endif - - CORE.Time.previous = GetTime(); // Get time as double -} - -// Wait for some time (stop program execution) -// NOTE: Sleep() granularity could be around 10 ms, it means, Sleep() could -// take longer than expected... for that reason we use the busy wait loop -// Ref: http://stackoverflow.com/questions/43057578/c-programming-win32-games-sleep-taking-longer-than-expected -// Ref: http://www.geisswerks.com/ryan/FAQS/timing.html --> All about timing on Win32! -void WaitTime(double seconds) -{ - if (seconds < 0) return; - -#if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) - double destinationTime = GetTime() + seconds; -#endif - -#if defined(SUPPORT_BUSY_WAIT_LOOP) - while (GetTime() < destinationTime) { } -#else - #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) - double sleepSeconds = seconds - seconds*0.05; // NOTE: We reserve a percentage of the time for busy waiting - #else - double sleepSeconds = seconds; - #endif - - // System halt functions - #if defined(_WIN32) - Sleep((unsigned long)(sleepSeconds*1000.0)); - #endif - #if defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__EMSCRIPTEN__) - struct timespec req = { 0 }; - time_t sec = sleepSeconds; - long nsec = (sleepSeconds - sec)*1000000000L; - req.tv_sec = sec; - req.tv_nsec = nsec; - - // NOTE: Use nanosleep() on Unix platforms... usleep() it's deprecated. - while (nanosleep(&req, &req) == -1) continue; - #endif - #if defined(__APPLE__) - usleep(sleepSeconds*1000000.0); - #endif - - #if defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) - while (GetTime() < destinationTime) { } - #endif -#endif -} - // Scan all files and directories in a base path // WARNING: files.paths[] must be previously allocated and // contain enough space to store all required paths From 28fb58f0ea11f0b30b8aaf79d7bb148491ed8775 Mon Sep 17 00:00:00 2001 From: Murlocohol Date: Wed, 11 Oct 2023 06:15:40 -0400 Subject: [PATCH 0344/1350] [rtext] TextFormat() warn user if buffer overflow occured. (#3399) * [rtext] TextFormat now alerts user to truncation. * Update rtext.c * Update rcore.c * Update rtext.c --- src/rcore.c | 12 +++++++++++- src/rtext.c | 11 ++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 18d2e2929..c1c9980b2 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2922,6 +2922,7 @@ static void PlayAutomationEvent(unsigned int frame) #if !defined(SUPPORT_MODULE_RTEXT) // Formatting of text with variables to 'embed' // WARNING: String returned will expire after this function is called MAX_TEXTFORMAT_BUFFERS times + const char *TextFormat(const char *text, ...) { #ifndef MAX_TEXTFORMAT_BUFFERS @@ -2940,12 +2941,21 @@ const char *TextFormat(const char *text, ...) va_list args; va_start(args, text); - vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); + int charCountRequired = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); va_end(args); + // If charCountRequired is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured + if(charCountRequired > MAX_TEXT_BUFFER_LENGTH) + { + // We are going to insert [TRUN] at the end of the string so the user knows what happened + char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // 7 = six letters + '\0' + sprintf(truncBuffer, "[TRUN]"); + } + index += 1; // Move to next buffer for next function call if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0; return currentBuffer; } + #endif // !SUPPORT_MODULE_RTEXT diff --git a/src/rtext.c b/src/rtext.c index fb8440131..16b65507b 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1371,15 +1371,24 @@ const char *TextFormat(const char *text, ...) va_list args; va_start(args, text); - vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); + int charCountRequired = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); va_end(args); + // If charCountRequired is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured + if(charCountRequired > MAX_TEXT_BUFFER_LENGTH) + { + // We are going to insert [TRUN] at the end of the string so the user knows what happened + char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // 7 = six letters + '\0' + sprintf(truncBuffer, "[TRUN]"); + } + index += 1; // Move to next buffer for next function call if (index >= MAX_TEXTFORMAT_BUFFERS) index = 0; return currentBuffer; } + // Get integer value from text // NOTE: This function replaces atoi() [stdlib.h] int TextToInteger(const char *text) From 61af8e76310cfc23015e0eaee99c55fb72714a30 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Oct 2023 12:20:03 +0200 Subject: [PATCH 0345/1350] REVIEWED: #3399, Fix #3366 --- src/rcore.c | 9 ++++----- src/rtext.c | 8 ++++---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index c1c9980b2..e7868ebb8 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2922,7 +2922,6 @@ static void PlayAutomationEvent(unsigned int frame) #if !defined(SUPPORT_MODULE_RTEXT) // Formatting of text with variables to 'embed' // WARNING: String returned will expire after this function is called MAX_TEXTFORMAT_BUFFERS times - const char *TextFormat(const char *text, ...) { #ifndef MAX_TEXTFORMAT_BUFFERS @@ -2941,14 +2940,14 @@ const char *TextFormat(const char *text, ...) va_list args; va_start(args, text); - int charCountRequired = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); + int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); va_end(args); - // If charCountRequired is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured - if(charCountRequired > MAX_TEXT_BUFFER_LENGTH) + // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured + if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH) { // We are going to insert [TRUN] at the end of the string so the user knows what happened - char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // 7 = six letters + '\0' + char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // Adding 7 bytes = six char + '\0' sprintf(truncBuffer, "[TRUN]"); } diff --git a/src/rtext.c b/src/rtext.c index 16b65507b..593fdb53f 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1371,14 +1371,14 @@ const char *TextFormat(const char *text, ...) va_list args; va_start(args, text); - int charCountRequired = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); + int requiredByteCount = vsnprintf(currentBuffer, MAX_TEXT_BUFFER_LENGTH, text, args); va_end(args); - // If charCountRequired is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured - if(charCountRequired > MAX_TEXT_BUFFER_LENGTH) + // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured + if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH) { // We are going to insert [TRUN] at the end of the string so the user knows what happened - char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // 7 = six letters + '\0' + char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // Adding 7 bytes = six chars + '\0' sprintf(truncBuffer, "[TRUN]"); } From 6ed8acde6730c34a7e127b16ab567a5f3438b29b Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 11 Oct 2023 14:29:21 -0300 Subject: [PATCH 0346/1350] Fix windowMin/Max to screenMin/Max for android, drm, template (#3400) --- src/rcore.h | 2 -- src/rcore_android.c | 10 +++++----- src/rcore_drm.c | 18 +++++++++--------- src/rcore_template.c | 12 ++++++------ 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/rcore.h b/src/rcore.h index 4d40c9567..dbff6ab13 100644 --- a/src/rcore.h +++ b/src/rcore.h @@ -127,8 +127,6 @@ typedef struct CoreData { Point renderOffset; // Offset from render area (must be divided by 2) Size screenMin; // Screen minimum width and height (for resizable window) Size screenMax; // Screen maximum width and height (for resizable window) - Size windowMin; // Window minimum width and height - Size windowMax; // Window maximum width and height Matrix screenScale; // Matrix to scale screen (framebuffer rendering) char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) diff --git a/src/rcore_android.c b/src/rcore_android.c index 4413dfa87..bfb57fedf 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -416,15 +416,15 @@ void SetWindowMonitor(int monitor) // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMinSize(int width, int height) { - CORE.Window.windowMin.width = width; - CORE.Window.windowMin.height = height; + CORE.Window.screenMin.width = width; + CORE.Window.screenMin.height = height; } // Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMaxSize(int width, int height) { - CORE.Window.windowMax.width = width; - CORE.Window.windowMax.height = height; + CORE.Window.screenMax.width = width; + CORE.Window.screenMax.height = height; } // Set window dimensions @@ -1198,7 +1198,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1; else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0; - + // Map touch[0] as mouse input for convenience CORE.Input.Mouse.currentPosition = CORE.Input.Touch.position[0]; CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f }; diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 459444ada..2f0eab682 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -281,7 +281,7 @@ void InitWindow(int width, int height, const char *title) InitGamepad(); // Gamepad init InitKeyboard(); // Keyboard init (stdin) //-------------------------------------------------------------- - + TRACELOG(LOG_INFO, "PLATFORM: DRM: Application initialized successfully"); } @@ -517,15 +517,15 @@ void SetWindowMonitor(int monitor) // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMinSize(int width, int height) { - CORE.Window.windowMin.width = width; - CORE.Window.windowMin.height = height; + CORE.Window.screenMin.width = width; + CORE.Window.screenMin.height = height; } // Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMaxSize(int width, int height) { - CORE.Window.windowMax.width = width; - CORE.Window.windowMax.height = height; + CORE.Window.screenMax.width = width; + CORE.Window.screenMax.height = height; } // Set window dimensions @@ -841,10 +841,10 @@ static bool InitGraphicsDevice(int width, int height) CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default // Set the window minimum and maximum default values to 0 - CORE.Window.windowMin.width = 0; - CORE.Window.windowMin.height = 0; - CORE.Window.windowMax.width = 0; - CORE.Window.windowMax.height = 0; + CORE.Window.screenMin.width = 0; + CORE.Window.screenMin.height = 0; + CORE.Window.screenMax.width = 0; + CORE.Window.screenMax.height = 0; // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... // ...in top-down or left-right to match display aspect ratio (no weird scaling) diff --git a/src/rcore_template.c b/src/rcore_template.c index 5b6507eb8..ea5af40d9 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -141,7 +141,7 @@ void InitWindow(int width, int height, const char *title) // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } - + // Initialize hi-res timer InitTimer(); @@ -150,7 +150,7 @@ void InitWindow(int width, int height, const char *title) // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); - + #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font // WARNING: External function: Module required: rtext @@ -344,15 +344,15 @@ void SetWindowMonitor(int monitor) // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMinSize(int width, int height) { - CORE.Window.windowMin.width = width; - CORE.Window.windowMin.height = height; + CORE.Window.screenMin.width = width; + CORE.Window.screenMin.height = height; } // Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMaxSize(int width, int height) { - CORE.Window.windowMax.width = width; - CORE.Window.windowMax.height = height; + CORE.Window.screenMax.width = width; + CORE.Window.screenMax.height = height; } // Set window dimensions From 876e6b3a0d4db8e6124bf8f95d048879f2f760b1 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 11 Oct 2023 20:25:09 +0200 Subject: [PATCH 0347/1350] REVIEWED: `TextFormat()`, added "..." for truncation #3366 It seems more standard than [TRUN] --- src/rcore.c | 6 +++--- src/rtext.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index e7868ebb8..253436e51 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2946,9 +2946,9 @@ const char *TextFormat(const char *text, ...) // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH) { - // We are going to insert [TRUN] at the end of the string so the user knows what happened - char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // Adding 7 bytes = six char + '\0' - sprintf(truncBuffer, "[TRUN]"); + // Inserting "..." at the end of the string to mark as truncated + char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0" + sprintf(truncBuffer, "..."); } index += 1; // Move to next buffer for next function call diff --git a/src/rtext.c b/src/rtext.c index 593fdb53f..2d72bbe6b 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1377,9 +1377,9 @@ const char *TextFormat(const char *text, ...) // If requiredByteCount is larger than the MAX_TEXT_BUFFER_LENGTH, then overflow occured if (requiredByteCount >= MAX_TEXT_BUFFER_LENGTH) { - // We are going to insert [TRUN] at the end of the string so the user knows what happened - char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 7; // Adding 7 bytes = six chars + '\0' - sprintf(truncBuffer, "[TRUN]"); + // Inserting "..." at the end of the string to mark as truncated + char *truncBuffer = buffers[index] + MAX_TEXT_BUFFER_LENGTH - 4; // Adding 4 bytes = "...\0" + sprintf(truncBuffer, "..."); } index += 1; // Move to next buffer for next function call From 2e65bc675ce2ef92ccc784e25739b27edd7be94b Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 13 Oct 2023 14:14:16 +0200 Subject: [PATCH 0348/1350] Moved some platforms functions to generic `rcore` #3313 Reviewed `InitWindow()` to clearly note platform specific code --- src/rcore.c | 53 +++++++++++++++++----- src/rcore_android.c | 45 +++++-------------- src/rcore_desktop.c | 70 ++++++++++------------------- src/rcore_drm.c | 60 ++++++++----------------- src/rcore_template.c | 48 ++++---------------- src/rcore_web.c | 102 ++++++++++++++++--------------------------- 6 files changed, 142 insertions(+), 236 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 253436e51..3efa67b2c 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -322,18 +322,15 @@ const char *TextFormat(const char *text, ...); // Formatting of text with //void InitWindow(int width, int height, const char *title) //void CloseWindow(void) //bool WindowShouldClose(void) -//bool IsWindowHidden(void) -//bool IsWindowMinimized(void) -//bool IsWindowMaximized(void) -//bool IsWindowFocused(void) -//bool IsWindowResized(void) //void ToggleFullscreen(void) +//void ToggleBorderlessWindowed(void) //void MaximizeWindow(void) //void MinimizeWindow(void) //void RestoreWindow(void) -//void ToggleBorderlessWindowed(void) + //void SetWindowState(unsigned int flags) //void ClearWindowState(unsigned int flags) + //void SetWindowIcon(Image image) //void SetWindowIcons(Image *images, int count) //void SetWindowTitle(const char *title) @@ -345,25 +342,27 @@ const char *TextFormat(const char *text, ...); // Formatting of text with //void SetWindowOpacity(float opacity) //void SetWindowFocused(void) //void *GetWindowHandle(void) +//Vector2 GetWindowPosition(void) +//Vector2 GetWindowScaleDPI(void) + //int GetMonitorCount(void) //int GetCurrentMonitor(void) -//Vector2 GetMonitorPosition(int monitor) //int GetMonitorWidth(int monitor) //int GetMonitorHeight(int monitor) //int GetMonitorPhysicalWidth(int monitor) //int GetMonitorPhysicalHeight(int monitor) //int GetMonitorRefreshRate(int monitor) +//Vector2 GetMonitorPosition(int monitor) //const char *GetMonitorName(int monitor) -//Vector2 GetWindowPosition(void) -//Vector2 GetWindowScaleDPI(void) + //void SetClipboardText(const char *text) //const char *GetClipboardText(void) + //void ShowCursor(void) //void HideCursor(void) //void EnableCursor(void) //void DisableCursor(void) - // Check if window has been initialized successfully bool IsWindowReady(void) { @@ -376,6 +375,36 @@ bool IsWindowFullscreen(void) return CORE.Window.fullscreen; } +// Check if window is currently hidden +bool IsWindowHidden(void) +{ + return ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0); +} + +// Check if window has been minimized +bool IsWindowMinimized(void) +{ + return ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0); +} + +// Check if window has been maximized +bool IsWindowMaximized(void) +{ + return ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0); +} + +// Check if window has the focus +bool IsWindowFocused(void) +{ + return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0); +} + +// Check if window has been resizedLastFrame +bool IsWindowResized(void) +{ + return CORE.Window.resizedLastFrame; +} + // Check if one specific window flag is enabled bool IsWindowState(unsigned int flag) { @@ -394,13 +423,13 @@ int GetScreenHeight(void) return CORE.Window.screen.height; } -// Get current render width which is equal to screen width * dpi scale +// Get current render width which is equal to screen width*dpi scale int GetRenderWidth(void) { return CORE.Window.render.width; } -// Get current screen height which is equal to screen height * dpi scale +// Get current screen height which is equal to screen height*dpi scale int GetRenderHeight(void) { return CORE.Window.render.height; diff --git a/src/rcore_android.c b/src/rcore_android.c index bfb57fedf..7cc71bbca 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -183,13 +183,14 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN CORE.Window.eventWaiting = false; + + // Platform specific init window + //-------------------------------------------------------------- CORE.Window.screen.width = width; CORE.Window.screen.height = height; CORE.Window.currentFbo.width = width; CORE.Window.currentFbo.height = height; - - // Platform specific init window - //-------------------------------------------------------------- + // Set desired windows flags before initializing anything ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER @@ -227,6 +228,12 @@ void InitWindow(int width, int height, const char *title) // Initialize base path for storage CORE.Storage.basePath = platform.app->activity->internalDataPath; + + // Set some default window flags + CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false + CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED); // false + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED); // true + CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED); // false TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Application initialized successfully"); @@ -311,36 +318,6 @@ bool WindowShouldClose(void) else return true; } -// Check if window is currently hidden -bool IsWindowHidden(void) -{ - return false; -} - -// Check if window has been minimized -bool IsWindowMinimized(void) -{ - return false; -} - -// Check if window has been maximized -bool IsWindowMaximized(void) -{ - return false; -} - -// Check if window has the focus -bool IsWindowFocused(void) -{ - return platform.appEnabled; -} - -// Check if window has been resizedLastFrame -bool IsWindowResized(void) -{ - return false; -} - // Toggle fullscreen mode void ToggleFullscreen(void) { @@ -936,12 +913,14 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) case APP_CMD_GAINED_FOCUS: { platform.appEnabled = true; + CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; //ResumeMusicStream(); } break; case APP_CMD_PAUSE: break; case APP_CMD_LOST_FOCUS: { platform.appEnabled = false; + CORE.Window.flags |= FLAG_WINDOW_UNFOCUSED; //PauseMusicStream(); } break; case APP_CMD_TERM_WINDOW: diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 54b684a0c..c2e5b23f1 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -2,7 +2,7 @@ * * rcore_desktop - Functions to manage window, graphics device and inputs * -* PLATFORM: DESKTOP +* PLATFORM: DESKTOP: GLFW * - Windows (Win32, Win64) * - Linux (X11/Wayland desktop mode) * - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) @@ -187,8 +187,26 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN CORE.Window.eventWaiting = false; + + // Platform specific init window + //-------------------------------------------------------------- + glfwSetErrorCallback(ErrorCallback); +/* + // TODO: Setup GLFW custom allocators to match raylib ones + const GLFWallocator allocator = { + .allocate = MemAlloc, + .deallocate = MemFree, + .reallocate = MemRealloc, + .user = NULL + }; + + glfwInitAllocator(&allocator); +*/ + // Initialize graphics device // NOTE: returns true if window and graphic device has been initialized successfully + // WARNING: Actually, all window initialization and input callbacks initialization is + // done inside InitGraphicsDevice(), this functionality should be changed! CORE.Window.ready = InitGraphicsDevice(width, height); // If graphic device is no properly initialized, we end program @@ -197,13 +215,15 @@ void InitWindow(int width, int height, const char *title) // Initialize hi-res timer InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + //-------------------------------------------------------------- + // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font // WARNING: External function: Module required: rtext @@ -304,36 +324,6 @@ bool WindowShouldClose(void) else return true; } -// Check if window is currently hidden -bool IsWindowHidden(void) -{ - return ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0); -} - -// Check if window has been minimized -bool IsWindowMinimized(void) -{ - return ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0); -} - -// Check if window has been maximized -bool IsWindowMaximized(void) -{ - return ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0); -} - -// Check if window has the focus -bool IsWindowFocused(void) -{ - return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0); -} - -// Check if window has been resizedLastFrame -bool IsWindowResized(void) -{ - return CORE.Window.resizedLastFrame; -} - // Toggle fullscreen mode void ToggleFullscreen(void) { @@ -1408,18 +1398,6 @@ static bool InitGraphicsDevice(int width, int height) // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... // ...in top-down or left-right to match display aspect ratio (no weird scaling) - glfwSetErrorCallback(ErrorCallback); -/* - // TODO: Setup GLFW custom allocators to match raylib ones - const GLFWallocator allocator = { - .allocate = MemAlloc, - .deallocate = MemFree, - .reallocate = MemRealloc, - .user = NULL - }; - - glfwInitAllocator(&allocator); -*/ #if defined(__APPLE__) glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE); #endif diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 2f0eab682..c0e88c723 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -215,6 +215,9 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN CORE.Window.eventWaiting = false; + + // Platform specific init window + //-------------------------------------------------------------- // Initialize graphics device (display device and OpenGL context) // NOTE: returns true if window and graphic device has been initialized successfully CORE.Window.ready = InitGraphicsDevice(width, height); @@ -223,15 +226,28 @@ void InitWindow(int width, int height, const char *title) if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); + // Set some default window flags + CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false + CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED); // false + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED); // true + CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED); // false + // Initialize hi-res timer InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + + // Initialize raw input system + InitEvdevInput(); // Evdev inputs initialization + InitGamepad(); // Gamepad init + InitKeyboard(); // Keyboard init (stdin) + //-------------------------------------------------------------- + // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font // WARNING: External function: Module required: rtext @@ -274,14 +290,6 @@ void InitWindow(int width, int height, const char *title) CORE.Time.frameCounter = 0; #endif - // Platform specific init window - //-------------------------------------------------------------- - // Initialize raw input system - InitEvdevInput(); // Evdev inputs initialization - InitGamepad(); // Gamepad init - InitKeyboard(); // Keyboard init (stdin) - //-------------------------------------------------------------- - TRACELOG(LOG_INFO, "PLATFORM: DRM: Application initialized successfully"); } @@ -412,36 +420,6 @@ bool WindowShouldClose(void) else return true; } -// Check if window is currently hidden -bool IsWindowHidden(void) -{ - return false; -} - -// Check if window has been minimized -bool IsWindowMinimized(void) -{ - return false; -} - -// Check if window has been maximized -bool IsWindowMaximized(void) -{ - return false; -} - -// Check if window has the focus -bool IsWindowFocused(void) -{ - return true; -} - -// Check if window has been resizedLastFrame -bool IsWindowResized(void) -{ - return false; -} - // Toggle fullscreen mode void ToggleFullscreen(void) { diff --git a/src/rcore_template.c b/src/rcore_template.c index ea5af40d9..88b3c4a7e 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -128,8 +128,11 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Window.eventWaiting = false; + + + // TODO: Platform specific init window + //-------------------------------------------------------------- CORE.Window.screen.width = width; CORE.Window.screen.height = height; CORE.Window.currentFbo.width = width; @@ -144,13 +147,15 @@ void InitWindow(int width, int height, const char *title) // Initialize hi-res timer InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + //-------------------------------------------------------------- + // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font // WARNING: External function: Module required: rtext @@ -193,11 +198,6 @@ void InitWindow(int width, int height, const char *title) CORE.Time.frameCounter = 0; #endif - // TODO: Platform specific init window - //-------------------------------------------------------------- - // ... - //-------------------------------------------------------------- - TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Application initialized successfully"); } @@ -239,36 +239,6 @@ bool WindowShouldClose(void) else return true; } -// Check if window is currently hidden -bool IsWindowHidden(void) -{ - return false; -} - -// Check if window has been minimized -bool IsWindowMinimized(void) -{ - return false; -} - -// Check if window has been maximized -bool IsWindowMaximized(void) -{ - return false; -} - -// Check if window has the focus -bool IsWindowFocused(void) -{ - return platform.appEnabled; -} - -// Check if window has been resizedLastFrame -bool IsWindowResized(void) -{ - return false; -} - // Toggle fullscreen mode void ToggleFullscreen(void) { diff --git a/src/rcore_web.c b/src/rcore_web.c index d24ef1214..261498a5b 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -171,6 +171,9 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN CORE.Window.eventWaiting = false; + + // Platform specific init window + //-------------------------------------------------------------- // Initialize graphics device (display device and OpenGL context) // NOTE: returns true if window and graphic device has been initialized successfully CORE.Window.ready = InitGraphicsDevice(width, height); @@ -181,13 +184,44 @@ void InitWindow(int width, int height, const char *title) // Initialize hi-res timer InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + + // Setup callback functions for the DOM events + emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); + + // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review + // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas) + // emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); + // Check Resize event (note this is done on the window since most browsers don't support this on #canvas) + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); + + // Trigger this once to get initial window sizing + EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); + + // Support keyboard events -> Not used, GLFW.JS takes care of that + // emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); + // emscripten_set_keydown_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); + + // Support mouse events + emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback); + + // Support touch events + emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + + // Support gamepad events (not provided by GLFW3 on emscripten) + emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); + emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); + //-------------------------------------------------------------- + // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font // WARNING: External function: Module required: rtext @@ -230,38 +264,6 @@ void InitWindow(int width, int height, const char *title) CORE.Time.frameCounter = 0; #endif - // Platform specific init window - //-------------------------------------------------------------- - // Setup callback functions for the DOM events - emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); - - // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review - // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas) - // emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - // Check Resize event (note this is done on the window since most browsers don't support this on #canvas) - emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - - // Trigger this once to get initial window sizing - EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); - - // Support keyboard events -> Not used, GLFW.JS takes care of that - // emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); - // emscripten_set_keydown_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); - - // Support mouse events - emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback); - - // Support touch events - emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - - // Support gamepad events (not provided by GLFW3 on emscripten) - emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); - emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); - //-------------------------------------------------------------- - TRACELOG(LOG_INFO, "PLATFORM: WEB: Application initialized successfully"); } @@ -309,36 +311,6 @@ bool WindowShouldClose(void) return false; } -// Check if window is currently hidden -bool IsWindowHidden(void) -{ - return false; -} - -// Check if window has been minimized -bool IsWindowMinimized(void) -{ - return false; -} - -// Check if window has been maximized -bool IsWindowMaximized(void) -{ - return false; -} - -// Check if window has the focus -bool IsWindowFocused(void) -{ - return ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) == 0); -} - -// Check if window has been resizedLastFrame -bool IsWindowResized(void) -{ - return CORE.Window.resizedLastFrame; -} - // Toggle fullscreen mode void ToggleFullscreen(void) { From 0daa5ce1e71963577a2bcf6140df2d71d8e6c730 Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Fri, 13 Oct 2023 16:36:42 +0200 Subject: [PATCH 0349/1350] Fix `GetMouseDelta()` issue for Android (#3404) --- src/rcore_android.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/rcore_android.c b/src/rcore_android.c index 7cc71bbca..5f6f34bab 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -1178,6 +1178,16 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) if (CORE.Input.Touch.pointCount > 0) CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 1; else CORE.Input.Touch.currentTouchState[MOUSE_BUTTON_LEFT] = 0; + // Stores the previous position of touch[0] only while it's active to calculate the delta. + if (flags == AMOTION_EVENT_ACTION_MOVE) + { + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + } + else + { + CORE.Input.Mouse.previousPosition = CORE.Input.Touch.position[0]; + } + // Map touch[0] as mouse input for convenience CORE.Input.Mouse.currentPosition = CORE.Input.Touch.position[0]; CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f }; From 0f4a8cf7cb18dbd3594c77c5b2558f6cc979f2ef Mon Sep 17 00:00:00 2001 From: Babak Date: Fri, 13 Oct 2023 16:37:35 +0200 Subject: [PATCH 0350/1350] Ported to stb_image_resize2.h (#3403) --- src/external/stb_image_resize.h | 2634 -------- src/external/stb_image_resize2.h | 10303 +++++++++++++++++++++++++++++ src/rtextures.c | 12 +- 3 files changed, 10309 insertions(+), 2640 deletions(-) delete mode 100644 src/external/stb_image_resize.h create mode 100644 src/external/stb_image_resize2.h diff --git a/src/external/stb_image_resize.h b/src/external/stb_image_resize.h deleted file mode 100644 index ef9e6fe87..000000000 --- a/src/external/stb_image_resize.h +++ /dev/null @@ -1,2634 +0,0 @@ -/* stb_image_resize - v0.97 - public domain image resizing - by Jorge L Rodriguez (@VinoBS) - 2014 - http://github.com/nothings/stb - - Written with emphasis on usability, portability, and efficiency. (No - SIMD or threads, so it be easily outperformed by libs that use those.) - Only scaling and translation is supported, no rotations or shears. - Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation. - - COMPILING & LINKING - In one C/C++ file that #includes this file, do this: - #define STB_IMAGE_RESIZE_IMPLEMENTATION - before the #include. That will create the implementation in that file. - - QUICKSTART - stbir_resize_uint8( input_pixels , in_w , in_h , 0, - output_pixels, out_w, out_h, 0, num_channels) - stbir_resize_float(...) - stbir_resize_uint8_srgb( input_pixels , in_w , in_h , 0, - output_pixels, out_w, out_h, 0, - num_channels , alpha_chan , 0) - stbir_resize_uint8_srgb_edgemode( - input_pixels , in_w , in_h , 0, - output_pixels, out_w, out_h, 0, - num_channels , alpha_chan , 0, STBIR_EDGE_CLAMP) - // WRAP/REFLECT/ZERO - - FULL API - See the "header file" section of the source for API documentation. - - ADDITIONAL DOCUMENTATION - - SRGB & FLOATING POINT REPRESENTATION - The sRGB functions presume IEEE floating point. If you do not have - IEEE floating point, define STBIR_NON_IEEE_FLOAT. This will use - a slower implementation. - - MEMORY ALLOCATION - The resize functions here perform a single memory allocation using - malloc. To control the memory allocation, before the #include that - triggers the implementation, do: - - #define STBIR_MALLOC(size,context) ... - #define STBIR_FREE(ptr,context) ... - - Each resize function makes exactly one call to malloc/free, so to use - temp memory, store the temp memory in the context and return that. - - ASSERT - Define STBIR_ASSERT(boolval) to override assert() and not use assert.h - - OPTIMIZATION - Define STBIR_SATURATE_INT to compute clamp values in-range using - integer operations instead of float operations. This may be faster - on some platforms. - - DEFAULT FILTERS - For functions which don't provide explicit control over what filters - to use, you can change the compile-time defaults with - - #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something - #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something - - See stbir_filter in the header-file section for the list of filters. - - NEW FILTERS - A number of 1D filter kernels are used. For a list of - supported filters see the stbir_filter enum. To add a new filter, - write a filter function and add it to stbir__filter_info_table. - - PROGRESS - For interactive use with slow resize operations, you can install - a progress-report callback: - - #define STBIR_PROGRESS_REPORT(val) some_func(val) - - The parameter val is a float which goes from 0 to 1 as progress is made. - - For example: - - static void my_progress_report(float progress); - #define STBIR_PROGRESS_REPORT(val) my_progress_report(val) - - #define STB_IMAGE_RESIZE_IMPLEMENTATION - #include "stb_image_resize.h" - - static void my_progress_report(float progress) - { - printf("Progress: %f%%\n", progress*100); - } - - MAX CHANNELS - If your image has more than 64 channels, define STBIR_MAX_CHANNELS - to the max you'll have. - - ALPHA CHANNEL - Most of the resizing functions provide the ability to control how - the alpha channel of an image is processed. The important things - to know about this: - - 1. The best mathematically-behaved version of alpha to use is - called "premultiplied alpha", in which the other color channels - have had the alpha value multiplied in. If you use premultiplied - alpha, linear filtering (such as image resampling done by this - library, or performed in texture units on GPUs) does the "right - thing". While premultiplied alpha is standard in the movie CGI - industry, it is still uncommon in the videogame/real-time world. - - If you linearly filter non-premultiplied alpha, strange effects - occur. (For example, the 50/50 average of 99% transparent bright green - and 1% transparent black produces 50% transparent dark green when - non-premultiplied, whereas premultiplied it produces 50% - transparent near-black. The former introduces green energy - that doesn't exist in the source image.) - - 2. Artists should not edit premultiplied-alpha images; artists - want non-premultiplied alpha images. Thus, art tools generally output - non-premultiplied alpha images. - - 3. You will get best results in most cases by converting images - to premultiplied alpha before processing them mathematically. - - 4. If you pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, the - resizer does not do anything special for the alpha channel; - it is resampled identically to other channels. This produces - the correct results for premultiplied-alpha images, but produces - less-than-ideal results for non-premultiplied-alpha images. - - 5. If you do not pass the flag STBIR_FLAG_ALPHA_PREMULTIPLIED, - then the resizer weights the contribution of input pixels - based on their alpha values, or, equivalently, it multiplies - the alpha value into the color channels, resamples, then divides - by the resultant alpha value. Input pixels which have alpha=0 do - not contribute at all to output pixels unless _all_ of the input - pixels affecting that output pixel have alpha=0, in which case - the result for that pixel is the same as it would be without - STBIR_FLAG_ALPHA_PREMULTIPLIED. However, this is only true for - input images in integer formats. For input images in float format, - input pixels with alpha=0 have no effect, and output pixels - which have alpha=0 will be 0 in all channels. (For float images, - you can manually achieve the same result by adding a tiny epsilon - value to the alpha channel of every image, and then subtracting - or clamping it at the end.) - - 6. You can suppress the behavior described in #5 and make - all-0-alpha pixels have 0 in all channels by #defining - STBIR_NO_ALPHA_EPSILON. - - 7. You can separately control whether the alpha channel is - interpreted as linear or affected by the colorspace. By default - it is linear; you almost never want to apply the colorspace. - (For example, graphics hardware does not apply sRGB conversion - to the alpha channel.) - - CONTRIBUTORS - Jorge L Rodriguez: Implementation - Sean Barrett: API design, optimizations - Aras Pranckevicius: bugfix - Nathan Reed: warning fixes - - REVISIONS - 0.97 (2020-02-02) fixed warning - 0.96 (2019-03-04) fixed warnings - 0.95 (2017-07-23) fixed warnings - 0.94 (2017-03-18) fixed warnings - 0.93 (2017-03-03) fixed bug with certain combinations of heights - 0.92 (2017-01-02) fix integer overflow on large (>2GB) images - 0.91 (2016-04-02) fix warnings; fix handling of subpixel regions - 0.90 (2014-09-17) first released version - - LICENSE - See end of file for license information. - - TODO - Don't decode all of the image data when only processing a partial tile - Don't use full-width decode buffers when only processing a partial tile - When processing wide images, break processing into tiles so data fits in L1 cache - Installable filters? - Resize that respects alpha test coverage - (Reference code: FloatImage::alphaTestCoverage and FloatImage::scaleAlphaToCoverage: - https://code.google.com/p/nvidia-texture-tools/source/browse/trunk/src/nvimage/FloatImage.cpp ) -*/ - -#ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE_H -#define STBIR_INCLUDE_STB_IMAGE_RESIZE_H - -#ifdef _MSC_VER -typedef unsigned char stbir_uint8; -typedef unsigned short stbir_uint16; -typedef unsigned int stbir_uint32; -#else -#include -typedef uint8_t stbir_uint8; -typedef uint16_t stbir_uint16; -typedef uint32_t stbir_uint32; -#endif - -#ifndef STBIRDEF -#ifdef STB_IMAGE_RESIZE_STATIC -#define STBIRDEF static -#else -#ifdef __cplusplus -#define STBIRDEF extern "C" -#else -#define STBIRDEF extern -#endif -#endif -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// Easy-to-use API: -// -// * "input pixels" points to an array of image data with 'num_channels' channels (e.g. RGB=3, RGBA=4) -// * input_w is input image width (x-axis), input_h is input image height (y-axis) -// * stride is the offset between successive rows of image data in memory, in bytes. you can -// specify 0 to mean packed continuously in memory -// * alpha channel is treated identically to other channels. -// * colorspace is linear or sRGB as specified by function name -// * returned result is 1 for success or 0 in case of an error. -// #define STBIR_ASSERT() to trigger an assert on parameter validation errors. -// * Memory required grows approximately linearly with input and output size, but with -// discontinuities at input_w == output_w and input_h == output_h. -// * These functions use a "default" resampling filter defined at compile time. To change the filter, -// you can change the compile-time defaults by #defining STBIR_DEFAULT_FILTER_UPSAMPLE -// and STBIR_DEFAULT_FILTER_DOWNSAMPLE, or you can use the medium-complexity API. - -STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels); - -STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels); - - -// The following functions interpret image data as gamma-corrected sRGB. -// Specify STBIR_ALPHA_CHANNEL_NONE if you have no alpha channel, -// or otherwise provide the index of the alpha channel. Flags value -// of 0 will probably do the right thing if you're not sure what -// the flags mean. - -#define STBIR_ALPHA_CHANNEL_NONE -1 - -// Set this flag if your texture has premultiplied alpha. Otherwise, stbir will -// use alpha-weighted resampling (effectively premultiplying, resampling, -// then unpremultiplying). -#define STBIR_FLAG_ALPHA_PREMULTIPLIED (1 << 0) -// The specified alpha channel should be handled as gamma-corrected value even -// when doing sRGB operations. -#define STBIR_FLAG_ALPHA_USES_COLORSPACE (1 << 1) - -STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags); - - -typedef enum -{ - STBIR_EDGE_CLAMP = 1, - STBIR_EDGE_REFLECT = 2, - STBIR_EDGE_WRAP = 3, - STBIR_EDGE_ZERO = 4, -} stbir_edge; - -// This function adds the ability to specify how requests to sample off the edge of the image are handled. -STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode); - -////////////////////////////////////////////////////////////////////////////// -// -// Medium-complexity API -// -// This extends the easy-to-use API as follows: -// -// * Alpha-channel can be processed separately -// * If alpha_channel is not STBIR_ALPHA_CHANNEL_NONE -// * Alpha channel will not be gamma corrected (unless flags&STBIR_FLAG_GAMMA_CORRECT) -// * Filters will be weighted by alpha channel (unless flags&STBIR_FLAG_ALPHA_PREMULTIPLIED) -// * Filter can be selected explicitly -// * uint16 image type -// * sRGB colorspace available for all types -// * context parameter for passing to STBIR_MALLOC - -typedef enum -{ - STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses - STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios - STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering - STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque - STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline - STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 -} stbir_filter; - -typedef enum -{ - STBIR_COLORSPACE_LINEAR, - STBIR_COLORSPACE_SRGB, - - STBIR_MAX_COLORSPACES, -} stbir_colorspace; - -// The following functions are all identical except for the type of the image data - -STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context); - -STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context); - -STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context); - - - -////////////////////////////////////////////////////////////////////////////// -// -// Full-complexity API -// -// This extends the medium API as follows: -// -// * uint32 image type -// * not typesafe -// * separate filter types for each axis -// * separate edge modes for each axis -// * can specify scale explicitly for subpixel correctness -// * can specify image source tile using texture coordinates - -typedef enum -{ - STBIR_TYPE_UINT8 , - STBIR_TYPE_UINT16, - STBIR_TYPE_UINT32, - STBIR_TYPE_FLOAT , - - STBIR_MAX_TYPES -} stbir_datatype; - -STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context); - -STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context, - float x_scale, float y_scale, - float x_offset, float y_offset); - -STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context, - float s0, float t0, float s1, float t1); -// (s0, t0) & (s1, t1) are the top-left and bottom right corner (uv addressing style: [0, 1]x[0, 1]) of a region of the input image to use. - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE_H - - - - - -#ifdef STB_IMAGE_RESIZE_IMPLEMENTATION - -#ifndef STBIR_ASSERT -#include -#define STBIR_ASSERT(x) assert(x) -#endif - -// For memset -#include - -#include - -#ifndef STBIR_MALLOC -#include -// use comma operator to evaluate c, to avoid "unused parameter" warnings -#define STBIR_MALLOC(size,c) ((void)(c), malloc(size)) -#define STBIR_FREE(ptr,c) ((void)(c), free(ptr)) -#endif - -#ifndef _MSC_VER -#ifdef __cplusplus -#define stbir__inline inline -#else -#define stbir__inline -#endif -#else -#define stbir__inline __forceinline -#endif - - -// should produce compiler error if size is wrong -typedef unsigned char stbir__validate_uint32[sizeof(stbir_uint32) == 4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBIR__NOTUSED(v) (void)(v) -#else -#define STBIR__NOTUSED(v) (void)sizeof(v) -#endif - -#define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) - -#ifndef STBIR_DEFAULT_FILTER_UPSAMPLE -#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM -#endif - -#ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE -#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL -#endif - -#ifndef STBIR_PROGRESS_REPORT -#define STBIR_PROGRESS_REPORT(float_0_to_1) -#endif - -#ifndef STBIR_MAX_CHANNELS -#define STBIR_MAX_CHANNELS 64 -#endif - -#if STBIR_MAX_CHANNELS > 65536 -#error "Too many channels; STBIR_MAX_CHANNELS must be no more than 65536." -// because we store the indices in 16-bit variables -#endif - -// This value is added to alpha just before premultiplication to avoid -// zeroing out color values. It is equivalent to 2^-80. If you don't want -// that behavior (it may interfere if you have floating point images with -// very small alpha values) then you can define STBIR_NO_ALPHA_EPSILON to -// disable it. -#ifndef STBIR_ALPHA_EPSILON -#define STBIR_ALPHA_EPSILON ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) -#endif - - - -#ifdef _MSC_VER -#define STBIR__UNUSED_PARAM(v) (void)(v) -#else -#define STBIR__UNUSED_PARAM(v) (void)sizeof(v) -#endif - -// must match stbir_datatype -static unsigned char stbir__type_size[] = { - 1, // STBIR_TYPE_UINT8 - 2, // STBIR_TYPE_UINT16 - 4, // STBIR_TYPE_UINT32 - 4, // STBIR_TYPE_FLOAT -}; - -// Kernel function centered at 0 -typedef float (stbir__kernel_fn)(float x, float scale); -typedef float (stbir__support_fn)(float scale); - -typedef struct -{ - stbir__kernel_fn* kernel; - stbir__support_fn* support; -} stbir__filter_info; - -// When upsampling, the contributors are which source pixels contribute. -// When downsampling, the contributors are which destination pixels are contributed to. -typedef struct -{ - int n0; // First contributing pixel - int n1; // Last contributing pixel -} stbir__contributors; - -typedef struct -{ - const void* input_data; - int input_w; - int input_h; - int input_stride_bytes; - - void* output_data; - int output_w; - int output_h; - int output_stride_bytes; - - float s0, t0, s1, t1; - - float horizontal_shift; // Units: output pixels - float vertical_shift; // Units: output pixels - float horizontal_scale; - float vertical_scale; - - int channels; - int alpha_channel; - stbir_uint32 flags; - stbir_datatype type; - stbir_filter horizontal_filter; - stbir_filter vertical_filter; - stbir_edge edge_horizontal; - stbir_edge edge_vertical; - stbir_colorspace colorspace; - - stbir__contributors* horizontal_contributors; - float* horizontal_coefficients; - - stbir__contributors* vertical_contributors; - float* vertical_coefficients; - - int decode_buffer_pixels; - float* decode_buffer; - - float* horizontal_buffer; - - // cache these because ceil/floor are inexplicably showing up in profile - int horizontal_coefficient_width; - int vertical_coefficient_width; - int horizontal_filter_pixel_width; - int vertical_filter_pixel_width; - int horizontal_filter_pixel_margin; - int vertical_filter_pixel_margin; - int horizontal_num_contributors; - int vertical_num_contributors; - - int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter) - int ring_buffer_num_entries; // Total number of entries in the ring buffer. - int ring_buffer_first_scanline; - int ring_buffer_last_scanline; - int ring_buffer_begin_index; // first_scanline is at this index in the ring buffer - float* ring_buffer; - - float* encode_buffer; // A temporary buffer to store floats so we don't lose precision while we do multiply-adds. - - int horizontal_contributors_size; - int horizontal_coefficients_size; - int vertical_contributors_size; - int vertical_coefficients_size; - int decode_buffer_size; - int horizontal_buffer_size; - int ring_buffer_size; - int encode_buffer_size; -} stbir__info; - - -static const float stbir__max_uint8_as_float = 255.0f; -static const float stbir__max_uint16_as_float = 65535.0f; -static const double stbir__max_uint32_as_float = 4294967295.0; - - -static stbir__inline int stbir__min(int a, int b) -{ - return a < b ? a : b; -} - -static stbir__inline float stbir__saturate(float x) -{ - if (x < 0) - return 0; - - if (x > 1) - return 1; - - return x; -} - -#ifdef STBIR_SATURATE_INT -static stbir__inline stbir_uint8 stbir__saturate8(int x) -{ - if ((unsigned int) x <= 255) - return x; - - if (x < 0) - return 0; - - return 255; -} - -static stbir__inline stbir_uint16 stbir__saturate16(int x) -{ - if ((unsigned int) x <= 65535) - return x; - - if (x < 0) - return 0; - - return 65535; -} -#endif - -static float stbir__srgb_uchar_to_linear_float[256] = { - 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, - 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, - 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, - 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, - 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, - 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, - 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, - 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, - 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, - 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, - 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, - 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, - 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, - 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, - 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, - 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, - 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, - 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, - 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, - 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, - 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, - 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, - 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, - 0.982251f, 0.991102f, 1.0f -}; - -static float stbir__srgb_to_linear(float f) -{ - if (f <= 0.04045f) - return f / 12.92f; - else - return (float)pow((f + 0.055f) / 1.055f, 2.4f); -} - -static float stbir__linear_to_srgb(float f) -{ - if (f <= 0.0031308f) - return f * 12.92f; - else - return 1.055f * (float)pow(f, 1 / 2.4f) - 0.055f; -} - -#ifndef STBIR_NON_IEEE_FLOAT -// From https://gist.github.com/rygorous/2203834 - -typedef union -{ - stbir_uint32 u; - float f; -} stbir__FP32; - -static const stbir_uint32 fp32_to_srgb8_tab4[104] = { - 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d, - 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a, - 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033, - 0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067, - 0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5, - 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2, - 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143, - 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af, - 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240, - 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300, - 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401, - 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559, - 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723, -}; - -static stbir_uint8 stbir__linear_to_srgb_uchar(float in) -{ - static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps - static const stbir__FP32 minval = { (127-13) << 23 }; - stbir_uint32 tab,bias,scale,t; - stbir__FP32 f; - - // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively. - // The tests are carefully written so that NaNs map to 0, same as in the reference - // implementation. - if (!(in > minval.f)) // written this way to catch NaNs - in = minval.f; - if (in > almostone.f) - in = almostone.f; - - // Do the table lookup and unpack bias, scale - f.f = in; - tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20]; - bias = (tab >> 16) << 9; - scale = tab & 0xffff; - - // Grab next-highest mantissa bits and perform linear interpolation - t = (f.u >> 12) & 0xff; - return (unsigned char) ((bias + scale*t) >> 16); -} - -#else -// sRGB transition values, scaled by 1<<28 -static int stbir__srgb_offset_to_linear_scaled[256] = -{ - 0, 40738, 122216, 203693, 285170, 366648, 448125, 529603, - 611080, 692557, 774035, 855852, 942009, 1033024, 1128971, 1229926, - 1335959, 1447142, 1563542, 1685229, 1812268, 1944725, 2082664, 2226148, - 2375238, 2529996, 2690481, 2856753, 3028870, 3206888, 3390865, 3580856, - 3776916, 3979100, 4187460, 4402049, 4622919, 4850123, 5083710, 5323731, - 5570236, 5823273, 6082892, 6349140, 6622065, 6901714, 7188133, 7481369, - 7781466, 8088471, 8402427, 8723380, 9051372, 9386448, 9728650, 10078021, - 10434603, 10798439, 11169569, 11548036, 11933879, 12327139, 12727857, 13136073, - 13551826, 13975156, 14406100, 14844697, 15290987, 15745007, 16206795, 16676389, - 17153826, 17639142, 18132374, 18633560, 19142734, 19659934, 20185196, 20718552, - 21260042, 21809696, 22367554, 22933648, 23508010, 24090680, 24681686, 25281066, - 25888850, 26505076, 27129772, 27762974, 28404716, 29055026, 29713942, 30381490, - 31057708, 31742624, 32436272, 33138682, 33849884, 34569912, 35298800, 36036568, - 36783260, 37538896, 38303512, 39077136, 39859796, 40651528, 41452360, 42262316, - 43081432, 43909732, 44747252, 45594016, 46450052, 47315392, 48190064, 49074096, - 49967516, 50870356, 51782636, 52704392, 53635648, 54576432, 55526772, 56486700, - 57456236, 58435408, 59424248, 60422780, 61431036, 62449032, 63476804, 64514376, - 65561776, 66619028, 67686160, 68763192, 69850160, 70947088, 72053992, 73170912, - 74297864, 75434880, 76581976, 77739184, 78906536, 80084040, 81271736, 82469648, - 83677792, 84896192, 86124888, 87363888, 88613232, 89872928, 91143016, 92423512, - 93714432, 95015816, 96327688, 97650056, 98982952, 100326408, 101680440, 103045072, - 104420320, 105806224, 107202800, 108610064, 110028048, 111456776, 112896264, 114346544, - 115807632, 117279552, 118762328, 120255976, 121760536, 123276016, 124802440, 126339832, - 127888216, 129447616, 131018048, 132599544, 134192112, 135795792, 137410592, 139036528, - 140673648, 142321952, 143981456, 145652208, 147334208, 149027488, 150732064, 152447968, - 154175200, 155913792, 157663776, 159425168, 161197984, 162982240, 164777968, 166585184, - 168403904, 170234160, 172075968, 173929344, 175794320, 177670896, 179559120, 181458992, - 183370528, 185293776, 187228736, 189175424, 191133888, 193104112, 195086128, 197079968, - 199085648, 201103184, 203132592, 205173888, 207227120, 209292272, 211369392, 213458480, - 215559568, 217672656, 219797792, 221934976, 224084240, 226245600, 228419056, 230604656, - 232802400, 235012320, 237234432, 239468736, 241715280, 243974080, 246245120, 248528464, - 250824112, 253132064, 255452368, 257785040, 260130080, 262487520, 264857376, 267239664, -}; - -static stbir_uint8 stbir__linear_to_srgb_uchar(float f) -{ - int x = (int) (f * (1 << 28)); // has headroom so you don't need to clamp - int v = 0; - int i; - - // Refine the guess with a short binary search. - i = v + 128; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 64; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 32; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 16; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 8; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 4; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 2; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - i = v + 1; if (x >= stbir__srgb_offset_to_linear_scaled[i]) v = i; - - return (stbir_uint8) v; -} -#endif - -static float stbir__filter_trapezoid(float x, float scale) -{ - float halfscale = scale / 2; - float t = 0.5f + halfscale; - STBIR_ASSERT(scale <= 1); - - x = (float)fabs(x); - - if (x >= t) - return 0; - else - { - float r = 0.5f - halfscale; - if (x <= r) - return 1; - else - return (t - x) / scale; - } -} - -static float stbir__support_trapezoid(float scale) -{ - STBIR_ASSERT(scale <= 1); - return 0.5f + scale / 2; -} - -static float stbir__filter_triangle(float x, float s) -{ - STBIR__UNUSED_PARAM(s); - - x = (float)fabs(x); - - if (x <= 1.0f) - return 1 - x; - else - return 0; -} - -static float stbir__filter_cubic(float x, float s) -{ - STBIR__UNUSED_PARAM(s); - - x = (float)fabs(x); - - if (x < 1.0f) - return (4 + x*x*(3*x - 6))/6; - else if (x < 2.0f) - return (8 + x*(-12 + x*(6 - x)))/6; - - return (0.0f); -} - -static float stbir__filter_catmullrom(float x, float s) -{ - STBIR__UNUSED_PARAM(s); - - x = (float)fabs(x); - - if (x < 1.0f) - return 1 - x*x*(2.5f - 1.5f*x); - else if (x < 2.0f) - return 2 - x*(4 + x*(0.5f*x - 2.5f)); - - return (0.0f); -} - -static float stbir__filter_mitchell(float x, float s) -{ - STBIR__UNUSED_PARAM(s); - - x = (float)fabs(x); - - if (x < 1.0f) - return (16 + x*x*(21 * x - 36))/18; - else if (x < 2.0f) - return (32 + x*(-60 + x*(36 - 7*x)))/18; - - return (0.0f); -} - -static float stbir__support_zero(float s) -{ - STBIR__UNUSED_PARAM(s); - return 0; -} - -static float stbir__support_one(float s) -{ - STBIR__UNUSED_PARAM(s); - return 1; -} - -static float stbir__support_two(float s) -{ - STBIR__UNUSED_PARAM(s); - return 2; -} - -static stbir__filter_info stbir__filter_info_table[] = { - { NULL, stbir__support_zero }, - { stbir__filter_trapezoid, stbir__support_trapezoid }, - { stbir__filter_triangle, stbir__support_one }, - { stbir__filter_cubic, stbir__support_two }, - { stbir__filter_catmullrom, stbir__support_two }, - { stbir__filter_mitchell, stbir__support_two }, -}; - -stbir__inline static int stbir__use_upsampling(float ratio) -{ - return ratio > 1; -} - -stbir__inline static int stbir__use_width_upsampling(stbir__info* stbir_info) -{ - return stbir__use_upsampling(stbir_info->horizontal_scale); -} - -stbir__inline static int stbir__use_height_upsampling(stbir__info* stbir_info) -{ - return stbir__use_upsampling(stbir_info->vertical_scale); -} - -// This is the maximum number of input samples that can affect an output sample -// with the given filter -static int stbir__get_filter_pixel_width(stbir_filter filter, float scale) -{ - STBIR_ASSERT(filter != 0); - STBIR_ASSERT(filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - - if (stbir__use_upsampling(scale)) - return (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2); - else - return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2 / scale); -} - -// This is how much to expand buffers to account for filters seeking outside -// the image boundaries. -static int stbir__get_filter_pixel_margin(stbir_filter filter, float scale) -{ - return stbir__get_filter_pixel_width(filter, scale) / 2; -} - -static int stbir__get_coefficient_width(stbir_filter filter, float scale) -{ - if (stbir__use_upsampling(scale)) - return (int)ceil(stbir__filter_info_table[filter].support(1 / scale) * 2); - else - return (int)ceil(stbir__filter_info_table[filter].support(scale) * 2); -} - -static int stbir__get_contributors(float scale, stbir_filter filter, int input_size, int output_size) -{ - if (stbir__use_upsampling(scale)) - return output_size; - else - return (input_size + stbir__get_filter_pixel_margin(filter, scale) * 2); -} - -static int stbir__get_total_horizontal_coefficients(stbir__info* info) -{ - return info->horizontal_num_contributors - * stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); -} - -static int stbir__get_total_vertical_coefficients(stbir__info* info) -{ - return info->vertical_num_contributors - * stbir__get_coefficient_width (info->vertical_filter, info->vertical_scale); -} - -static stbir__contributors* stbir__get_contributor(stbir__contributors* contributors, int n) -{ - return &contributors[n]; -} - -// For perf reasons this code is duplicated in stbir__resample_horizontal_upsample/downsample, -// if you change it here change it there too. -static float* stbir__get_coefficient(float* coefficients, stbir_filter filter, float scale, int n, int c) -{ - int width = stbir__get_coefficient_width(filter, scale); - return &coefficients[width*n + c]; -} - -static int stbir__edge_wrap_slow(stbir_edge edge, int n, int max) -{ - switch (edge) - { - case STBIR_EDGE_ZERO: - return 0; // we'll decode the wrong pixel here, and then overwrite with 0s later - - case STBIR_EDGE_CLAMP: - if (n < 0) - return 0; - - if (n >= max) - return max - 1; - - return n; // NOTREACHED - - case STBIR_EDGE_REFLECT: - { - if (n < 0) - { - if (n < max) - return -n; - else - return max - 1; - } - - if (n >= max) - { - int max2 = max * 2; - if (n >= max2) - return 0; - else - return max2 - n - 1; - } - - return n; // NOTREACHED - } - - case STBIR_EDGE_WRAP: - if (n >= 0) - return (n % max); - else - { - int m = (-n) % max; - - if (m != 0) - m = max - m; - - return (m); - } - // NOTREACHED - - default: - STBIR_ASSERT(!"Unimplemented edge type"); - return 0; - } -} - -stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max) -{ - // avoid per-pixel switch - if (n >= 0 && n < max) - return n; - return stbir__edge_wrap_slow(edge, n, max); -} - -// What input pixels contribute to this output pixel? -static void stbir__calculate_sample_range_upsample(int n, float out_filter_radius, float scale_ratio, float out_shift, int* in_first_pixel, int* in_last_pixel, float* in_center_of_out) -{ - float out_pixel_center = (float)n + 0.5f; - float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; - float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; - - float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) / scale_ratio; - float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) / scale_ratio; - - *in_center_of_out = (out_pixel_center + out_shift) / scale_ratio; - *in_first_pixel = (int)(floor(in_pixel_influence_lowerbound + 0.5)); - *in_last_pixel = (int)(floor(in_pixel_influence_upperbound - 0.5)); -} - -// What output pixels does this input pixel contribute to? -static void stbir__calculate_sample_range_downsample(int n, float in_pixels_radius, float scale_ratio, float out_shift, int* out_first_pixel, int* out_last_pixel, float* out_center_of_in) -{ - float in_pixel_center = (float)n + 0.5f; - float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; - float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; - - float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale_ratio - out_shift; - float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale_ratio - out_shift; - - *out_center_of_in = in_pixel_center * scale_ratio - out_shift; - *out_first_pixel = (int)(floor(out_pixel_influence_lowerbound + 0.5)); - *out_last_pixel = (int)(floor(out_pixel_influence_upperbound - 0.5)); -} - -static void stbir__calculate_coefficients_upsample(stbir_filter filter, float scale, int in_first_pixel, int in_last_pixel, float in_center_of_out, stbir__contributors* contributor, float* coefficient_group) -{ - int i; - float total_filter = 0; - float filter_scale; - - STBIR_ASSERT(in_last_pixel - in_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(1/scale) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. - - contributor->n0 = in_first_pixel; - contributor->n1 = in_last_pixel; - - STBIR_ASSERT(contributor->n1 >= contributor->n0); - - for (i = 0; i <= in_last_pixel - in_first_pixel; i++) - { - float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; - coefficient_group[i] = stbir__filter_info_table[filter].kernel(in_center_of_out - in_pixel_center, 1 / scale); - - // If the coefficient is zero, skip it. (Don't do the <0 check here, we want the influence of those outside pixels.) - if (i == 0 && !coefficient_group[i]) - { - contributor->n0 = ++in_first_pixel; - i--; - continue; - } - - total_filter += coefficient_group[i]; - } - - // NOTE(fg): Not actually true in general, nor is there any reason to expect it should be. - // It would be true in exact math but is at best approximately true in floating-point math, - // and it would not make sense to try and put actual bounds on this here because it depends - // on the image aspect ratio which can get pretty extreme. - //STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(in_last_pixel + 1) + 0.5f - in_center_of_out, 1/scale) == 0); - - STBIR_ASSERT(total_filter > 0.9); - STBIR_ASSERT(total_filter < 1.1f); // Make sure it's not way off. - - // Make sure the sum of all coefficients is 1. - filter_scale = 1 / total_filter; - - for (i = 0; i <= in_last_pixel - in_first_pixel; i++) - coefficient_group[i] *= filter_scale; - - for (i = in_last_pixel - in_first_pixel; i >= 0; i--) - { - if (coefficient_group[i]) - break; - - // This line has no weight. We can skip it. - contributor->n1 = contributor->n0 + i - 1; - } -} - -static void stbir__calculate_coefficients_downsample(stbir_filter filter, float scale_ratio, int out_first_pixel, int out_last_pixel, float out_center_of_in, stbir__contributors* contributor, float* coefficient_group) -{ - int i; - - STBIR_ASSERT(out_last_pixel - out_first_pixel <= (int)ceil(stbir__filter_info_table[filter].support(scale_ratio) * 2)); // Taken directly from stbir__get_coefficient_width() which we can't call because we don't know if we're horizontal or vertical. - - contributor->n0 = out_first_pixel; - contributor->n1 = out_last_pixel; - - STBIR_ASSERT(contributor->n1 >= contributor->n0); - - for (i = 0; i <= out_last_pixel - out_first_pixel; i++) - { - float out_pixel_center = (float)(i + out_first_pixel) + 0.5f; - float x = out_pixel_center - out_center_of_in; - coefficient_group[i] = stbir__filter_info_table[filter].kernel(x, scale_ratio) * scale_ratio; - } - - // NOTE(fg): Not actually true in general, nor is there any reason to expect it should be. - // It would be true in exact math but is at best approximately true in floating-point math, - // and it would not make sense to try and put actual bounds on this here because it depends - // on the image aspect ratio which can get pretty extreme. - //STBIR_ASSERT(stbir__filter_info_table[filter].kernel((float)(out_last_pixel + 1) + 0.5f - out_center_of_in, scale_ratio) == 0); - - for (i = out_last_pixel - out_first_pixel; i >= 0; i--) - { - if (coefficient_group[i]) - break; - - // This line has no weight. We can skip it. - contributor->n1 = contributor->n0 + i - 1; - } -} - -static void stbir__normalize_downsample_coefficients(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, int input_size, int output_size) -{ - int num_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); - int num_coefficients = stbir__get_coefficient_width(filter, scale_ratio); - int i, j; - int skip; - - for (i = 0; i < output_size; i++) - { - float scale; - float total = 0; - - for (j = 0; j < num_contributors; j++) - { - if (i >= contributors[j].n0 && i <= contributors[j].n1) - { - float coefficient = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0); - total += coefficient; - } - else if (i < contributors[j].n0) - break; - } - - STBIR_ASSERT(total > 0.9f); - STBIR_ASSERT(total < 1.1f); - - scale = 1 / total; - - for (j = 0; j < num_contributors; j++) - { - if (i >= contributors[j].n0 && i <= contributors[j].n1) - *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i - contributors[j].n0) *= scale; - else if (i < contributors[j].n0) - break; - } - } - - // Optimize: Skip zero coefficients and contributions outside of image bounds. - // Do this after normalizing because normalization depends on the n0/n1 values. - for (j = 0; j < num_contributors; j++) - { - int range, max, width; - - skip = 0; - while (*stbir__get_coefficient(coefficients, filter, scale_ratio, j, skip) == 0) - skip++; - - contributors[j].n0 += skip; - - while (contributors[j].n0 < 0) - { - contributors[j].n0++; - skip++; - } - - range = contributors[j].n1 - contributors[j].n0 + 1; - max = stbir__min(num_coefficients, range); - - width = stbir__get_coefficient_width(filter, scale_ratio); - for (i = 0; i < max; i++) - { - if (i + skip >= width) - break; - - *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i) = *stbir__get_coefficient(coefficients, filter, scale_ratio, j, i + skip); - } - - continue; - } - - // Using min to avoid writing into invalid pixels. - for (i = 0; i < num_contributors; i++) - contributors[i].n1 = stbir__min(contributors[i].n1, output_size - 1); -} - -// Each scan line uses the same kernel values so we should calculate the kernel -// values once and then we can use them for every scan line. -static void stbir__calculate_filters(stbir__contributors* contributors, float* coefficients, stbir_filter filter, float scale_ratio, float shift, int input_size, int output_size) -{ - int n; - int total_contributors = stbir__get_contributors(scale_ratio, filter, input_size, output_size); - - if (stbir__use_upsampling(scale_ratio)) - { - float out_pixels_radius = stbir__filter_info_table[filter].support(1 / scale_ratio) * scale_ratio; - - // Looping through out pixels - for (n = 0; n < total_contributors; n++) - { - float in_center_of_out; // Center of the current out pixel in the in pixel space - int in_first_pixel, in_last_pixel; - - stbir__calculate_sample_range_upsample(n, out_pixels_radius, scale_ratio, shift, &in_first_pixel, &in_last_pixel, &in_center_of_out); - - stbir__calculate_coefficients_upsample(filter, scale_ratio, in_first_pixel, in_last_pixel, in_center_of_out, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); - } - } - else - { - float in_pixels_radius = stbir__filter_info_table[filter].support(scale_ratio) / scale_ratio; - - // Looping through in pixels - for (n = 0; n < total_contributors; n++) - { - float out_center_of_in; // Center of the current out pixel in the in pixel space - int out_first_pixel, out_last_pixel; - int n_adjusted = n - stbir__get_filter_pixel_margin(filter, scale_ratio); - - stbir__calculate_sample_range_downsample(n_adjusted, in_pixels_radius, scale_ratio, shift, &out_first_pixel, &out_last_pixel, &out_center_of_in); - - stbir__calculate_coefficients_downsample(filter, scale_ratio, out_first_pixel, out_last_pixel, out_center_of_in, stbir__get_contributor(contributors, n), stbir__get_coefficient(coefficients, filter, scale_ratio, n, 0)); - } - - stbir__normalize_downsample_coefficients(contributors, coefficients, filter, scale_ratio, input_size, output_size); - } -} - -static float* stbir__get_decode_buffer(stbir__info* stbir_info) -{ - // The 0 index of the decode buffer starts after the margin. This makes - // it okay to use negative indexes on the decode buffer. - return &stbir_info->decode_buffer[stbir_info->horizontal_filter_pixel_margin * stbir_info->channels]; -} - -#define STBIR__DECODE(type, colorspace) ((int)(type) * (STBIR_MAX_COLORSPACES) + (int)(colorspace)) - -static void stbir__decode_scanline(stbir__info* stbir_info, int n) -{ - int c; - int channels = stbir_info->channels; - int alpha_channel = stbir_info->alpha_channel; - int type = stbir_info->type; - int colorspace = stbir_info->colorspace; - int input_w = stbir_info->input_w; - size_t input_stride_bytes = stbir_info->input_stride_bytes; - float* decode_buffer = stbir__get_decode_buffer(stbir_info); - stbir_edge edge_horizontal = stbir_info->edge_horizontal; - stbir_edge edge_vertical = stbir_info->edge_vertical; - size_t in_buffer_row_offset = stbir__edge_wrap(edge_vertical, n, stbir_info->input_h) * input_stride_bytes; - const void* input_data = (char *) stbir_info->input_data + in_buffer_row_offset; - int max_x = input_w + stbir_info->horizontal_filter_pixel_margin; - int decode = STBIR__DECODE(type, colorspace); - - int x = -stbir_info->horizontal_filter_pixel_margin; - - // special handling for STBIR_EDGE_ZERO because it needs to return an item that doesn't appear in the input, - // and we want to avoid paying overhead on every pixel if not STBIR_EDGE_ZERO - if (edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->input_h)) - { - for (; x < max_x; x++) - for (c = 0; c < channels; c++) - decode_buffer[x*channels + c] = 0; - return; - } - - switch (decode) - { - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = ((float)((const unsigned char*)input_data)[input_pixel_index + c]) / stbir__max_uint8_as_float; - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_uchar_to_linear_float[((const unsigned char*)input_data)[input_pixel_index + c]]; - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned char*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint8_as_float; - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = ((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float; - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((float)((const unsigned short*)input_data)[input_pixel_index + c]) / stbir__max_uint16_as_float); - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = ((float)((const unsigned short*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint16_as_float; - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float); - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear((float)(((double)((const unsigned int*)input_data)[input_pixel_index + c]) / stbir__max_uint32_as_float)); - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = (float)(((double)((const unsigned int*)input_data)[input_pixel_index + alpha_channel]) / stbir__max_uint32_as_float); - } - break; - - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = ((const float*)input_data)[input_pixel_index + c]; - } - break; - - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): - for (; x < max_x; x++) - { - int decode_pixel_index = x * channels; - int input_pixel_index = stbir__edge_wrap(edge_horizontal, x, input_w) * channels; - for (c = 0; c < channels; c++) - decode_buffer[decode_pixel_index + c] = stbir__srgb_to_linear(((const float*)input_data)[input_pixel_index + c]); - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - decode_buffer[decode_pixel_index + alpha_channel] = ((const float*)input_data)[input_pixel_index + alpha_channel]; - } - - break; - - default: - STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); - break; - } - - if (!(stbir_info->flags & STBIR_FLAG_ALPHA_PREMULTIPLIED)) - { - for (x = -stbir_info->horizontal_filter_pixel_margin; x < max_x; x++) - { - int decode_pixel_index = x * channels; - - // If the alpha value is 0 it will clobber the color values. Make sure it's not. - float alpha = decode_buffer[decode_pixel_index + alpha_channel]; -#ifndef STBIR_NO_ALPHA_EPSILON - if (stbir_info->type != STBIR_TYPE_FLOAT) { - alpha += STBIR_ALPHA_EPSILON; - decode_buffer[decode_pixel_index + alpha_channel] = alpha; - } -#endif - for (c = 0; c < channels; c++) - { - if (c == alpha_channel) - continue; - - decode_buffer[decode_pixel_index + c] *= alpha; - } - } - } - - if (edge_horizontal == STBIR_EDGE_ZERO) - { - for (x = -stbir_info->horizontal_filter_pixel_margin; x < 0; x++) - { - for (c = 0; c < channels; c++) - decode_buffer[x*channels + c] = 0; - } - for (x = input_w; x < max_x; x++) - { - for (c = 0; c < channels; c++) - decode_buffer[x*channels + c] = 0; - } - } -} - -static float* stbir__get_ring_buffer_entry(float* ring_buffer, int index, int ring_buffer_length) -{ - return &ring_buffer[index * ring_buffer_length]; -} - -static float* stbir__add_empty_ring_buffer_entry(stbir__info* stbir_info, int n) -{ - int ring_buffer_index; - float* ring_buffer; - - stbir_info->ring_buffer_last_scanline = n; - - if (stbir_info->ring_buffer_begin_index < 0) - { - ring_buffer_index = stbir_info->ring_buffer_begin_index = 0; - stbir_info->ring_buffer_first_scanline = n; - } - else - { - ring_buffer_index = (stbir_info->ring_buffer_begin_index + (stbir_info->ring_buffer_last_scanline - stbir_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries; - STBIR_ASSERT(ring_buffer_index != stbir_info->ring_buffer_begin_index); - } - - ring_buffer = stbir__get_ring_buffer_entry(stbir_info->ring_buffer, ring_buffer_index, stbir_info->ring_buffer_length_bytes / sizeof(float)); - memset(ring_buffer, 0, stbir_info->ring_buffer_length_bytes); - - return ring_buffer; -} - - -static void stbir__resample_horizontal_upsample(stbir__info* stbir_info, float* output_buffer) -{ - int x, k; - int output_w = stbir_info->output_w; - int channels = stbir_info->channels; - float* decode_buffer = stbir__get_decode_buffer(stbir_info); - stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; - float* horizontal_coefficients = stbir_info->horizontal_coefficients; - int coefficient_width = stbir_info->horizontal_coefficient_width; - - for (x = 0; x < output_w; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int out_pixel_index = x * channels; - int coefficient_group = coefficient_width * x; - int coefficient_counter = 0; - - STBIR_ASSERT(n1 >= n0); - STBIR_ASSERT(n0 >= -stbir_info->horizontal_filter_pixel_margin); - STBIR_ASSERT(n1 >= -stbir_info->horizontal_filter_pixel_margin); - STBIR_ASSERT(n0 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); - STBIR_ASSERT(n1 < stbir_info->input_w + stbir_info->horizontal_filter_pixel_margin); - - switch (channels) { - case 1: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * 1; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - } - break; - case 2: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * 2; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - } - break; - case 3: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * 3; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; - } - break; - case 4: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * 4; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - STBIR_ASSERT(coefficient != 0); - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; - output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; - } - break; - default: - for (k = n0; k <= n1; k++) - { - int in_pixel_index = k * channels; - float coefficient = horizontal_coefficients[coefficient_group + coefficient_counter++]; - int c; - STBIR_ASSERT(coefficient != 0); - for (c = 0; c < channels; c++) - output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; - } - break; - } - } -} - -static void stbir__resample_horizontal_downsample(stbir__info* stbir_info, float* output_buffer) -{ - int x, k; - int input_w = stbir_info->input_w; - int channels = stbir_info->channels; - float* decode_buffer = stbir__get_decode_buffer(stbir_info); - stbir__contributors* horizontal_contributors = stbir_info->horizontal_contributors; - float* horizontal_coefficients = stbir_info->horizontal_coefficients; - int coefficient_width = stbir_info->horizontal_coefficient_width; - int filter_pixel_margin = stbir_info->horizontal_filter_pixel_margin; - int max_x = input_w + filter_pixel_margin * 2; - - STBIR_ASSERT(!stbir__use_width_upsampling(stbir_info)); - - switch (channels) { - case 1: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * 1; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * 1; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - } - } - break; - - case 2: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * 2; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * 2; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - } - } - break; - - case 3: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * 3; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * 3; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; - } - } - break; - - case 4: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * 4; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int out_pixel_index = k * 4; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - output_buffer[out_pixel_index + 0] += decode_buffer[in_pixel_index + 0] * coefficient; - output_buffer[out_pixel_index + 1] += decode_buffer[in_pixel_index + 1] * coefficient; - output_buffer[out_pixel_index + 2] += decode_buffer[in_pixel_index + 2] * coefficient; - output_buffer[out_pixel_index + 3] += decode_buffer[in_pixel_index + 3] * coefficient; - } - } - break; - - default: - for (x = 0; x < max_x; x++) - { - int n0 = horizontal_contributors[x].n0; - int n1 = horizontal_contributors[x].n1; - - int in_x = x - filter_pixel_margin; - int in_pixel_index = in_x * channels; - int max_n = n1; - int coefficient_group = coefficient_width * x; - - for (k = n0; k <= max_n; k++) - { - int c; - int out_pixel_index = k * channels; - float coefficient = horizontal_coefficients[coefficient_group + k - n0]; - for (c = 0; c < channels; c++) - output_buffer[out_pixel_index + c] += decode_buffer[in_pixel_index + c] * coefficient; - } - } - break; - } -} - -static void stbir__decode_and_resample_upsample(stbir__info* stbir_info, int n) -{ - // Decode the nth scanline from the source image into the decode buffer. - stbir__decode_scanline(stbir_info, n); - - // Now resample it into the ring buffer. - if (stbir__use_width_upsampling(stbir_info)) - stbir__resample_horizontal_upsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n)); - else - stbir__resample_horizontal_downsample(stbir_info, stbir__add_empty_ring_buffer_entry(stbir_info, n)); - - // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. -} - -static void stbir__decode_and_resample_downsample(stbir__info* stbir_info, int n) -{ - // Decode the nth scanline from the source image into the decode buffer. - stbir__decode_scanline(stbir_info, n); - - memset(stbir_info->horizontal_buffer, 0, stbir_info->output_w * stbir_info->channels * sizeof(float)); - - // Now resample it into the horizontal buffer. - if (stbir__use_width_upsampling(stbir_info)) - stbir__resample_horizontal_upsample(stbir_info, stbir_info->horizontal_buffer); - else - stbir__resample_horizontal_downsample(stbir_info, stbir_info->horizontal_buffer); - - // Now it's sitting in the horizontal buffer ready to be distributed into the ring buffers. -} - -// Get the specified scan line from the ring buffer. -static float* stbir__get_ring_buffer_scanline(int get_scanline, float* ring_buffer, int begin_index, int first_scanline, int ring_buffer_num_entries, int ring_buffer_length) -{ - int ring_buffer_index = (begin_index + (get_scanline - first_scanline)) % ring_buffer_num_entries; - return stbir__get_ring_buffer_entry(ring_buffer, ring_buffer_index, ring_buffer_length); -} - - -static void stbir__encode_scanline(stbir__info* stbir_info, int num_pixels, void *output_buffer, float *encode_buffer, int channels, int alpha_channel, int decode) -{ - int x; - int n; - int num_nonalpha; - stbir_uint16 nonalpha[STBIR_MAX_CHANNELS]; - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) - { - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - float alpha = encode_buffer[pixel_index + alpha_channel]; - float reciprocal_alpha = alpha ? 1.0f / alpha : 0; - - // unrolling this produced a 1% slowdown upscaling a large RGBA linear-space image on my machine - stb - for (n = 0; n < channels; n++) - if (n != alpha_channel) - encode_buffer[pixel_index + n] *= reciprocal_alpha; - - // We added in a small epsilon to prevent the color channel from being deleted with zero alpha. - // Because we only add it for integer types, it will automatically be discarded on integer - // conversion, so we don't need to subtract it back out (which would be problematic for - // numeric precision reasons). - } - } - - // build a table of all channels that need colorspace correction, so - // we don't perform colorspace correction on channels that don't need it. - for (x = 0, num_nonalpha = 0; x < channels; ++x) - { - if (x != alpha_channel || (stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) - { - nonalpha[num_nonalpha++] = (stbir_uint16)x; - } - } - - #define STBIR__ROUND_INT(f) ((int) ((f)+0.5)) - #define STBIR__ROUND_UINT(f) ((stbir_uint32) ((f)+0.5)) - - #ifdef STBIR__SATURATE_INT - #define STBIR__ENCODE_LINEAR8(f) stbir__saturate8 (STBIR__ROUND_INT((f) * stbir__max_uint8_as_float )) - #define STBIR__ENCODE_LINEAR16(f) stbir__saturate16(STBIR__ROUND_INT((f) * stbir__max_uint16_as_float)) - #else - #define STBIR__ENCODE_LINEAR8(f) (unsigned char ) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint8_as_float ) - #define STBIR__ENCODE_LINEAR16(f) (unsigned short) STBIR__ROUND_INT(stbir__saturate(f) * stbir__max_uint16_as_float) - #endif - - switch (decode) - { - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((unsigned char*)output_buffer)[index] = STBIR__ENCODE_LINEAR8(encode_buffer[index]); - } - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT8, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < num_nonalpha; n++) - { - int index = pixel_index + nonalpha[n]; - ((unsigned char*)output_buffer)[index] = stbir__linear_to_srgb_uchar(encode_buffer[index]); - } - - if (!(stbir_info->flags & STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned char *)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR8(encode_buffer[pixel_index+alpha_channel]); - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((unsigned short*)output_buffer)[index] = STBIR__ENCODE_LINEAR16(encode_buffer[index]); - } - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT16, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < num_nonalpha; n++) - { - int index = pixel_index + nonalpha[n]; - ((unsigned short*)output_buffer)[index] = (unsigned short)STBIR__ROUND_INT(stbir__linear_to_srgb(stbir__saturate(encode_buffer[index])) * stbir__max_uint16_as_float); - } - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned short*)output_buffer)[pixel_index + alpha_channel] = STBIR__ENCODE_LINEAR16(encode_buffer[pixel_index + alpha_channel]); - } - - break; - - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__saturate(encode_buffer[index])) * stbir__max_uint32_as_float); - } - } - break; - - case STBIR__DECODE(STBIR_TYPE_UINT32, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < num_nonalpha; n++) - { - int index = pixel_index + nonalpha[n]; - ((unsigned int*)output_buffer)[index] = (unsigned int)STBIR__ROUND_UINT(((double)stbir__linear_to_srgb(stbir__saturate(encode_buffer[index]))) * stbir__max_uint32_as_float); - } - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((unsigned int*)output_buffer)[pixel_index + alpha_channel] = (unsigned int)STBIR__ROUND_INT(((double)stbir__saturate(encode_buffer[pixel_index + alpha_channel])) * stbir__max_uint32_as_float); - } - break; - - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_LINEAR): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < channels; n++) - { - int index = pixel_index + n; - ((float*)output_buffer)[index] = encode_buffer[index]; - } - } - break; - - case STBIR__DECODE(STBIR_TYPE_FLOAT, STBIR_COLORSPACE_SRGB): - for (x=0; x < num_pixels; ++x) - { - int pixel_index = x*channels; - - for (n = 0; n < num_nonalpha; n++) - { - int index = pixel_index + nonalpha[n]; - ((float*)output_buffer)[index] = stbir__linear_to_srgb(encode_buffer[index]); - } - - if (!(stbir_info->flags&STBIR_FLAG_ALPHA_USES_COLORSPACE)) - ((float*)output_buffer)[pixel_index + alpha_channel] = encode_buffer[pixel_index + alpha_channel]; - } - break; - - default: - STBIR_ASSERT(!"Unknown type/colorspace/channels combination."); - break; - } -} - -static void stbir__resample_vertical_upsample(stbir__info* stbir_info, int n) -{ - int x, k; - int output_w = stbir_info->output_w; - stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; - float* vertical_coefficients = stbir_info->vertical_coefficients; - int channels = stbir_info->channels; - int alpha_channel = stbir_info->alpha_channel; - int type = stbir_info->type; - int colorspace = stbir_info->colorspace; - int ring_buffer_entries = stbir_info->ring_buffer_num_entries; - void* output_data = stbir_info->output_data; - float* encode_buffer = stbir_info->encode_buffer; - int decode = STBIR__DECODE(type, colorspace); - int coefficient_width = stbir_info->vertical_coefficient_width; - int coefficient_counter; - int contributor = n; - - float* ring_buffer = stbir_info->ring_buffer; - int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; - int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; - int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); - - int n0,n1, output_row_start; - int coefficient_group = coefficient_width * contributor; - - n0 = vertical_contributors[contributor].n0; - n1 = vertical_contributors[contributor].n1; - - output_row_start = n * stbir_info->output_stride_bytes; - - STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); - - memset(encode_buffer, 0, output_w * sizeof(float) * channels); - - // I tried reblocking this for better cache usage of encode_buffer - // (using x_outer, k, x_inner), but it lost speed. -- stb - - coefficient_counter = 0; - switch (channels) { - case 1: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * 1; - encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; - } - } - break; - case 2: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * 2; - encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; - encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; - } - } - break; - case 3: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * 3; - encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; - encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; - encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; - } - } - break; - case 4: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * 4; - encode_buffer[in_pixel_index + 0] += ring_buffer_entry[in_pixel_index + 0] * coefficient; - encode_buffer[in_pixel_index + 1] += ring_buffer_entry[in_pixel_index + 1] * coefficient; - encode_buffer[in_pixel_index + 2] += ring_buffer_entry[in_pixel_index + 2] * coefficient; - encode_buffer[in_pixel_index + 3] += ring_buffer_entry[in_pixel_index + 3] * coefficient; - } - } - break; - default: - for (k = n0; k <= n1; k++) - { - int coefficient_index = coefficient_counter++; - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - for (x = 0; x < output_w; ++x) - { - int in_pixel_index = x * channels; - int c; - for (c = 0; c < channels; c++) - encode_buffer[in_pixel_index + c] += ring_buffer_entry[in_pixel_index + c] * coefficient; - } - } - break; - } - stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, encode_buffer, channels, alpha_channel, decode); -} - -static void stbir__resample_vertical_downsample(stbir__info* stbir_info, int n) -{ - int x, k; - int output_w = stbir_info->output_w; - stbir__contributors* vertical_contributors = stbir_info->vertical_contributors; - float* vertical_coefficients = stbir_info->vertical_coefficients; - int channels = stbir_info->channels; - int ring_buffer_entries = stbir_info->ring_buffer_num_entries; - float* horizontal_buffer = stbir_info->horizontal_buffer; - int coefficient_width = stbir_info->vertical_coefficient_width; - int contributor = n + stbir_info->vertical_filter_pixel_margin; - - float* ring_buffer = stbir_info->ring_buffer; - int ring_buffer_begin_index = stbir_info->ring_buffer_begin_index; - int ring_buffer_first_scanline = stbir_info->ring_buffer_first_scanline; - int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); - int n0,n1; - - n0 = vertical_contributors[contributor].n0; - n1 = vertical_contributors[contributor].n1; - - STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); - - for (k = n0; k <= n1; k++) - { - int coefficient_index = k - n0; - int coefficient_group = coefficient_width * contributor; - float coefficient = vertical_coefficients[coefficient_group + coefficient_index]; - - float* ring_buffer_entry = stbir__get_ring_buffer_scanline(k, ring_buffer, ring_buffer_begin_index, ring_buffer_first_scanline, ring_buffer_entries, ring_buffer_length); - - switch (channels) { - case 1: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * 1; - ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; - } - break; - case 2: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * 2; - ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; - ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; - } - break; - case 3: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * 3; - ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; - ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; - ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; - } - break; - case 4: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * 4; - ring_buffer_entry[in_pixel_index + 0] += horizontal_buffer[in_pixel_index + 0] * coefficient; - ring_buffer_entry[in_pixel_index + 1] += horizontal_buffer[in_pixel_index + 1] * coefficient; - ring_buffer_entry[in_pixel_index + 2] += horizontal_buffer[in_pixel_index + 2] * coefficient; - ring_buffer_entry[in_pixel_index + 3] += horizontal_buffer[in_pixel_index + 3] * coefficient; - } - break; - default: - for (x = 0; x < output_w; x++) - { - int in_pixel_index = x * channels; - - int c; - for (c = 0; c < channels; c++) - ring_buffer_entry[in_pixel_index + c] += horizontal_buffer[in_pixel_index + c] * coefficient; - } - break; - } - } -} - -static void stbir__buffer_loop_upsample(stbir__info* stbir_info) -{ - int y; - float scale_ratio = stbir_info->vertical_scale; - float out_scanlines_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(1/scale_ratio) * scale_ratio; - - STBIR_ASSERT(stbir__use_height_upsampling(stbir_info)); - - for (y = 0; y < stbir_info->output_h; y++) - { - float in_center_of_out = 0; // Center of the current out scanline in the in scanline space - int in_first_scanline = 0, in_last_scanline = 0; - - stbir__calculate_sample_range_upsample(y, out_scanlines_radius, scale_ratio, stbir_info->vertical_shift, &in_first_scanline, &in_last_scanline, &in_center_of_out); - - STBIR_ASSERT(in_last_scanline - in_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); - - if (stbir_info->ring_buffer_begin_index >= 0) - { - // Get rid of whatever we don't need anymore. - while (in_first_scanline > stbir_info->ring_buffer_first_scanline) - { - if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) - { - // We just popped the last scanline off the ring buffer. - // Reset it to the empty state. - stbir_info->ring_buffer_begin_index = -1; - stbir_info->ring_buffer_first_scanline = 0; - stbir_info->ring_buffer_last_scanline = 0; - break; - } - else - { - stbir_info->ring_buffer_first_scanline++; - stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries; - } - } - } - - // Load in new ones. - if (stbir_info->ring_buffer_begin_index < 0) - stbir__decode_and_resample_upsample(stbir_info, in_first_scanline); - - while (in_last_scanline > stbir_info->ring_buffer_last_scanline) - stbir__decode_and_resample_upsample(stbir_info, stbir_info->ring_buffer_last_scanline + 1); - - // Now all buffers should be ready to write a row of vertical sampling. - stbir__resample_vertical_upsample(stbir_info, y); - - STBIR_PROGRESS_REPORT((float)y / stbir_info->output_h); - } -} - -static void stbir__empty_ring_buffer(stbir__info* stbir_info, int first_necessary_scanline) -{ - int output_stride_bytes = stbir_info->output_stride_bytes; - int channels = stbir_info->channels; - int alpha_channel = stbir_info->alpha_channel; - int type = stbir_info->type; - int colorspace = stbir_info->colorspace; - int output_w = stbir_info->output_w; - void* output_data = stbir_info->output_data; - int decode = STBIR__DECODE(type, colorspace); - - float* ring_buffer = stbir_info->ring_buffer; - int ring_buffer_length = stbir_info->ring_buffer_length_bytes/sizeof(float); - - if (stbir_info->ring_buffer_begin_index >= 0) - { - // Get rid of whatever we don't need anymore. - while (first_necessary_scanline > stbir_info->ring_buffer_first_scanline) - { - if (stbir_info->ring_buffer_first_scanline >= 0 && stbir_info->ring_buffer_first_scanline < stbir_info->output_h) - { - int output_row_start = stbir_info->ring_buffer_first_scanline * output_stride_bytes; - float* ring_buffer_entry = stbir__get_ring_buffer_entry(ring_buffer, stbir_info->ring_buffer_begin_index, ring_buffer_length); - stbir__encode_scanline(stbir_info, output_w, (char *) output_data + output_row_start, ring_buffer_entry, channels, alpha_channel, decode); - STBIR_PROGRESS_REPORT((float)stbir_info->ring_buffer_first_scanline / stbir_info->output_h); - } - - if (stbir_info->ring_buffer_first_scanline == stbir_info->ring_buffer_last_scanline) - { - // We just popped the last scanline off the ring buffer. - // Reset it to the empty state. - stbir_info->ring_buffer_begin_index = -1; - stbir_info->ring_buffer_first_scanline = 0; - stbir_info->ring_buffer_last_scanline = 0; - break; - } - else - { - stbir_info->ring_buffer_first_scanline++; - stbir_info->ring_buffer_begin_index = (stbir_info->ring_buffer_begin_index + 1) % stbir_info->ring_buffer_num_entries; - } - } - } -} - -static void stbir__buffer_loop_downsample(stbir__info* stbir_info) -{ - int y; - float scale_ratio = stbir_info->vertical_scale; - int output_h = stbir_info->output_h; - float in_pixels_radius = stbir__filter_info_table[stbir_info->vertical_filter].support(scale_ratio) / scale_ratio; - int pixel_margin = stbir_info->vertical_filter_pixel_margin; - int max_y = stbir_info->input_h + pixel_margin; - - STBIR_ASSERT(!stbir__use_height_upsampling(stbir_info)); - - for (y = -pixel_margin; y < max_y; y++) - { - float out_center_of_in; // Center of the current out scanline in the in scanline space - int out_first_scanline, out_last_scanline; - - stbir__calculate_sample_range_downsample(y, in_pixels_radius, scale_ratio, stbir_info->vertical_shift, &out_first_scanline, &out_last_scanline, &out_center_of_in); - - STBIR_ASSERT(out_last_scanline - out_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); - - if (out_last_scanline < 0 || out_first_scanline >= output_h) - continue; - - stbir__empty_ring_buffer(stbir_info, out_first_scanline); - - stbir__decode_and_resample_downsample(stbir_info, y); - - // Load in new ones. - if (stbir_info->ring_buffer_begin_index < 0) - stbir__add_empty_ring_buffer_entry(stbir_info, out_first_scanline); - - while (out_last_scanline > stbir_info->ring_buffer_last_scanline) - stbir__add_empty_ring_buffer_entry(stbir_info, stbir_info->ring_buffer_last_scanline + 1); - - // Now the horizontal buffer is ready to write to all ring buffer rows. - stbir__resample_vertical_downsample(stbir_info, y); - } - - stbir__empty_ring_buffer(stbir_info, stbir_info->output_h); -} - -static void stbir__setup(stbir__info *info, int input_w, int input_h, int output_w, int output_h, int channels) -{ - info->input_w = input_w; - info->input_h = input_h; - info->output_w = output_w; - info->output_h = output_h; - info->channels = channels; -} - -static void stbir__calculate_transform(stbir__info *info, float s0, float t0, float s1, float t1, float *transform) -{ - info->s0 = s0; - info->t0 = t0; - info->s1 = s1; - info->t1 = t1; - - if (transform) - { - info->horizontal_scale = transform[0]; - info->vertical_scale = transform[1]; - info->horizontal_shift = transform[2]; - info->vertical_shift = transform[3]; - } - else - { - info->horizontal_scale = ((float)info->output_w / info->input_w) / (s1 - s0); - info->vertical_scale = ((float)info->output_h / info->input_h) / (t1 - t0); - - info->horizontal_shift = s0 * info->output_w / (s1 - s0); - info->vertical_shift = t0 * info->output_h / (t1 - t0); - } -} - -static void stbir__choose_filter(stbir__info *info, stbir_filter h_filter, stbir_filter v_filter) -{ - if (h_filter == 0) - h_filter = stbir__use_upsampling(info->horizontal_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; - if (v_filter == 0) - v_filter = stbir__use_upsampling(info->vertical_scale) ? STBIR_DEFAULT_FILTER_UPSAMPLE : STBIR_DEFAULT_FILTER_DOWNSAMPLE; - info->horizontal_filter = h_filter; - info->vertical_filter = v_filter; -} - -static stbir_uint32 stbir__calculate_memory(stbir__info *info) -{ - int pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); - int filter_height = stbir__get_filter_pixel_width(info->vertical_filter, info->vertical_scale); - - info->horizontal_num_contributors = stbir__get_contributors(info->horizontal_scale, info->horizontal_filter, info->input_w, info->output_w); - info->vertical_num_contributors = stbir__get_contributors(info->vertical_scale , info->vertical_filter , info->input_h, info->output_h); - - // One extra entry because floating point precision problems sometimes cause an extra to be necessary. - info->ring_buffer_num_entries = filter_height + 1; - - info->horizontal_contributors_size = info->horizontal_num_contributors * sizeof(stbir__contributors); - info->horizontal_coefficients_size = stbir__get_total_horizontal_coefficients(info) * sizeof(float); - info->vertical_contributors_size = info->vertical_num_contributors * sizeof(stbir__contributors); - info->vertical_coefficients_size = stbir__get_total_vertical_coefficients(info) * sizeof(float); - info->decode_buffer_size = (info->input_w + pixel_margin * 2) * info->channels * sizeof(float); - info->horizontal_buffer_size = info->output_w * info->channels * sizeof(float); - info->ring_buffer_size = info->output_w * info->channels * info->ring_buffer_num_entries * sizeof(float); - info->encode_buffer_size = info->output_w * info->channels * sizeof(float); - - STBIR_ASSERT(info->horizontal_filter != 0); - STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late - STBIR_ASSERT(info->vertical_filter != 0); - STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); // this now happens too late - - if (stbir__use_height_upsampling(info)) - // The horizontal buffer is for when we're downsampling the height and we - // can't output the result of sampling the decode buffer directly into the - // ring buffers. - info->horizontal_buffer_size = 0; - else - // The encode buffer is to retain precision in the height upsampling method - // and isn't used when height downsampling. - info->encode_buffer_size = 0; - - return info->horizontal_contributors_size + info->horizontal_coefficients_size - + info->vertical_contributors_size + info->vertical_coefficients_size - + info->decode_buffer_size + info->horizontal_buffer_size - + info->ring_buffer_size + info->encode_buffer_size; -} - -static int stbir__resize_allocated(stbir__info *info, - const void* input_data, int input_stride_in_bytes, - void* output_data, int output_stride_in_bytes, - int alpha_channel, stbir_uint32 flags, stbir_datatype type, - stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace, - void* tempmem, size_t tempmem_size_in_bytes) -{ - size_t memory_required = stbir__calculate_memory(info); - - int width_stride_input = input_stride_in_bytes ? input_stride_in_bytes : info->channels * info->input_w * stbir__type_size[type]; - int width_stride_output = output_stride_in_bytes ? output_stride_in_bytes : info->channels * info->output_w * stbir__type_size[type]; - -#ifdef STBIR_DEBUG_OVERWRITE_TEST -#define OVERWRITE_ARRAY_SIZE 8 - unsigned char overwrite_output_before_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_tempmem_before_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_output_after_pre[OVERWRITE_ARRAY_SIZE]; - unsigned char overwrite_tempmem_after_pre[OVERWRITE_ARRAY_SIZE]; - - size_t begin_forbidden = width_stride_output * (info->output_h - 1) + info->output_w * info->channels * stbir__type_size[type]; - memcpy(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE); - memcpy(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE); -#endif - - STBIR_ASSERT(info->channels >= 0); - STBIR_ASSERT(info->channels <= STBIR_MAX_CHANNELS); - - if (info->channels < 0 || info->channels > STBIR_MAX_CHANNELS) - return 0; - - STBIR_ASSERT(info->horizontal_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - STBIR_ASSERT(info->vertical_filter < STBIR__ARRAY_SIZE(stbir__filter_info_table)); - - if (info->horizontal_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) - return 0; - if (info->vertical_filter >= STBIR__ARRAY_SIZE(stbir__filter_info_table)) - return 0; - - if (alpha_channel < 0) - flags |= STBIR_FLAG_ALPHA_USES_COLORSPACE | STBIR_FLAG_ALPHA_PREMULTIPLIED; - - if (!(flags&STBIR_FLAG_ALPHA_USES_COLORSPACE) || !(flags&STBIR_FLAG_ALPHA_PREMULTIPLIED)) { - STBIR_ASSERT(alpha_channel >= 0 && alpha_channel < info->channels); - } - - if (alpha_channel >= info->channels) - return 0; - - STBIR_ASSERT(tempmem); - - if (!tempmem) - return 0; - - STBIR_ASSERT(tempmem_size_in_bytes >= memory_required); - - if (tempmem_size_in_bytes < memory_required) - return 0; - - memset(tempmem, 0, tempmem_size_in_bytes); - - info->input_data = input_data; - info->input_stride_bytes = width_stride_input; - - info->output_data = output_data; - info->output_stride_bytes = width_stride_output; - - info->alpha_channel = alpha_channel; - info->flags = flags; - info->type = type; - info->edge_horizontal = edge_horizontal; - info->edge_vertical = edge_vertical; - info->colorspace = colorspace; - - info->horizontal_coefficient_width = stbir__get_coefficient_width (info->horizontal_filter, info->horizontal_scale); - info->vertical_coefficient_width = stbir__get_coefficient_width (info->vertical_filter , info->vertical_scale ); - info->horizontal_filter_pixel_width = stbir__get_filter_pixel_width (info->horizontal_filter, info->horizontal_scale); - info->vertical_filter_pixel_width = stbir__get_filter_pixel_width (info->vertical_filter , info->vertical_scale ); - info->horizontal_filter_pixel_margin = stbir__get_filter_pixel_margin(info->horizontal_filter, info->horizontal_scale); - info->vertical_filter_pixel_margin = stbir__get_filter_pixel_margin(info->vertical_filter , info->vertical_scale ); - - info->ring_buffer_length_bytes = info->output_w * info->channels * sizeof(float); - info->decode_buffer_pixels = info->input_w + info->horizontal_filter_pixel_margin * 2; - -#define STBIR__NEXT_MEMPTR(current, newtype) (newtype*)(((unsigned char*)current) + current##_size) - - info->horizontal_contributors = (stbir__contributors *) tempmem; - info->horizontal_coefficients = STBIR__NEXT_MEMPTR(info->horizontal_contributors, float); - info->vertical_contributors = STBIR__NEXT_MEMPTR(info->horizontal_coefficients, stbir__contributors); - info->vertical_coefficients = STBIR__NEXT_MEMPTR(info->vertical_contributors, float); - info->decode_buffer = STBIR__NEXT_MEMPTR(info->vertical_coefficients, float); - - if (stbir__use_height_upsampling(info)) - { - info->horizontal_buffer = NULL; - info->ring_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); - info->encode_buffer = STBIR__NEXT_MEMPTR(info->ring_buffer, float); - - STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->encode_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); - } - else - { - info->horizontal_buffer = STBIR__NEXT_MEMPTR(info->decode_buffer, float); - info->ring_buffer = STBIR__NEXT_MEMPTR(info->horizontal_buffer, float); - info->encode_buffer = NULL; - - STBIR_ASSERT((size_t)STBIR__NEXT_MEMPTR(info->ring_buffer, unsigned char) == (size_t)tempmem + tempmem_size_in_bytes); - } - -#undef STBIR__NEXT_MEMPTR - - // This signals that the ring buffer is empty - info->ring_buffer_begin_index = -1; - - stbir__calculate_filters(info->horizontal_contributors, info->horizontal_coefficients, info->horizontal_filter, info->horizontal_scale, info->horizontal_shift, info->input_w, info->output_w); - stbir__calculate_filters(info->vertical_contributors, info->vertical_coefficients, info->vertical_filter, info->vertical_scale, info->vertical_shift, info->input_h, info->output_h); - - STBIR_PROGRESS_REPORT(0); - - if (stbir__use_height_upsampling(info)) - stbir__buffer_loop_upsample(info); - else - stbir__buffer_loop_downsample(info); - - STBIR_PROGRESS_REPORT(1); - -#ifdef STBIR_DEBUG_OVERWRITE_TEST - STBIR_ASSERT(memcmp(overwrite_output_before_pre, &((unsigned char*)output_data)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); - STBIR_ASSERT(memcmp(overwrite_output_after_pre, &((unsigned char*)output_data)[begin_forbidden], OVERWRITE_ARRAY_SIZE) == 0); - STBIR_ASSERT(memcmp(overwrite_tempmem_before_pre, &((unsigned char*)tempmem)[-OVERWRITE_ARRAY_SIZE], OVERWRITE_ARRAY_SIZE) == 0); - STBIR_ASSERT(memcmp(overwrite_tempmem_after_pre, &((unsigned char*)tempmem)[tempmem_size_in_bytes], OVERWRITE_ARRAY_SIZE) == 0); -#endif - - return 1; -} - - -static int stbir__resize_arbitrary( - void *alloc_context, - const void* input_data, int input_w, int input_h, int input_stride_in_bytes, - void* output_data, int output_w, int output_h, int output_stride_in_bytes, - float s0, float t0, float s1, float t1, float *transform, - int channels, int alpha_channel, stbir_uint32 flags, stbir_datatype type, - stbir_filter h_filter, stbir_filter v_filter, - stbir_edge edge_horizontal, stbir_edge edge_vertical, stbir_colorspace colorspace) -{ - stbir__info info; - int result; - size_t memory_required; - void* extra_memory; - - stbir__setup(&info, input_w, input_h, output_w, output_h, channels); - stbir__calculate_transform(&info, s0,t0,s1,t1,transform); - stbir__choose_filter(&info, h_filter, v_filter); - memory_required = stbir__calculate_memory(&info); - extra_memory = STBIR_MALLOC(memory_required, alloc_context); - - if (!extra_memory) - return 0; - - result = stbir__resize_allocated(&info, input_data, input_stride_in_bytes, - output_data, output_stride_in_bytes, - alpha_channel, flags, type, - edge_horizontal, edge_vertical, - colorspace, extra_memory, memory_required); - - STBIR_FREE(extra_memory, alloc_context); - - return result; -} - -STBIRDEF int stbir_resize_uint8( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels) -{ - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); -} - -STBIRDEF int stbir_resize_float( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels) -{ - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,-1,0, STBIR_TYPE_FLOAT, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_LINEAR); -} - -STBIRDEF int stbir_resize_uint8_srgb(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags) -{ - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_COLORSPACE_SRGB); -} - -STBIRDEF int stbir_resize_uint8_srgb_edgemode(const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode) -{ - return stbir__resize_arbitrary(NULL, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, STBIR_FILTER_DEFAULT, STBIR_FILTER_DEFAULT, - edge_wrap_mode, edge_wrap_mode, STBIR_COLORSPACE_SRGB); -} - -STBIRDEF int stbir_resize_uint8_generic( const unsigned char *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT8, filter, filter, - edge_wrap_mode, edge_wrap_mode, space); -} - -STBIRDEF int stbir_resize_uint16_generic(const stbir_uint16 *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - stbir_uint16 *output_pixels , int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_UINT16, filter, filter, - edge_wrap_mode, edge_wrap_mode, space); -} - - -STBIRDEF int stbir_resize_float_generic( const float *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - float *output_pixels , int output_w, int output_h, int output_stride_in_bytes, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_wrap_mode, stbir_filter filter, stbir_colorspace space, - void *alloc_context) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, STBIR_TYPE_FLOAT, filter, filter, - edge_wrap_mode, edge_wrap_mode, space); -} - - -STBIRDEF int stbir_resize( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, - edge_mode_horizontal, edge_mode_vertical, space); -} - - -STBIRDEF int stbir_resize_subpixel(const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context, - float x_scale, float y_scale, - float x_offset, float y_offset) -{ - float transform[4]; - transform[0] = x_scale; - transform[1] = y_scale; - transform[2] = x_offset; - transform[3] = y_offset; - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - 0,0,1,1,transform,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, - edge_mode_horizontal, edge_mode_vertical, space); -} - -STBIRDEF int stbir_resize_region( const void *input_pixels , int input_w , int input_h , int input_stride_in_bytes, - void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, - stbir_datatype datatype, - int num_channels, int alpha_channel, int flags, - stbir_edge edge_mode_horizontal, stbir_edge edge_mode_vertical, - stbir_filter filter_horizontal, stbir_filter filter_vertical, - stbir_colorspace space, void *alloc_context, - float s0, float t0, float s1, float t1) -{ - return stbir__resize_arbitrary(alloc_context, input_pixels, input_w, input_h, input_stride_in_bytes, - output_pixels, output_w, output_h, output_stride_in_bytes, - s0,t0,s1,t1,NULL,num_channels,alpha_channel,flags, datatype, filter_horizontal, filter_vertical, - edge_mode_horizontal, edge_mode_vertical, space); -} - -#endif // STB_IMAGE_RESIZE_IMPLEMENTATION - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/ diff --git a/src/external/stb_image_resize2.h b/src/external/stb_image_resize2.h new file mode 100644 index 000000000..e0c428246 --- /dev/null +++ b/src/external/stb_image_resize2.h @@ -0,0 +1,10303 @@ +/* stb_image_resize2 - v2.01 - public domain image resizing + + by Jeff Roberts (v2) and Jorge L Rodriguez + http://github.com/nothings/stb + + Can be threaded with the extended API. SSE2, AVX, Neon and WASM SIMD support. Only + scaling and translation is supported, no rotations or shears. + + COMPILING & LINKING + In one C/C++ file that #includes this file, do this: + #define STB_IMAGE_RESIZE_IMPLEMENTATION + before the #include. That will create the implementation in that file. + + PORTING FROM VERSION 1 + + The API has changed. You can continue to use the old version of stb_image_resize.h, + which is available in the "deprecated/" directory. + + If you're using the old simple-to-use API, porting is straightforward. + (For more advanced APIs, read the documentation.) + + stbir_resize_uint8(): + - call `stbir_resize_uint8_linear`, cast channel count to `stbir_pixel_layout` + + stbir_resize_float(): + - call `stbir_resize_float_linear`, cast channel count to `stbir_pixel_layout` + + stbir_resize_uint8_srgb(): + - function name is unchanged + - cast channel count to `stbir_pixel_layout` + - above is sufficient unless your image has alpha and it's not RGBA/BGRA + - in that case, follow the below instructions for stbir_resize_uint8_srgb_edgemode + + stbir_resize_uint8_srgb_edgemode() + - switch to the "medium complexity" API + - stbir_resize(), very similar API but a few more parameters: + - pixel_layout: cast channel count to `stbir_pixel_layout` + - data_type: STBIR_TYPE_UINT8_SRGB + - edge: unchanged (STBIR_EDGE_WRAP, etc.) + - filter: STBIR_FILTER_DEFAULT + - which channel is alpha is specified in stbir_pixel_layout, see enum for details + + EASY API CALLS: + Easy API downsamples w/Mitchell filter, upsamples w/cubic interpolation, clamps to edge. + + stbir_resize_uint8_srgb( input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + pixel_layout_enum ) + + stbir_resize_uint8_linear( input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + pixel_layout_enum ) + + stbir_resize_float_linear( input_pixels, input_w, input_h, input_stride_in_bytes, + output_pixels, output_w, output_h, output_stride_in_bytes, + pixel_layout_enum ) + + If you pass NULL or zero for the output_pixels, we will allocate the output buffer + for you and return it from the function (free with free() or STBIR_FREE). + As a special case, XX_stride_in_bytes of 0 means packed continuously in memory. + + API LEVELS + There are three levels of API - easy-to-use, medium-complexity and extended-complexity. + + See the "header file" section of the source for API documentation. + + ADDITIONAL DOCUMENTATION + + MEMORY ALLOCATION + By default, we use malloc and free for memory allocation. To override the + memory allocation, before the implementation #include, add a: + + #define STBIR_MALLOC(size,user_data) ... + #define STBIR_FREE(ptr,user_data) ... + + Each resize makes exactly one call to malloc/free (unless you use the + extended API where you can do one allocation for many resizes). Under + address sanitizer, we do separate allocations to find overread/writes. + + PERFORMANCE + This library was written with an emphasis on performance. When testing + stb_image_resize with RGBA, the fastest mode is STBIR_4CHANNEL with + STBIR_TYPE_UINT8 pixels and CLAMPed edges (which is what many other resize + libs do by default). Also, make sure SIMD is turned on of course (default + for 64-bit targets). Avoid WRAP edge mode if you want the fastest speed. + + This library also comes with profiling built-in. If you define STBIR_PROFILE, + you can use the advanced API and get low-level profiling information by + calling stbir_resize_extended_profile_info() or stbir_resize_split_profile_info() + after a resize. + + SIMD + Most of the routines have optimized SSE2, AVX, NEON and WASM versions. + + On Microsoft compilers, we automatically turn on SIMD for 64-bit x64 and + ARM; for 32-bit x86 and ARM, you select SIMD mode by defining STBIR_SSE2 or + STBIR_NEON. For AVX and AVX2, we auto-select it by detecting the /arch:AVX + or /arch:AVX2 switches. You can also always manually turn SSE2, AVX or AVX2 + support on by defining STBIR_SSE2, STBIR_AVX or STBIR_AVX2. + + On Linux, SSE2 and Neon is on by default for 64-bit x64 or ARM64. For 32-bit, + we select x86 SIMD mode by whether you have -msse2, -mavx or -mavx2 enabled + on the command line. For 32-bit ARM, you must pass -mfpu=neon-vfpv4 for both + clang and GCC, but GCC also requires an additional -mfp16-format=ieee to + automatically enable NEON. + + On x86 platforms, you can also define STBIR_FP16C to turn on FP16C instructions + for converting back and forth to half-floats. This is autoselected when we + are using AVX2. Clang and GCC also require the -mf16c switch. ARM always uses + the built-in half float hardware NEON instructions. + + You can also tell us to use multiply-add instructions with STBIR_USE_FMA. + Because x86 doesn't always have fma, we turn it off by default to maintain + determinism across all platforms. If you don't care about non-FMA determinism + and are willing to restrict yourself to more recent x86 CPUs (around the AVX + timeframe), then fma will give you around a 15% speedup. + + You can force off SIMD in all cases by defining STBIR_NO_SIMD. You can turn + off AVX or AVX2 specifically with STBIR_NO_AVX or STBIR_NO_AVX2. AVX is 10% + to 40% faster, and AVX2 is generally another 12%. + + ALPHA CHANNEL + Most of the resizing functions provide the ability to control how the alpha + channel of an image is processed. + + When alpha represents transparency, it is important that when combining + colors with filtering, the pixels should not be treated equally; they + should use a weighted average based on their alpha values. For example, + if a pixel is 1% opaque bright green and another pixel is 99% opaque + black and you average them, the average will be 50% opaque, but the + unweighted average and will be a middling green color, while the weighted + average will be nearly black. This means the unweighted version introduced + green energy that didn't exist in the source image. + + (If you want to know why this makes sense, you can work out the math for + the following: consider what happens if you alpha composite a source image + over a fixed color and then average the output, vs. if you average the + source image pixels and then composite that over the same fixed color. + Only the weighted average produces the same result as the ground truth + composite-then-average result.) + + Therefore, it is in general best to "alpha weight" the pixels when applying + filters to them. This essentially means multiplying the colors by the alpha + values before combining them, and then dividing by the alpha value at the + end. + + The computer graphics industry introduced a technique called "premultiplied + alpha" or "associated alpha" in which image colors are stored in image files + already multiplied by their alpha. This saves some math when compositing, + and also avoids the need to divide by the alpha at the end (which is quite + inefficient). However, while premultiplied alpha is common in the movie CGI + industry, it is not commonplace in other industries like videogames, and most + consumer file formats are generally expected to contain not-premultiplied + colors. For example, Photoshop saves PNG files "unpremultiplied", and web + browsers like Chrome and Firefox expect PNG images to be unpremultiplied. + + Note that there are three possibilities that might describe your image + and resize expectation: + + 1. images are not premultiplied, alpha weighting is desired + 2. images are not premultiplied, alpha weighting is not desired + 3. images are premultiplied + + Both case #2 and case #3 require the exact same math: no alpha weighting + should be applied or removed. Only case 1 requires extra math operations; + the other two cases can be handled identically. + + stb_image_resize expects case #1 by default, applying alpha weighting to + images, expecting the input images to be unpremultiplied. This is what the + COLOR+ALPHA buffer types tell the resizer to do. + + When you use the pixel layouts STBIR_RGBA, STBIR_BGRA, STBIR_ARGB, + STBIR_ABGR, STBIR_RX, or STBIR_XR you are telling us that the pixels are + non-premultiplied. In these cases, the resizer will alpha weight the colors + (effectively creating the premultiplied image), do the filtering, and then + convert back to non-premult on exit. + + When you use the pixel layouts STBIR_RGBA_PM, STBIR_RGBA_PM, STBIR_RGBA_PM, + STBIR_RGBA_PM, STBIR_RX_PM or STBIR_XR_PM, you are telling that the pixels + ARE premultiplied. In this case, the resizer doesn't have to do the + premultipling - it can filter directly on the input. This about twice as + fast as the non-premultiplied case, so it's the right option if your data is + already setup correctly. + + When you use the pixel layout STBIR_4CHANNEL or STBIR_2CHANNEL, you are + telling us that there is no channel that represents transparency; it may be + RGB and some unrelated fourth channel that has been stored in the alpha + channel, but it is actually not alpha. No special processing will be + performed. + + The difference between the generic 4 or 2 channel layouts, and the + specialized _PM versions is with the _PM versions you are telling us that + the data *is* alpha, just don't premultiply it. That's important when + using SRGB pixel formats, we need to know where the alpha is, because + it is converted linearly (rather than with the SRGB converters). + + Because alpha weighting produces the same effect as premultiplying, you + even have the option with non-premultiplied inputs to let the resizer + produce a premultiplied output. Because the intially computed alpha-weighted + output image is effectively premultiplied, this is actually more performant + than the normal path which un-premultiplies the output image as a final step. + + Finally, when converting both in and out of non-premulitplied space (for + example, when using STBIR_RGBA), we go to somewhat heroic measures to + ensure that areas with zero alpha value pixels get something reasonable + in the RGB values. If you don't care about the RGB values of zero alpha + pixels, you can call the stbir_set_non_pm_alpha_speed_over_quality() + function - this runs a premultiplied resize about 25% faster. That said, + when you really care about speed, using premultiplied pixels for both in + and out (STBIR_RGBA_PM, etc) much faster than both of these premultiplied + options. + + PIXEL LAYOUT CONVERSION + The resizer can convert from some pixel layouts to others. When using the + stbir_set_pixel_layouts(), you can, for example, specify STBIR_RGBA + on input, and STBIR_ARGB on output, and it will re-organize the channels + during the resize. Currently, you can only convert between two pixel + layouts with the same number of channels. + + DETERMINISM + We commit to being deterministic (from x64 to ARM to scalar to SIMD, etc). + This requires compiling with fast-math off (using at least /fp:precise). + Also, you must turn off fp-contracting (which turns mult+adds into fmas)! + We attempt to do this with pragmas, but with Clang, you usually want to add + -ffp-contract=off to the command line as well. + + For 32-bit x86, you must use SSE and SSE2 codegen for determinism. That is, + if the scalar x87 unit gets used at all, we immediately lose determinism. + On Microsoft Visual Studio 2008 and earlier, from what we can tell there is + no way to be deterministic in 32-bit x86 (some x87 always leaks in, even + with fp:strict). On 32-bit x86 GCC, determinism requires both -msse2 and + -fpmath=sse. + + Note that we will not be deterministic with float data containing NaNs - + the NaNs will propagate differently on different SIMD and platforms. + + If you turn on STBIR_USE_FMA, then we will be deterministic with other + fma targets, but we will differ from non-fma targets (this is unavoidable, + because a fma isn't simply an add with a mult - it also introduces a + rounding difference compared to non-fma instruction sequences. + + FLOAT PIXEL FORMAT RANGE + Any range of values can be used for the non-alpha float data that you pass + in (0 to 1, -1 to 1, whatever). However, if you are inputting float values + but *outputting* bytes or shorts, you must use a range of 0 to 1 so that we + scale back properly. The alpha channel must also be 0 to 1 for any format + that does premultiplication prior to resizing. + + Note also that with float output, using filters with negative lobes, the + output filtered values might go slightly out of range. You can define + STBIR_FLOAT_LOW_CLAMP and/or STBIR_FLOAT_HIGH_CLAMP to specify the range + to clamp to on output, if that's important. + + MAX/MIN SCALE FACTORS + The input pixel resolutions are in integers, and we do the internal pointer + resolution in size_t sized integers. However, the scale ratio from input + resolution to output resolution is calculated in float form. This means + the effective possible scale ratio is limited to 24 bits (or 16 million + to 1). As you get close to the size of the float resolution (again, 16 + million pixels wide or high), you might start seeing float inaccuracy + issues in general in the pipeline. If you have to do extreme resizes, + you can usually do this is multiple stages (using float intermediate + buffers). + + FLIPPED IMAGES + Stride is just the delta from one scanline to the next. This means you can + use a negative stride to handle inverted images (point to the final + scanline and use a negative stride). You can invert the input or output, + using negative strides. + + DEFAULT FILTERS + For functions which don't provide explicit control over what filters to + use, you can change the compile-time defaults with: + + #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_something + #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_something + + See stbir_filter in the header-file section for the list of filters. + + NEW FILTERS + A number of 1D filter kernels are supplied. For a list of supported + filters, see the stbir_filter enum. You can install your own filters by + using the stbir_set_filter_callbacks function. + + PROGRESS + For interactive use with slow resize operations, you can use the the + scanline callbacks in the extended API. It would have to be a *very* large + image resample to need progress though - we're very fast. + + CEIL and FLOOR + In scalar mode, the only functions we use from math.h are ceilf and floorf, + but if you have your own versions, you can define the STBIR_CEILF(v) and + STBIR_FLOORF(v) macros and we'll use them instead. In SIMD, we just use + our own versions. + + ASSERT + Define STBIR_ASSERT(boolval) to override assert() and not use assert.h + + FUTURE TODOS + * For polyphase integral filters, we just memcpy the coeffs to dupe + them, but we should indirect and use the same coeff memory. + * Add pixel layout conversions for sensible different channel counts + (maybe, 1->3/4, 3->4, 4->1, 3->1). + * For SIMD encode and decode scanline routines, do any pre-aligning + for bad input/output buffer alignments and pitch? + * For very wide scanlines, we should we do vertical strips to stay within + L2 cache. Maybe do chunks of 1K pixels at a time. There would be + some pixel reconversion, but probably dwarfed by things falling out + of cache. Probably also something possible with alternating between + scattering and gathering at high resize scales? + * Rewrite the coefficient generator to do many at once. + * AVX-512 vertical kernels - worried about downclocking here. + * Convert the reincludes to macros when we know they aren't changing. + * Experiment with pivoting the horizontal and always using the + vertical filters (which are faster, but perhaps not enough to overcome + the pivot cost and the extra memory touches). Need to buffer the whole + image so have to balance memory use. + * Most of our code is internally function pointers, should we compile + all the SIMD stuff always and dynamically dispatch? + + CONTRIBUTORS + Jeff Roberts: 2.0 implementation, optimizations, SIMD + Martins Mozeiko: NEON simd, WASM simd, clang and GCC whisperer. + Fabian Giesen: half float and srgb converters + Sean Barrett: API design, optimizations + Jorge L Rodriguez: Original 1.0 implementation + Aras Pranckevicius: bugfixes for 1.0 + Nathan Reed: warning fixes for 1.0 + + REVISIONS + 2.00 (2022-02-20) mostly new source: new api, optimizations, simd, vertical-first, etc + (2x-5x faster without simd, 4x-12x faster with simd) + (in some cases, 20x to 40x faster - resizing to very small for example) + 0.96 (2019-03-04) fixed warnings + 0.95 (2017-07-23) fixed warnings + 0.94 (2017-03-18) fixed warnings + 0.93 (2017-03-03) fixed bug with certain combinations of heights + 0.92 (2017-01-02) fix integer overflow on large (>2GB) images + 0.91 (2016-04-02) fix warnings; fix handling of subpixel regions + 0.90 (2014-09-17) first released version + + LICENSE + See end of file for license information. +*/ + +#if !defined(STB_IMAGE_RESIZE_DO_HORIZONTALS) && !defined(STB_IMAGE_RESIZE_DO_VERTICALS) && !defined(STB_IMAGE_RESIZE_DO_CODERS) // for internal re-includes + +#ifndef STBIR_INCLUDE_STB_IMAGE_RESIZE2_H +#define STBIR_INCLUDE_STB_IMAGE_RESIZE2_H + +#include +#ifdef _MSC_VER +typedef unsigned char stbir_uint8; +typedef unsigned short stbir_uint16; +typedef unsigned int stbir_uint32; +typedef unsigned __int64 stbir_uint64; +#else +#include +typedef uint8_t stbir_uint8; +typedef uint16_t stbir_uint16; +typedef uint32_t stbir_uint32; +typedef uint64_t stbir_uint64; +#endif + +#ifdef _M_IX86_FP +#if ( _M_IX86_FP >= 1 ) +#ifndef STBIR_SSE +#define STBIR_SSE +#endif +#endif +#endif + +#if defined(_x86_64) || defined( __x86_64__ ) || defined( _M_X64 ) || defined(__x86_64) || defined(_M_AMD64) || defined(__SSE2__) || defined(STBIR_SSE) || defined(STBIR_SSE2) + #ifndef STBIR_SSE2 + #define STBIR_SSE2 + #endif + #if defined(__AVX__) || defined(STBIR_AVX2) + #ifndef STBIR_AVX + #ifndef STBIR_NO_AVX + #define STBIR_AVX + #endif + #endif + #endif + #if defined(__AVX2__) || defined(STBIR_AVX2) + #ifndef STBIR_NO_AVX2 + #ifndef STBIR_AVX2 + #define STBIR_AVX2 + #endif + #if defined( _MSC_VER ) && !defined(__clang__) + #ifndef STBIR_FP16C // FP16C instructions are on all AVX2 cpus, so we can autoselect it here on microsoft - clang needs -m16c + #define STBIR_FP16C + #endif + #endif + #endif + #endif + #ifdef __F16C__ + #ifndef STBIR_FP16C // turn on FP16C instructions if the define is set (for clang and gcc) + #define STBIR_FP16C + #endif + #endif +#endif + +#if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) || defined(_M_ARM) || (__ARM_NEON_FP & 4) != 0 && __ARM_FP16_FORMAT_IEEE != 0 +#ifndef STBIR_NEON +#define STBIR_NEON +#endif +#endif + +#if defined(_M_ARM) +#ifdef STBIR_USE_FMA +#undef STBIR_USE_FMA // no FMA for 32-bit arm on MSVC +#endif +#endif + +#if defined(__wasm__) && defined(__wasm_simd128__) +#ifndef STBIR_WASM +#define STBIR_WASM +#endif +#endif + +#ifndef STBIRDEF +#ifdef STB_IMAGE_RESIZE_STATIC +#define STBIRDEF static +#else +#ifdef __cplusplus +#define STBIRDEF extern "C" +#else +#define STBIRDEF extern +#endif +#endif +#endif + +////////////////////////////////////////////////////////////////////////////// +//// start "header file" /////////////////////////////////////////////////// +// +// Easy-to-use API: +// +// * stride is the offset between successive rows of image data +// in memory, in bytes. specify 0 for packed continuously in memory +// * colorspace is linear or sRGB as specified by function name +// * Uses the default filters +// * Uses edge mode clamped +// * returned result is 1 for success or 0 in case of an error. + + +// stbir_pixel_layout specifies: +// number of channels +// order of channels +// whether color is premultiplied by alpha +// for back compatibility, you can cast the old channel count to an stbir_pixel_layout +typedef enum +{ + STBIR_BGR = 0, // 3-chan, with order specified (for channel flipping) + STBIR_1CHANNEL = 1, + STBIR_2CHANNEL = 2, + STBIR_RGB = 3, // 3-chan, with order specified (for channel flipping) + STBIR_RGBA = 4, // alpha formats, alpha is NOT premultiplied into color channels + + STBIR_4CHANNEL = 5, + STBIR_BGRA = 6, + STBIR_ARGB = 7, + STBIR_ABGR = 8, + STBIR_RA = 9, + STBIR_AR = 10, + + STBIR_RGBA_PM = 11, // alpha formats, alpha is premultiplied into color channels + STBIR_BGRA_PM = 12, + STBIR_ARGB_PM = 13, + STBIR_ABGR_PM = 14, + STBIR_RA_PM = 15, + STBIR_AR_PM = 16, +} stbir_pixel_layout; + +//=============================================================== +// Simple-complexity API +// +// If output_pixels is NULL (0), then we will allocate the buffer and return it to you. +//-------------------------------- + +STBIRDEF unsigned char * stbir_resize_uint8_srgb( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_type ); + +STBIRDEF unsigned char * stbir_resize_uint8_linear( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_type ); + +STBIRDEF float * stbir_resize_float_linear( const float *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_type ); +//=============================================================== + +//=============================================================== +// Medium-complexity API +// +// This extends the easy-to-use API as follows: +// +// * Can specify the datatype - U8, U8_SRGB, U16, FLOAT, HALF_FLOAT +// * Edge wrap can selected explicitly +// * Filter can be selected explicitly +//-------------------------------- + +typedef enum +{ + STBIR_EDGE_CLAMP = 0, + STBIR_EDGE_REFLECT = 1, + STBIR_EDGE_WRAP = 2, // this edge mode is slower and uses more memory + STBIR_EDGE_ZERO = 3, +} stbir_edge; + +typedef enum +{ + STBIR_FILTER_DEFAULT = 0, // use same filter type that easy-to-use API chooses + STBIR_FILTER_BOX = 1, // A trapezoid w/1-pixel wide ramps, same result as box for integer scale ratios + STBIR_FILTER_TRIANGLE = 2, // On upsampling, produces same results as bilinear texture filtering + STBIR_FILTER_CUBICBSPLINE = 3, // The cubic b-spline (aka Mitchell-Netrevalli with B=1,C=0), gaussian-esque + STBIR_FILTER_CATMULLROM = 4, // An interpolating cubic spline + STBIR_FILTER_MITCHELL = 5, // Mitchell-Netrevalli filter with B=1/3, C=1/3 + STBIR_FILTER_POINT_SAMPLE = 6, // Simple point sampling + STBIR_FILTER_OTHER = 7, // User callback specified +} stbir_filter; + +typedef enum +{ + STBIR_TYPE_UINT8 = 0, + STBIR_TYPE_UINT8_SRGB = 1, + STBIR_TYPE_UINT8_SRGB_ALPHA = 2, // alpha channel, when present, should also be SRGB (this is very unusual) + STBIR_TYPE_UINT16 = 3, + STBIR_TYPE_FLOAT = 4, + STBIR_TYPE_HALF_FLOAT = 5 +} stbir_datatype; + +// medium api +STBIRDEF void * stbir_resize( const void *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout, stbir_datatype data_type, + stbir_edge edge, stbir_filter filter ); +//=============================================================== + + + +//=============================================================== +// Extended-complexity API +// +// This API exposes all resize functionality. +// +// * Separate filter types for each axis +// * Separate edge modes for each axis +// * Separate input and output data types +// * Can specify regions with subpixel correctness +// * Can specify alpha flags +// * Can specify a memory callback +// * Can specify a callback data type for pixel input and output +// * Can be threaded for a single resize +// * Can be used to resize many frames without recalculating the sampler info +// +// Use this API as follows: +// 1) Call the stbir_resize_init function on a local STBIR_RESIZE structure +// 2) Call any of the stbir_set functions +// 3) Optionally call stbir_build_samplers() if you are going to resample multiple times +// with the same input and output dimensions (like resizing video frames) +// 4) Resample by calling stbir_resize_extended(). +// 5) Call stbir_free_samplers() if you called stbir_build_samplers() +//-------------------------------- + + +// Types: + +// INPUT CALLBACK: this callback is used for input scanlines +typedef void const * stbir_input_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context ); + +// OUTPUT CALLBACK: this callback is used for output scanlines +typedef void stbir_output_callback( void const * output_ptr, int num_pixels, int y, void * context ); + +// callbacks for user installed filters +typedef float stbir__kernel_callback( float x, float scale, void * user_data ); // centered at zero +typedef float stbir__support_callback( float scale, void * user_data ); + +// internal structure with precomputed scaling +typedef struct stbir__info stbir__info; + +typedef struct STBIR_RESIZE // use the stbir_resize_init and stbir_override functions to set these values for future compatibility +{ + void * user_data; + void const * input_pixels; + int input_w, input_h; + double input_s0, input_t0, input_s1, input_t1; + stbir_input_callback * input_cb; + void * output_pixels; + int output_w, output_h; + int output_subx, output_suby, output_subw, output_subh; + stbir_output_callback * output_cb; + int input_stride_in_bytes; + int output_stride_in_bytes; + int splits; + int fast_alpha; + int needs_rebuild; + int called_alloc; + stbir_pixel_layout input_pixel_layout_public; + stbir_pixel_layout output_pixel_layout_public; + stbir_datatype input_data_type; + stbir_datatype output_data_type; + stbir_filter horizontal_filter, vertical_filter; + stbir_edge horizontal_edge, vertical_edge; + stbir__kernel_callback * horizontal_filter_kernel; stbir__support_callback * horizontal_filter_support; + stbir__kernel_callback * vertical_filter_kernel; stbir__support_callback * vertical_filter_support; + stbir__info * samplers; +} STBIR_RESIZE; + +// extended complexity api + + +// First off, you must ALWAYS call stbir_resize_init on your resize structure before any of the other calls! +STBIRDEF void stbir_resize_init( STBIR_RESIZE * resize, + const void *input_pixels, int input_w, int input_h, int input_stride_in_bytes, // stride can be zero + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, // stride can be zero + stbir_pixel_layout pixel_layout, stbir_datatype data_type ); + +//=============================================================== +// You can update these parameters any time after resize_init and there is no cost +//-------------------------------- + +STBIRDEF void stbir_set_datatypes( STBIR_RESIZE * resize, stbir_datatype input_type, stbir_datatype output_type ); +STBIRDEF void stbir_set_pixel_callbacks( STBIR_RESIZE * resize, stbir_input_callback * input_cb, stbir_output_callback * output_cb ); // no callbacks by default +STBIRDEF void stbir_set_user_data( STBIR_RESIZE * resize, void * user_data ); // pass back STBIR_RESIZE* by default +STBIRDEF void stbir_set_buffer_ptrs( STBIR_RESIZE * resize, const void * input_pixels, int input_stride_in_bytes, void * output_pixels, int output_stride_in_bytes ); + +//=============================================================== + + +//=============================================================== +// If you call any of these functions, you will trigger a sampler rebuild! +//-------------------------------- + +STBIRDEF int stbir_set_pixel_layouts( STBIR_RESIZE * resize, stbir_pixel_layout input_pixel_layout, stbir_pixel_layout output_pixel_layout ); // sets new buffer layouts +STBIRDEF int stbir_set_edgemodes( STBIR_RESIZE * resize, stbir_edge horizontal_edge, stbir_edge vertical_edge ); // CLAMP by default + +STBIRDEF int stbir_set_filters( STBIR_RESIZE * resize, stbir_filter horizontal_filter, stbir_filter vertical_filter ); // STBIR_DEFAULT_FILTER_UPSAMPLE/DOWNSAMPLE by default +STBIRDEF int stbir_set_filter_callbacks( STBIR_RESIZE * resize, stbir__kernel_callback * horizontal_filter, stbir__support_callback * horizontal_support, stbir__kernel_callback * vertical_filter, stbir__support_callback * vertical_support ); + +STBIRDEF int stbir_set_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ); // sets both sub-regions (full regions by default) +STBIRDEF int stbir_set_input_subrect( STBIR_RESIZE * resize, double s0, double t0, double s1, double t1 ); // sets input sub-region (full region by default) +STBIRDEF int stbir_set_output_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ); // sets output sub-region (full region by default) + +// when inputting AND outputting non-premultiplied alpha pixels, we use a slower but higher quality technique +// that fills the zero alpha pixel's RGB values with something plausible. If you don't care about areas of +// zero alpha, you can call this function to get about a 25% speed improvement for STBIR_RGBA to STBIR_RGBA +// types of resizes. +STBIRDEF int stbir_set_non_pm_alpha_speed_over_quality( STBIR_RESIZE * resize, int non_pma_alpha_speed_over_quality ); +//=============================================================== + + +//=============================================================== +// You can call build_samplers to prebuild all the internal data we need to resample. +// Then, if you call resize_extended many times with the same resize, you only pay the +// cost once. +// If you do call build_samplers, you MUST call free_samplers eventually. +//-------------------------------- + +// This builds the samplers and does one allocation +STBIRDEF int stbir_build_samplers( STBIR_RESIZE * resize ); + +// You MUST call this, if you call stbir_build_samplers or stbir_build_samplers_with_splits +STBIRDEF void stbir_free_samplers( STBIR_RESIZE * resize ); +//=============================================================== + + +// And this is the main function to perform the resize synchronously on one thread. +STBIRDEF int stbir_resize_extended( STBIR_RESIZE * resize ); + + +//=============================================================== +// Use these functions for multithreading. +// 1) You call stbir_build_samplers_with_splits first on the main thread +// 2) Then stbir_resize_with_split on each thread +// 3) stbir_free_samplers when done on the main thread +//-------------------------------- + +// This will build samplers for threading. +// You can pass in the number of threads you'd like to use (try_splits). +// It returns the number of splits (threads) that you can call it with. +/// It might be less if the image resize can't be split up that many ways. + +STBIRDEF int stbir_build_samplers_with_splits( STBIR_RESIZE * resize, int try_splits ); + +// This function does a split of the resizing (you call this fuction for each +// split, on multiple threads). A split is a piece of the output resize pixel space. + +// Note that you MUST call stbir_build_samplers_with_splits before stbir_resize_extended_split! + +// Usually, you will always call stbir_resize_split with split_start as the thread_index +// and "1" for the split_count. +// But, if you have a weird situation where you MIGHT want 8 threads, but sometimes +// only 4 threads, you can use 0,2,4,6 for the split_start's and use "2" for the +// split_count each time to turn in into a 4 thread resize. (This is unusual). + +STBIRDEF int stbir_resize_extended_split( STBIR_RESIZE * resize, int split_start, int split_count ); +//=============================================================== + + +//=============================================================== +// Pixel Callbacks info: +//-------------------------------- + +// The input callback is super flexible - it calls you with the input address +// (based on the stride and base pointer), it gives you an optional_output +// pointer that you can fill, or you can just return your own pointer into +// your own data. +// +// You can also do conversion from non-supported data types if necessary - in +// this case, you ignore the input_ptr and just use the x and y parameters to +// calculate your own input_ptr based on the size of each non-supported pixel. +// (Something like the third example below.) +// +// You can also install just an input or just an output callback by setting the +// callback that you don't want to zero. +// +// First example, progress: (getting a callback that you can monitor the progress): +// void const * my_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context ) +// { +// percentage_done = y / input_height; +// return input_ptr; // use buffer from call +// } +// +// Next example, copying: (copy from some other buffer or stream): +// void const * my_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context ) +// { +// CopyOrStreamData( optional_output, other_data_src, num_pixels * pixel_width_in_bytes ); +// return optional_output; // return the optional buffer that we filled +// } +// +// Third example, input another buffer without copying: (zero-copy from other buffer): +// void const * my_callback( void * optional_output, void const * input_ptr, int num_pixels, int x, int y, void * context ) +// { +// void * pixels = ( (char*) other_image_base ) + ( y * other_image_stride ) + ( x * other_pixel_width_in_bytes ); +// return pixels; // return pointer to your data without copying +// } +// +// +// The output callback is considerably simpler - it just calls you so that you can dump +// out each scanline. You could even directly copy out to disk if you have a simple format +// like TGA or BMP. You can also convert to other output types here if you want. +// +// Simple example: +// void const * my_output( void * output_ptr, int num_pixels, int y, void * context ) +// { +// percentage_done = y / output_height; +// fwrite( output_ptr, pixel_width_in_bytes, num_pixels, output_file ); +// } +//=============================================================== + + + + +//=============================================================== +// optional built-in profiling API +//-------------------------------- + +#ifdef STBIR_PROFILE + +typedef struct STBIR_PROFILE_INFO +{ + stbir_uint64 total_clocks; + + // how many clocks spent (of total_clocks) in the various resize routines, along with a string description + // there are "resize_count" number of zones + stbir_uint64 clocks[ 8 ]; + char const ** descriptions; + + // count of clocks and descriptions + stbir_uint32 count; +} STBIR_PROFILE_INFO; + +// use after calling stbir_resize_extended (or stbir_build_samplers or stbir_build_samplers_with_splits) +STBIRDEF void stbir_resize_build_profile_info( STBIR_PROFILE_INFO * out_info, STBIR_RESIZE const * resize ); + +// use after calling stbir_resize_extended +STBIRDEF void stbir_resize_extended_profile_info( STBIR_PROFILE_INFO * out_info, STBIR_RESIZE const * resize ); + +// use after calling stbir_resize_extended_split +STBIRDEF void stbir_resize_split_profile_info( STBIR_PROFILE_INFO * out_info, STBIR_RESIZE const * resize, int split_start, int split_num ); + +//=============================================================== + +#endif + + +//// end header file ///////////////////////////////////////////////////// +#endif // STBIR_INCLUDE_STB_IMAGE_RESIZE2_H + +#if defined(STB_IMAGE_RESIZE_IMPLEMENTATION) || defined(STB_IMAGE_RESIZE2_IMPLEMENTATION) + +#ifndef STBIR_ASSERT +#include +#define STBIR_ASSERT(x) assert(x) +#endif + +#ifndef STBIR_MALLOC +#include +#define STBIR_MALLOC(size,user_data) ((void)(user_data), malloc(size)) +#define STBIR_FREE(ptr,user_data) ((void)(user_data), free(ptr)) +// (we used the comma operator to evaluate user_data, to avoid "unused parameter" warnings) +#endif + +#ifdef _MSC_VER + +#define stbir__inline __forceinline + +#else + +#define stbir__inline __inline__ + +// Clang address sanitizer +#if defined(__has_feature) + #if __has_feature(address_sanitizer) || __has_feature(memory_sanitizer) + #ifndef STBIR__SEPARATE_ALLOCATIONS + #define STBIR__SEPARATE_ALLOCATIONS + #endif + #endif +#endif + +#endif + +// GCC and MSVC +#if defined(__SANITIZE_ADDRESS__) + #ifndef STBIR__SEPARATE_ALLOCATIONS + #define STBIR__SEPARATE_ALLOCATIONS + #endif +#endif + +// Always turn off automatic FMA use - use STBIR_USE_FMA if you want. +// Otherwise, this is a determinism disaster. +#ifndef STBIR_DONT_CHANGE_FP_CONTRACT // override in case you don't want this behavior +#if defined(_MSC_VER) && !defined(__clang__) +#if _MSC_VER > 1200 +#pragma fp_contract(off) +#endif +#elif defined(__GNUC__) && !defined(__clang__) +#pragma GCC optimize("fp-contract=off") +#else +#pragma STDC FP_CONTRACT OFF +#endif +#endif + +#ifdef _MSC_VER +#define STBIR__UNUSED(v) (void)(v) +#else +#define STBIR__UNUSED(v) (void)sizeof(v) +#endif + +#define STBIR__ARRAY_SIZE(a) (sizeof((a))/sizeof((a)[0])) + + +#ifndef STBIR_DEFAULT_FILTER_UPSAMPLE +#define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_CATMULLROM +#endif + +#ifndef STBIR_DEFAULT_FILTER_DOWNSAMPLE +#define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_MITCHELL +#endif + + +#ifndef STBIR__HEADER_FILENAME +#define STBIR__HEADER_FILENAME "stb_image_resize2.h" +#endif + +// the internal pixel layout enums are in a different order, so we can easily do range comparisons of types +// the public pixel layout is ordered in a way that if you cast num_channels (1-4) to the enum, you get something sensible +typedef enum +{ + STBIRI_1CHANNEL = 0, + STBIRI_2CHANNEL = 1, + STBIRI_RGB = 2, + STBIRI_BGR = 3, + STBIRI_4CHANNEL = 4, + + STBIRI_RGBA = 5, + STBIRI_BGRA = 6, + STBIRI_ARGB = 7, + STBIRI_ABGR = 8, + STBIRI_RA = 9, + STBIRI_AR = 10, + + STBIRI_RGBA_PM = 11, + STBIRI_BGRA_PM = 12, + STBIRI_ARGB_PM = 13, + STBIRI_ABGR_PM = 14, + STBIRI_RA_PM = 15, + STBIRI_AR_PM = 16, +} stbir_internal_pixel_layout; + +// define the public pixel layouts to not compile inside the implementation (to avoid accidental use) +#define STBIR_BGR bad_dont_use_in_implementation +#define STBIR_1CHANNEL STBIR_BGR +#define STBIR_2CHANNEL STBIR_BGR +#define STBIR_RGB STBIR_BGR +#define STBIR_RGBA STBIR_BGR +#define STBIR_4CHANNEL STBIR_BGR +#define STBIR_BGRA STBIR_BGR +#define STBIR_ARGB STBIR_BGR +#define STBIR_ABGR STBIR_BGR +#define STBIR_RA STBIR_BGR +#define STBIR_AR STBIR_BGR +#define STBIR_RGBA_PM STBIR_BGR +#define STBIR_BGRA_PM STBIR_BGR +#define STBIR_ARGB_PM STBIR_BGR +#define STBIR_ABGR_PM STBIR_BGR +#define STBIR_RA_PM STBIR_BGR +#define STBIR_AR_PM STBIR_BGR + +// must match stbir_datatype +static unsigned char stbir__type_size[] = { + 1,1,1,2,4,2 // STBIR_TYPE_UINT8,STBIR_TYPE_UINT8_SRGB,STBIR_TYPE_UINT8_SRGB_ALPHA,STBIR_TYPE_UINT16,STBIR_TYPE_FLOAT,STBIR_TYPE_HALF_FLOAT +}; + +// When gathering, the contributors are which source pixels contribute. +// When scattering, the contributors are which destination pixels are contributed to. +typedef struct +{ + int n0; // First contributing pixel + int n1; // Last contributing pixel +} stbir__contributors; + +typedef struct +{ + int lowest; // First sample index for whole filter + int highest; // Last sample index for whole filter + int widest; // widest single set of samples for an output +} stbir__filter_extent_info; + +typedef struct +{ + int n0; // First pixel of decode buffer to write to + int n1; // Last pixel of decode that will be written to + int pixel_offset_for_input; // Pixel offset into input_scanline +} stbir__span; + +typedef struct stbir__scale_info +{ + int input_full_size; + int output_sub_size; + float scale; + float inv_scale; + float pixel_shift; // starting shift in output pixel space (in pixels) + int scale_is_rational; + stbir_uint32 scale_numerator, scale_denominator; +} stbir__scale_info; + +typedef struct +{ + stbir__contributors * contributors; + float* coefficients; + stbir__contributors * gather_prescatter_contributors; + float * gather_prescatter_coefficients; + stbir__scale_info scale_info; + float support; + stbir_filter filter_enum; + stbir__kernel_callback * filter_kernel; + stbir__support_callback * filter_support; + stbir_edge edge; + int coefficient_width; + int filter_pixel_width; + int filter_pixel_margin; + int num_contributors; + int contributors_size; + int coefficients_size; + stbir__filter_extent_info extent_info; + int is_gather; // 0 = scatter, 1 = gather with scale >= 1, 2 = gather with scale < 1 + int gather_prescatter_num_contributors; + int gather_prescatter_coefficient_width; + int gather_prescatter_contributors_size; + int gather_prescatter_coefficients_size; +} stbir__sampler; + +typedef struct +{ + stbir__contributors conservative; + int edge_sizes[2]; // this can be less than filter_pixel_margin, if the filter and scaling falls off + stbir__span spans[2]; // can be two spans, if doing input subrect with clamp mode WRAP +} stbir__extents; + +typedef struct +{ +#ifdef STBIR_PROFILE + union + { + struct { stbir_uint64 total, looping, vertical, horizontal, decode, encode, alpha, unalpha; } named; + stbir_uint64 array[8]; + } profile; + stbir_uint64 * current_zone_excluded_ptr; +#endif + float* decode_buffer; + + int ring_buffer_first_scanline; + int ring_buffer_last_scanline; + int ring_buffer_begin_index; // first_scanline is at this index in the ring buffer + int start_output_y, end_output_y; + int start_input_y, end_input_y; // used in scatter only + + #ifdef STBIR__SEPARATE_ALLOCATIONS + float** ring_buffers; // one pointer for each ring buffer + #else + float* ring_buffer; // one big buffer that we index into + #endif + + float* vertical_buffer; + + char no_cache_straddle[64]; +} stbir__per_split_info; + +typedef void stbir__decode_pixels_func( float * decode, int width_times_channels, void const * input ); +typedef void stbir__alpha_weight_func( float * decode_buffer, int width_times_channels ); +typedef void stbir__horizontal_gather_channels_func( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, + stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ); +typedef void stbir__alpha_unweight_func(float * encode_buffer, int width_times_channels ); +typedef void stbir__encode_pixels_func( void * output, int width_times_channels, float const * encode ); + +struct stbir__info +{ +#ifdef STBIR_PROFILE + union + { + struct { stbir_uint64 total, build, alloc, horizontal, vertical, cleanup, pivot; } named; + stbir_uint64 array[7]; + } profile; + stbir_uint64 * current_zone_excluded_ptr; +#endif + stbir__sampler horizontal; + stbir__sampler vertical; + + void const * input_data; + void * output_data; + + int input_stride_bytes; + int output_stride_bytes; + int ring_buffer_length_bytes; // The length of an individual entry in the ring buffer. The total number of ring buffers is stbir__get_filter_pixel_width(filter) + int ring_buffer_num_entries; // Total number of entries in the ring buffer. + + stbir_datatype input_type; + stbir_datatype output_type; + + stbir_input_callback * in_pixels_cb; + void * user_data; + stbir_output_callback * out_pixels_cb; + + stbir__extents scanline_extents; + + void * alloced_mem; + stbir__per_split_info * split_info; // by default 1, but there will be N of these allocated based on the thread init you did + + stbir__decode_pixels_func * decode_pixels; + stbir__alpha_weight_func * alpha_weight; + stbir__horizontal_gather_channels_func * horizontal_gather_channels; + stbir__alpha_unweight_func * alpha_unweight; + stbir__encode_pixels_func * encode_pixels; + + int alloced_total; + int splits; // count of splits + + stbir_internal_pixel_layout input_pixel_layout_internal; + stbir_internal_pixel_layout output_pixel_layout_internal; + + int input_color_and_type; + int offset_x, offset_y; // offset within output_data + int vertical_first; + int channels; + int effective_channels; // same as channels, except on RGBA/ARGB (7), or XA/AX (3) + int alloc_ring_buffer_num_entries; // Number of entries in the ring buffer that will be allocated +}; + + +#define stbir__max_uint8_as_float 255.0f +#define stbir__max_uint16_as_float 65535.0f +#define stbir__max_uint8_as_float_inverted (1.0f/255.0f) +#define stbir__max_uint16_as_float_inverted (1.0f/65535.0f) +#define stbir__small_float ((float)1 / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20) / (1 << 20)) + +// min/max friendly +#define STBIR_CLAMP(x, xmin, xmax) do { \ + if ( (x) < (xmin) ) (x) = (xmin); \ + if ( (x) > (xmax) ) (x) = (xmax); \ +} while (0) + +static stbir__inline int stbir__min(int a, int b) +{ + return a < b ? a : b; +} + +static stbir__inline int stbir__max(int a, int b) +{ + return a > b ? a : b; +} + +static float stbir__srgb_uchar_to_linear_float[256] = { + 0.000000f, 0.000304f, 0.000607f, 0.000911f, 0.001214f, 0.001518f, 0.001821f, 0.002125f, 0.002428f, 0.002732f, 0.003035f, + 0.003347f, 0.003677f, 0.004025f, 0.004391f, 0.004777f, 0.005182f, 0.005605f, 0.006049f, 0.006512f, 0.006995f, 0.007499f, + 0.008023f, 0.008568f, 0.009134f, 0.009721f, 0.010330f, 0.010960f, 0.011612f, 0.012286f, 0.012983f, 0.013702f, 0.014444f, + 0.015209f, 0.015996f, 0.016807f, 0.017642f, 0.018500f, 0.019382f, 0.020289f, 0.021219f, 0.022174f, 0.023153f, 0.024158f, + 0.025187f, 0.026241f, 0.027321f, 0.028426f, 0.029557f, 0.030713f, 0.031896f, 0.033105f, 0.034340f, 0.035601f, 0.036889f, + 0.038204f, 0.039546f, 0.040915f, 0.042311f, 0.043735f, 0.045186f, 0.046665f, 0.048172f, 0.049707f, 0.051269f, 0.052861f, + 0.054480f, 0.056128f, 0.057805f, 0.059511f, 0.061246f, 0.063010f, 0.064803f, 0.066626f, 0.068478f, 0.070360f, 0.072272f, + 0.074214f, 0.076185f, 0.078187f, 0.080220f, 0.082283f, 0.084376f, 0.086500f, 0.088656f, 0.090842f, 0.093059f, 0.095307f, + 0.097587f, 0.099899f, 0.102242f, 0.104616f, 0.107023f, 0.109462f, 0.111932f, 0.114435f, 0.116971f, 0.119538f, 0.122139f, + 0.124772f, 0.127438f, 0.130136f, 0.132868f, 0.135633f, 0.138432f, 0.141263f, 0.144128f, 0.147027f, 0.149960f, 0.152926f, + 0.155926f, 0.158961f, 0.162029f, 0.165132f, 0.168269f, 0.171441f, 0.174647f, 0.177888f, 0.181164f, 0.184475f, 0.187821f, + 0.191202f, 0.194618f, 0.198069f, 0.201556f, 0.205079f, 0.208637f, 0.212231f, 0.215861f, 0.219526f, 0.223228f, 0.226966f, + 0.230740f, 0.234551f, 0.238398f, 0.242281f, 0.246201f, 0.250158f, 0.254152f, 0.258183f, 0.262251f, 0.266356f, 0.270498f, + 0.274677f, 0.278894f, 0.283149f, 0.287441f, 0.291771f, 0.296138f, 0.300544f, 0.304987f, 0.309469f, 0.313989f, 0.318547f, + 0.323143f, 0.327778f, 0.332452f, 0.337164f, 0.341914f, 0.346704f, 0.351533f, 0.356400f, 0.361307f, 0.366253f, 0.371238f, + 0.376262f, 0.381326f, 0.386430f, 0.391573f, 0.396755f, 0.401978f, 0.407240f, 0.412543f, 0.417885f, 0.423268f, 0.428691f, + 0.434154f, 0.439657f, 0.445201f, 0.450786f, 0.456411f, 0.462077f, 0.467784f, 0.473532f, 0.479320f, 0.485150f, 0.491021f, + 0.496933f, 0.502887f, 0.508881f, 0.514918f, 0.520996f, 0.527115f, 0.533276f, 0.539480f, 0.545725f, 0.552011f, 0.558340f, + 0.564712f, 0.571125f, 0.577581f, 0.584078f, 0.590619f, 0.597202f, 0.603827f, 0.610496f, 0.617207f, 0.623960f, 0.630757f, + 0.637597f, 0.644480f, 0.651406f, 0.658375f, 0.665387f, 0.672443f, 0.679543f, 0.686685f, 0.693872f, 0.701102f, 0.708376f, + 0.715694f, 0.723055f, 0.730461f, 0.737911f, 0.745404f, 0.752942f, 0.760525f, 0.768151f, 0.775822f, 0.783538f, 0.791298f, + 0.799103f, 0.806952f, 0.814847f, 0.822786f, 0.830770f, 0.838799f, 0.846873f, 0.854993f, 0.863157f, 0.871367f, 0.879622f, + 0.887923f, 0.896269f, 0.904661f, 0.913099f, 0.921582f, 0.930111f, 0.938686f, 0.947307f, 0.955974f, 0.964686f, 0.973445f, + 0.982251f, 0.991102f, 1.0f +}; + +typedef union +{ + unsigned int u; + float f; +} stbir__FP32; + +// From https://gist.github.com/rygorous/2203834 + +static const stbir_uint32 fp32_to_srgb8_tab4[104] = { + 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d, + 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a, + 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033, + 0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067, + 0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5, + 0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2, + 0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143, + 0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af, + 0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240, + 0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300, + 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401, + 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559, + 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723, +}; + +static stbir__inline stbir_uint8 stbir__linear_to_srgb_uchar(float in) +{ + static const stbir__FP32 almostone = { 0x3f7fffff }; // 1-eps + static const stbir__FP32 minval = { (127-13) << 23 }; + stbir_uint32 tab,bias,scale,t; + stbir__FP32 f; + + // Clamp to [2^(-13), 1-eps]; these two values map to 0 and 1, respectively. + // The tests are carefully written so that NaNs map to 0, same as in the reference + // implementation. + if (!(in > minval.f)) // written this way to catch NaNs + return 0; + if (in > almostone.f) + return 255; + + // Do the table lookup and unpack bias, scale + f.f = in; + tab = fp32_to_srgb8_tab4[(f.u - minval.u) >> 20]; + bias = (tab >> 16) << 9; + scale = tab & 0xffff; + + // Grab next-highest mantissa bits and perform linear interpolation + t = (f.u >> 12) & 0xff; + return (unsigned char) ((bias + scale*t) >> 16); +} + +#ifndef STBIR_FORCE_GATHER_FILTER_SCANLINES_AMOUNT +#define STBIR_FORCE_GATHER_FILTER_SCANLINES_AMOUNT 32 // when downsampling and <= 32 scanlines of buffering, use gather. gather used down to 1/8th scaling for 25% win. +#endif + +// restrict pointers for the output pointers +#if defined( _MSC_VER ) && !defined(__clang__) + #define STBIR_STREAMOUT_PTR( star ) star __restrict + #define STBIR_NO_UNROLL( ptr ) __assume(ptr) // this oddly keeps msvc from unrolling a loop +#elif defined( __clang__ ) + #define STBIR_STREAMOUT_PTR( star ) star __restrict__ + #define STBIR_NO_UNROLL( ptr ) __asm__ (""::"r"(ptr)) +#elif defined( __GNUC__ ) + #define STBIR_STREAMOUT_PTR( star ) star __restrict__ + #define STBIR_NO_UNROLL( ptr ) __asm__ (""::"r"(ptr)) +#else + #define STBIR_STREAMOUT_PTR( star ) star + #define STBIR_NO_UNROLL( ptr ) +#endif + +#ifdef STBIR_NO_SIMD // force simd off for whatever reason + +// force simd off overrides everything else, so clear it all + +#ifdef STBIR_SSE2 +#undef STBIR_SSE2 +#endif + +#ifdef STBIR_AVX +#undef STBIR_AVX +#endif + +#ifdef STBIR_NEON +#undef STBIR_NEON +#endif + +#ifdef STBIR_AVX2 +#undef STBIR_AVX2 +#endif + +#ifdef STBIR_FP16C +#undef STBIR_FP16C +#endif + +#ifdef STBIR_WASM +#undef STBIR_WASM +#endif + +#ifdef STBIR_SIMD +#undef STBIR_SIMD +#endif + +#else // STBIR_SIMD + +#ifdef STBIR_SSE2 + #include + + #define stbir__simdf __m128 + #define stbir__simdi __m128i + + #define stbir_simdi_castf( reg ) _mm_castps_si128(reg) + #define stbir_simdf_casti( reg ) _mm_castsi128_ps(reg) + + #define stbir__simdf_load( reg, ptr ) (reg) = _mm_loadu_ps( (float const*)(ptr) ) + #define stbir__simdi_load( reg, ptr ) (reg) = _mm_loadu_si128 ( (stbir__simdi const*)(ptr) ) + #define stbir__simdf_load1( out, ptr ) (out) = _mm_load_ss( (float const*)(ptr) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdi_load1( out, ptr ) (out) = _mm_castps_si128( _mm_load_ss( (float const*)(ptr) )) + #define stbir__simdf_load1z( out, ptr ) (out) = _mm_load_ss( (float const*)(ptr) ) // top values must be zero + #define stbir__simdf_frep4( fvar ) _mm_set_ps1( fvar ) + #define stbir__simdf_load1frep4( out, fvar ) (out) = _mm_set_ps1( fvar ) + #define stbir__simdf_load2( out, ptr ) (out) = _mm_castsi128_ps( _mm_loadl_epi64( (__m128i*)(ptr)) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdf_load2z( out, ptr ) (out) = _mm_castsi128_ps( _mm_loadl_epi64( (__m128i*)(ptr)) ) // top values must be zero + #define stbir__simdf_load2hmerge( out, reg, ptr ) (out) = _mm_castpd_ps(_mm_loadh_pd( _mm_castps_pd(reg), (double*)(ptr) )) + + #define stbir__simdf_zeroP() _mm_setzero_ps() + #define stbir__simdf_zero( reg ) (reg) = _mm_setzero_ps() + + #define stbir__simdf_store( ptr, reg ) _mm_storeu_ps( (float*)(ptr), reg ) + #define stbir__simdf_store1( ptr, reg ) _mm_store_ss( (float*)(ptr), reg ) + #define stbir__simdf_store2( ptr, reg ) _mm_storel_epi64( (__m128i*)(ptr), _mm_castps_si128(reg) ) + #define stbir__simdf_store2h( ptr, reg ) _mm_storeh_pd( (double*)(ptr), _mm_castps_pd(reg) ) + + #define stbir__simdi_store( ptr, reg ) _mm_storeu_si128( (__m128i*)(ptr), reg ) + #define stbir__simdi_store1( ptr, reg ) _mm_store_ss( (float*)(ptr), _mm_castsi128_ps(reg) ) + #define stbir__simdi_store2( ptr, reg ) _mm_storel_epi64( (__m128i*)(ptr), (reg) ) + + #define stbir__prefetch( ptr ) _mm_prefetch((char*)(ptr), _MM_HINT_T0 ) + + #define stbir__simdi_expand_u8_to_u32(out0,out1,out2,out3,ireg) \ + { \ + stbir__simdi zero = _mm_setzero_si128(); \ + out2 = _mm_unpacklo_epi8( ireg, zero ); \ + out3 = _mm_unpackhi_epi8( ireg, zero ); \ + out0 = _mm_unpacklo_epi16( out2, zero ); \ + out1 = _mm_unpackhi_epi16( out2, zero ); \ + out2 = _mm_unpacklo_epi16( out3, zero ); \ + out3 = _mm_unpackhi_epi16( out3, zero ); \ + } + +#define stbir__simdi_expand_u8_to_1u32(out,ireg) \ + { \ + stbir__simdi zero = _mm_setzero_si128(); \ + out = _mm_unpacklo_epi8( ireg, zero ); \ + out = _mm_unpacklo_epi16( out, zero ); \ + } + + #define stbir__simdi_expand_u16_to_u32(out0,out1,ireg) \ + { \ + stbir__simdi zero = _mm_setzero_si128(); \ + out0 = _mm_unpacklo_epi16( ireg, zero ); \ + out1 = _mm_unpackhi_epi16( ireg, zero ); \ + } + + #define stbir__simdf_convert_float_to_i32( i, f ) (i) = _mm_cvttps_epi32(f) + #define stbir__simdf_convert_float_to_int( f ) _mm_cvtt_ss2si(f) + #define stbir__simdf_convert_float_to_uint8( f ) ((unsigned char)_mm_cvtsi128_si32(_mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(f,STBIR__CONSTF(STBIR_max_uint8_as_float)),_mm_setzero_ps())))) + #define stbir__simdf_convert_float_to_short( f ) ((unsigned short)_mm_cvtsi128_si32(_mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(f,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())))) + + #define stbir__simdi_to_int( i ) _mm_cvtsi128_si32(i) + #define stbir__simdi_convert_i32_to_float(out, ireg) (out) = _mm_cvtepi32_ps( ireg ) + #define stbir__simdf_add( out, reg0, reg1 ) (out) = _mm_add_ps( reg0, reg1 ) + #define stbir__simdf_mult( out, reg0, reg1 ) (out) = _mm_mul_ps( reg0, reg1 ) + #define stbir__simdf_mult_mem( out, reg, ptr ) (out) = _mm_mul_ps( reg, _mm_loadu_ps( (float const*)(ptr) ) ) + #define stbir__simdf_mult1_mem( out, reg, ptr ) (out) = _mm_mul_ss( reg, _mm_load_ss( (float const*)(ptr) ) ) + #define stbir__simdf_add_mem( out, reg, ptr ) (out) = _mm_add_ps( reg, _mm_loadu_ps( (float const*)(ptr) ) ) + #define stbir__simdf_add1_mem( out, reg, ptr ) (out) = _mm_add_ss( reg, _mm_load_ss( (float const*)(ptr) ) ) + + #ifdef STBIR_USE_FMA // not on by default to maintain bit identical simd to non-simd + #include + #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = _mm_fmadd_ps( mul1, mul2, add ) + #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = _mm_fmadd_ss( mul1, mul2, add ) + #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = _mm_fmadd_ps( mul, _mm_loadu_ps( (float const*)(ptr) ), add ) + #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = _mm_fmadd_ss( mul, _mm_load_ss( (float const*)(ptr) ), add ) + #else + #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = _mm_add_ps( add, _mm_mul_ps( mul1, mul2 ) ) + #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = _mm_add_ss( add, _mm_mul_ss( mul1, mul2 ) ) + #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = _mm_add_ps( add, _mm_mul_ps( mul, _mm_loadu_ps( (float const*)(ptr) ) ) ) + #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = _mm_add_ss( add, _mm_mul_ss( mul, _mm_load_ss( (float const*)(ptr) ) ) ) + #endif + + #define stbir__simdf_add1( out, reg0, reg1 ) (out) = _mm_add_ss( reg0, reg1 ) + #define stbir__simdf_mult1( out, reg0, reg1 ) (out) = _mm_mul_ss( reg0, reg1 ) + + #define stbir__simdf_and( out, reg0, reg1 ) (out) = _mm_and_ps( reg0, reg1 ) + #define stbir__simdf_or( out, reg0, reg1 ) (out) = _mm_or_ps( reg0, reg1 ) + + #define stbir__simdf_min( out, reg0, reg1 ) (out) = _mm_min_ps( reg0, reg1 ) + #define stbir__simdf_max( out, reg0, reg1 ) (out) = _mm_max_ps( reg0, reg1 ) + #define stbir__simdf_min1( out, reg0, reg1 ) (out) = _mm_min_ss( reg0, reg1 ) + #define stbir__simdf_max1( out, reg0, reg1 ) (out) = _mm_max_ss( reg0, reg1 ) + + #define stbir__simdf_0123ABCDto3ABx( out, reg0, reg1 ) (out)=_mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( _mm_shuffle_ps( reg1,reg0, (0<<0) + (1<<2) + (2<<4) + (3<<6) )), (3<<0) + (0<<2) + (1<<4) + (2<<6) ) ) + #define stbir__simdf_0123ABCDto23Ax( out, reg0, reg1 ) (out)=_mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( _mm_shuffle_ps( reg1,reg0, (0<<0) + (1<<2) + (2<<4) + (3<<6) )), (2<<0) + (3<<2) + (0<<4) + (1<<6) ) ) + + static const stbir__simdf STBIR_zeroones = { 0.0f,1.0f,0.0f,1.0f }; + static const stbir__simdf STBIR_onezeros = { 1.0f,0.0f,1.0f,0.0f }; + #define stbir__simdf_aaa1( out, alp, ones ) (out)=_mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( _mm_movehl_ps( ones, alp ) ), (1<<0) + (1<<2) + (1<<4) + (2<<6) ) ) + #define stbir__simdf_1aaa( out, alp, ones ) (out)=_mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( _mm_movelh_ps( ones, alp ) ), (0<<0) + (2<<2) + (2<<4) + (2<<6) ) ) + #define stbir__simdf_a1a1( out, alp, ones) (out) = _mm_or_ps( _mm_castsi128_ps( _mm_srli_epi64( _mm_castps_si128(alp), 32 ) ), STBIR_zeroones ) + #define stbir__simdf_1a1a( out, alp, ones) (out) = _mm_or_ps( _mm_castsi128_ps( _mm_slli_epi64( _mm_castps_si128(alp), 32 ) ), STBIR_onezeros ) + + #define stbir__simdf_swiz( reg, one, two, three, four ) _mm_castsi128_ps( _mm_shuffle_epi32( _mm_castps_si128( reg ), (one<<0) + (two<<2) + (three<<4) + (four<<6) ) ) + + #define stbir__simdi_and( out, reg0, reg1 ) (out) = _mm_and_si128( reg0, reg1 ) + #define stbir__simdi_or( out, reg0, reg1 ) (out) = _mm_or_si128( reg0, reg1 ) + #define stbir__simdi_16madd( out, reg0, reg1 ) (out) = _mm_madd_epi16( reg0, reg1 ) + + #define stbir__simdf_pack_to_8bytes(out,aa,bb) \ + { \ + stbir__simdf af,bf; \ + stbir__simdi a,b; \ + af = _mm_min_ps( aa, STBIR_max_uint8_as_float ); \ + bf = _mm_min_ps( bb, STBIR_max_uint8_as_float ); \ + af = _mm_max_ps( af, _mm_setzero_ps() ); \ + bf = _mm_max_ps( bf, _mm_setzero_ps() ); \ + a = _mm_cvttps_epi32( af ); \ + b = _mm_cvttps_epi32( bf ); \ + a = _mm_packs_epi32( a, b ); \ + out = _mm_packus_epi16( a, a ); \ + } + + #define stbir__simdf_load4_transposed( o0, o1, o2, o3, ptr ) \ + stbir__simdf_load( o0, (ptr) ); \ + stbir__simdf_load( o1, (ptr)+4 ); \ + stbir__simdf_load( o2, (ptr)+8 ); \ + stbir__simdf_load( o3, (ptr)+12 ); \ + { \ + __m128 tmp0, tmp1, tmp2, tmp3; \ + tmp0 = _mm_unpacklo_ps(o0, o1); \ + tmp2 = _mm_unpacklo_ps(o2, o3); \ + tmp1 = _mm_unpackhi_ps(o0, o1); \ + tmp3 = _mm_unpackhi_ps(o2, o3); \ + o0 = _mm_movelh_ps(tmp0, tmp2); \ + o1 = _mm_movehl_ps(tmp2, tmp0); \ + o2 = _mm_movelh_ps(tmp1, tmp3); \ + o3 = _mm_movehl_ps(tmp3, tmp1); \ + } + + #define stbir__interleave_pack_and_store_16_u8( ptr, r0, r1, r2, r3 ) \ + r0 = _mm_packs_epi32( r0, r1 ); \ + r2 = _mm_packs_epi32( r2, r3 ); \ + r1 = _mm_unpacklo_epi16( r0, r2 ); \ + r3 = _mm_unpackhi_epi16( r0, r2 ); \ + r0 = _mm_unpacklo_epi16( r1, r3 ); \ + r2 = _mm_unpackhi_epi16( r1, r3 ); \ + r0 = _mm_packus_epi16( r0, r2 ); \ + stbir__simdi_store( ptr, r0 ); \ + + #define stbir__simdi_32shr( out, reg, imm ) out = _mm_srli_epi32( reg, imm ) + + #if defined(_MSC_VER) && !defined(__clang__) + // msvc inits with 8 bytes + #define STBIR__CONST_32_TO_8( v ) (char)(unsigned char)((v)&255),(char)(unsigned char)(((v)>>8)&255),(char)(unsigned char)(((v)>>16)&255),(char)(unsigned char)(((v)>>24)&255) + #define STBIR__CONST_4_32i( v ) STBIR__CONST_32_TO_8( v ), STBIR__CONST_32_TO_8( v ), STBIR__CONST_32_TO_8( v ), STBIR__CONST_32_TO_8( v ) + #define STBIR__CONST_4d_32i( v0, v1, v2, v3 ) STBIR__CONST_32_TO_8( v0 ), STBIR__CONST_32_TO_8( v1 ), STBIR__CONST_32_TO_8( v2 ), STBIR__CONST_32_TO_8( v3 ) + #else + // everything else inits with long long's + #define STBIR__CONST_4_32i( v ) (long long)((((stbir_uint64)(stbir_uint32)(v))<<32)|((stbir_uint64)(stbir_uint32)(v))),(long long)((((stbir_uint64)(stbir_uint32)(v))<<32)|((stbir_uint64)(stbir_uint32)(v))) + #define STBIR__CONST_4d_32i( v0, v1, v2, v3 ) (long long)((((stbir_uint64)(stbir_uint32)(v1))<<32)|((stbir_uint64)(stbir_uint32)(v0))),(long long)((((stbir_uint64)(stbir_uint32)(v3))<<32)|((stbir_uint64)(stbir_uint32)(v2))) + #endif + + #define STBIR__SIMDF_CONST(var, x) stbir__simdf var = { x, x, x, x } + #define STBIR__SIMDI_CONST(var, x) stbir__simdi var = { STBIR__CONST_4_32i(x) } + #define STBIR__CONSTF(var) (var) + #define STBIR__CONSTI(var) (var) + + #if defined(STBIR_AVX) || defined(__SSE4_1__) + #include + #define stbir__simdf_pack_to_8words(out,reg0,reg1) out = _mm_packus_epi32(_mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg0,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())), _mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg1,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps()))) + #else + STBIR__SIMDI_CONST(stbir__s32_32768, 32768); + STBIR__SIMDI_CONST(stbir__s16_32768, ((32768<<16)|32768)); + + #define stbir__simdf_pack_to_8words(out,reg0,reg1) \ + { \ + stbir__simdi tmp0,tmp1; \ + tmp0 = _mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg0,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())); \ + tmp1 = _mm_cvttps_epi32(_mm_max_ps(_mm_min_ps(reg1,STBIR__CONSTF(STBIR_max_uint16_as_float)),_mm_setzero_ps())); \ + tmp0 = _mm_sub_epi32( tmp0, stbir__s32_32768 ); \ + tmp1 = _mm_sub_epi32( tmp1, stbir__s32_32768 ); \ + out = _mm_packs_epi32( tmp0, tmp1 ); \ + out = _mm_sub_epi16( out, stbir__s16_32768 ); \ + } + + #endif + + #define STBIR_SIMD + + // if we detect AVX, set the simd8 defines + #ifdef STBIR_AVX + #include + #define STBIR_SIMD8 + #define stbir__simdf8 __m256 + #define stbir__simdi8 __m256i + #define stbir__simdf8_load( out, ptr ) (out) = _mm256_loadu_ps( (float const *)(ptr) ) + #define stbir__simdi8_load( out, ptr ) (out) = _mm256_loadu_si256( (__m256i const *)(ptr) ) + #define stbir__simdf8_mult( out, a, b ) (out) = _mm256_mul_ps( (a), (b) ) + #define stbir__simdf8_store( ptr, out ) _mm256_storeu_ps( (float*)(ptr), out ) + #define stbir__simdi8_store( ptr, reg ) _mm256_storeu_si256( (__m256i*)(ptr), reg ) + #define stbir__simdf8_frep8( fval ) _mm256_set1_ps( fval ) + + #define stbir__simdf8_min( out, reg0, reg1 ) (out) = _mm256_min_ps( reg0, reg1 ) + #define stbir__simdf8_max( out, reg0, reg1 ) (out) = _mm256_max_ps( reg0, reg1 ) + + #define stbir__simdf8_add4halves( out, bot4, top8 ) (out) = _mm_add_ps( bot4, _mm256_extractf128_ps( top8, 1 ) ) + #define stbir__simdf8_mult_mem( out, reg, ptr ) (out) = _mm256_mul_ps( reg, _mm256_loadu_ps( (float const*)(ptr) ) ) + #define stbir__simdf8_add_mem( out, reg, ptr ) (out) = _mm256_add_ps( reg, _mm256_loadu_ps( (float const*)(ptr) ) ) + #define stbir__simdf8_add( out, a, b ) (out) = _mm256_add_ps( a, b ) + #define stbir__simdf8_load1b( out, ptr ) (out) = _mm256_broadcast_ss( ptr ) + #define stbir__simdf_load1rep4( out, ptr ) (out) = _mm_broadcast_ss( ptr ) // avx load instruction + + #define stbir__simdi8_convert_i32_to_float(out, ireg) (out) = _mm256_cvtepi32_ps( ireg ) + #define stbir__simdf8_convert_float_to_i32( i, f ) (i) = _mm256_cvttps_epi32(f) + + #define stbir__simdf8_bot4s( out, a, b ) (out) = _mm256_permute2f128_ps(a,b, (0<<0)+(2<<4) ) + #define stbir__simdf8_top4s( out, a, b ) (out) = _mm256_permute2f128_ps(a,b, (1<<0)+(3<<4) ) + + #define stbir__simdf8_gettop4( reg ) _mm256_extractf128_ps(reg,1) + + #ifdef STBIR_AVX2 + + #define stbir__simdi8_expand_u8_to_u32(out0,out1,ireg) \ + { \ + stbir__simdi8 a, zero =_mm256_setzero_si256();\ + a = _mm256_permute4x64_epi64( _mm256_unpacklo_epi8( _mm256_permute4x64_epi64(_mm256_castsi128_si256(ireg),(0<<0)+(2<<2)+(1<<4)+(3<<6)), zero ),(0<<0)+(2<<2)+(1<<4)+(3<<6)); \ + out0 = _mm256_unpacklo_epi16( a, zero ); \ + out1 = _mm256_unpackhi_epi16( a, zero ); \ + } + + #define stbir__simdf8_pack_to_16bytes(out,aa,bb) \ + { \ + stbir__simdi8 t; \ + stbir__simdf8 af,bf; \ + stbir__simdi8 a,b; \ + af = _mm256_min_ps( aa, STBIR_max_uint8_as_floatX ); \ + bf = _mm256_min_ps( bb, STBIR_max_uint8_as_floatX ); \ + af = _mm256_max_ps( af, _mm256_setzero_ps() ); \ + bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \ + a = _mm256_cvttps_epi32( af ); \ + b = _mm256_cvttps_epi32( bf ); \ + t = _mm256_permute4x64_epi64( _mm256_packs_epi32( a, b ), (0<<0)+(2<<2)+(1<<4)+(3<<6) ); \ + out = _mm256_castsi256_si128( _mm256_permute4x64_epi64( _mm256_packus_epi16( t, t ), (0<<0)+(2<<2)+(1<<4)+(3<<6) ) ); \ + } + + #define stbir__simdi8_expand_u16_to_u32(out,ireg) out = _mm256_unpacklo_epi16( _mm256_permute4x64_epi64(_mm256_castsi128_si256(ireg),(0<<0)+(2<<2)+(1<<4)+(3<<6)), _mm256_setzero_si256() ); + + #define stbir__simdf8_pack_to_16words(out,aa,bb) \ + { \ + stbir__simdf8 af,bf; \ + stbir__simdi8 a,b; \ + af = _mm256_min_ps( aa, STBIR_max_uint16_as_floatX ); \ + bf = _mm256_min_ps( bb, STBIR_max_uint16_as_floatX ); \ + af = _mm256_max_ps( af, _mm256_setzero_ps() ); \ + bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \ + a = _mm256_cvttps_epi32( af ); \ + b = _mm256_cvttps_epi32( bf ); \ + (out) = _mm256_permute4x64_epi64( _mm256_packus_epi32(a, b), (0<<0)+(2<<2)+(1<<4)+(3<<6) ); \ + } + + #else + + #define stbir__simdi8_expand_u8_to_u32(out0,out1,ireg) \ + { \ + stbir__simdi a,zero = _mm_setzero_si128(); \ + a = _mm_unpacklo_epi8( ireg, zero ); \ + out0 = _mm256_setr_m128i( _mm_unpacklo_epi16( a, zero ), _mm_unpackhi_epi16( a, zero ) ); \ + a = _mm_unpackhi_epi8( ireg, zero ); \ + out1 = _mm256_setr_m128i( _mm_unpacklo_epi16( a, zero ), _mm_unpackhi_epi16( a, zero ) ); \ + } + + #define stbir__simdf8_pack_to_16bytes(out,aa,bb) \ + { \ + stbir__simdi t; \ + stbir__simdf8 af,bf; \ + stbir__simdi8 a,b; \ + af = _mm256_min_ps( aa, STBIR_max_uint8_as_floatX ); \ + bf = _mm256_min_ps( bb, STBIR_max_uint8_as_floatX ); \ + af = _mm256_max_ps( af, _mm256_setzero_ps() ); \ + bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \ + a = _mm256_cvttps_epi32( af ); \ + b = _mm256_cvttps_epi32( bf ); \ + out = _mm_packs_epi32( _mm256_castsi256_si128(a), _mm256_extractf128_si256( a, 1 ) ); \ + out = _mm_packus_epi16( out, out ); \ + t = _mm_packs_epi32( _mm256_castsi256_si128(b), _mm256_extractf128_si256( b, 1 ) ); \ + t = _mm_packus_epi16( t, t ); \ + out = _mm_castps_si128( _mm_shuffle_ps( _mm_castsi128_ps(out), _mm_castsi128_ps(t), (0<<0)+(1<<2)+(0<<4)+(1<<6) ) ); \ + } + + #define stbir__simdi8_expand_u16_to_u32(out,ireg) \ + { \ + stbir__simdi a,b,zero = _mm_setzero_si128(); \ + a = _mm_unpacklo_epi16( ireg, zero ); \ + b = _mm_unpackhi_epi16( ireg, zero ); \ + out = _mm256_insertf128_si256( _mm256_castsi128_si256( a ), b, 1 ); \ + } + + #define stbir__simdf8_pack_to_16words(out,aa,bb) \ + { \ + stbir__simdi t0,t1; \ + stbir__simdf8 af,bf; \ + stbir__simdi8 a,b; \ + af = _mm256_min_ps( aa, STBIR_max_uint16_as_floatX ); \ + bf = _mm256_min_ps( bb, STBIR_max_uint16_as_floatX ); \ + af = _mm256_max_ps( af, _mm256_setzero_ps() ); \ + bf = _mm256_max_ps( bf, _mm256_setzero_ps() ); \ + a = _mm256_cvttps_epi32( af ); \ + b = _mm256_cvttps_epi32( bf ); \ + t0 = _mm_packus_epi32( _mm256_castsi256_si128(a), _mm256_extractf128_si256( a, 1 ) ); \ + t1 = _mm_packus_epi32( _mm256_castsi256_si128(b), _mm256_extractf128_si256( b, 1 ) ); \ + out = _mm256_setr_m128i( t0, t1 ); \ + } + + #endif + + static __m256i stbir_00001111 = { STBIR__CONST_4d_32i( 0, 0, 0, 0 ), STBIR__CONST_4d_32i( 1, 1, 1, 1 ) }; + #define stbir__simdf8_0123to00001111( out, in ) (out) = _mm256_permutevar_ps ( in, stbir_00001111 ) + + static __m256i stbir_22223333 = { STBIR__CONST_4d_32i( 2, 2, 2, 2 ), STBIR__CONST_4d_32i( 3, 3, 3, 3 ) }; + #define stbir__simdf8_0123to22223333( out, in ) (out) = _mm256_permutevar_ps ( in, stbir_22223333 ) + + #define stbir__simdf8_0123to2222( out, in ) (out) = stbir__simdf_swiz(_mm256_castps256_ps128(in), 2,2,2,2 ) + + #define stbir__simdf8_load2( out, ptr ) (out) = _mm256_castsi256_ps(_mm256_castsi128_si256( _mm_loadl_epi64( (__m128i*)(ptr)) )) // top values can be random (not denormal or nan for perf) + #define stbir__simdf8_load4b( out, ptr ) (out) = _mm256_broadcast_ps( (__m128 const *)(ptr) ) + + static __m256i stbir_00112233 = { STBIR__CONST_4d_32i( 0, 0, 1, 1 ), STBIR__CONST_4d_32i( 2, 2, 3, 3 ) }; + #define stbir__simdf8_0123to00112233( out, in ) (out) = _mm256_permutevar_ps ( in, stbir_00112233 ) + #define stbir__simdf8_add4( out, a8, b ) (out) = _mm256_add_ps( a8, _mm256_castps128_ps256( b ) ) + + static __m256i stbir_load6 = { STBIR__CONST_4_32i( 0x80000000 ), STBIR__CONST_4d_32i( 0x80000000, 0x80000000, 0, 0 ) }; + #define stbir__simdf8_load6z( out, ptr ) (out) = _mm256_maskload_ps( ptr, stbir_load6 ) + + #define stbir__simdf8_0123to00000000( out, in ) (out) = _mm256_shuffle_ps ( in, in, (0<<0)+(0<<2)+(0<<4)+(0<<6) ) + #define stbir__simdf8_0123to11111111( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(1<<2)+(1<<4)+(1<<6) ) + #define stbir__simdf8_0123to22222222( out, in ) (out) = _mm256_shuffle_ps ( in, in, (2<<0)+(2<<2)+(2<<4)+(2<<6) ) + #define stbir__simdf8_0123to33333333( out, in ) (out) = _mm256_shuffle_ps ( in, in, (3<<0)+(3<<2)+(3<<4)+(3<<6) ) + #define stbir__simdf8_0123to21032103( out, in ) (out) = _mm256_shuffle_ps ( in, in, (2<<0)+(1<<2)+(0<<4)+(3<<6) ) + #define stbir__simdf8_0123to32103210( out, in ) (out) = _mm256_shuffle_ps ( in, in, (3<<0)+(2<<2)+(1<<4)+(0<<6) ) + #define stbir__simdf8_0123to12301230( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(2<<2)+(3<<4)+(0<<6) ) + #define stbir__simdf8_0123to10321032( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(0<<2)+(3<<4)+(2<<6) ) + #define stbir__simdf8_0123to30123012( out, in ) (out) = _mm256_shuffle_ps ( in, in, (3<<0)+(0<<2)+(1<<4)+(2<<6) ) + + #define stbir__simdf8_0123to11331133( out, in ) (out) = _mm256_shuffle_ps ( in, in, (1<<0)+(1<<2)+(3<<4)+(3<<6) ) + #define stbir__simdf8_0123to00220022( out, in ) (out) = _mm256_shuffle_ps ( in, in, (0<<0)+(0<<2)+(2<<4)+(2<<6) ) + + #define stbir__simdf8_aaa1( out, alp, ones ) (out) = _mm256_blend_ps( alp, ones, (1<<0)+(1<<1)+(1<<2)+(0<<3)+(1<<4)+(1<<5)+(1<<6)+(0<<7)); (out)=_mm256_shuffle_ps( out,out, (3<<0) + (3<<2) + (3<<4) + (0<<6) ) + #define stbir__simdf8_1aaa( out, alp, ones ) (out) = _mm256_blend_ps( alp, ones, (0<<0)+(1<<1)+(1<<2)+(1<<3)+(0<<4)+(1<<5)+(1<<6)+(1<<7)); (out)=_mm256_shuffle_ps( out,out, (1<<0) + (0<<2) + (0<<4) + (0<<6) ) + #define stbir__simdf8_a1a1( out, alp, ones) (out) = _mm256_blend_ps( alp, ones, (1<<0)+(0<<1)+(1<<2)+(0<<3)+(1<<4)+(0<<5)+(1<<6)+(0<<7)); (out)=_mm256_shuffle_ps( out,out, (1<<0) + (0<<2) + (3<<4) + (2<<6) ) + #define stbir__simdf8_1a1a( out, alp, ones) (out) = _mm256_blend_ps( alp, ones, (0<<0)+(1<<1)+(0<<2)+(1<<3)+(0<<4)+(1<<5)+(0<<6)+(1<<7)); (out)=_mm256_shuffle_ps( out,out, (1<<0) + (0<<2) + (3<<4) + (2<<6) ) + + #define stbir__simdf8_zero( reg ) (reg) = _mm256_setzero_ps() + + #ifdef STBIR_USE_FMA // not on by default to maintain bit identical simd to non-simd + #define stbir__simdf8_madd( out, add, mul1, mul2 ) (out) = _mm256_fmadd_ps( mul1, mul2, add ) + #define stbir__simdf8_madd_mem( out, add, mul, ptr ) (out) = _mm256_fmadd_ps( mul, _mm256_loadu_ps( (float const*)(ptr) ), add ) + #define stbir__simdf8_madd_mem4( out, add, mul, ptr ) (out) = _mm256_fmadd_ps( _mm256_castps128_ps256( mul ), _mm256_castps128_ps256( _mm_loadu_ps( (float const*)(ptr) ) ), add ) + #else + #define stbir__simdf8_madd( out, add, mul1, mul2 ) (out) = _mm256_add_ps( add, _mm256_mul_ps( mul1, mul2 ) ) + #define stbir__simdf8_madd_mem( out, add, mul, ptr ) (out) = _mm256_add_ps( add, _mm256_mul_ps( mul, _mm256_loadu_ps( (float const*)(ptr) ) ) ) + #define stbir__simdf8_madd_mem4( out, add, mul, ptr ) (out) = _mm256_add_ps( add, _mm256_castps128_ps256( _mm_mul_ps( mul, _mm_loadu_ps( (float const*)(ptr) ) ) ) ) + #endif + #define stbir__if_simdf8_cast_to_simdf4( val ) _mm256_castps256_ps128( val ) + + #endif + + #ifdef STBIR_FLOORF + #undef STBIR_FLOORF + #endif + #define STBIR_FLOORF stbir_simd_floorf + static stbir__inline float stbir_simd_floorf(float x) // martins floorf + { + #if defined(STBIR_AVX) || defined(__SSE4_1__) || defined(STBIR_SSE41) + __m128 t = _mm_set_ss(x); + return _mm_cvtss_f32( _mm_floor_ss(t, t) ); + #else + __m128 f = _mm_set_ss(x); + __m128 t = _mm_cvtepi32_ps(_mm_cvttps_epi32(f)); + __m128 r = _mm_add_ss(t, _mm_and_ps(_mm_cmplt_ss(f, t), _mm_set_ss(-1.0f))); + return _mm_cvtss_f32(r); + #endif + } + + #ifdef STBIR_CEILF + #undef STBIR_CEILF + #endif + #define STBIR_CEILF stbir_simd_ceilf + static stbir__inline float stbir_simd_ceilf(float x) // martins ceilf + { + #if defined(STBIR_AVX) || defined(__SSE4_1__) || defined(STBIR_SSE41) + __m128 t = _mm_set_ss(x); + return _mm_cvtss_f32( _mm_ceil_ss(t, t) ); + #else + __m128 f = _mm_set_ss(x); + __m128 t = _mm_cvtepi32_ps(_mm_cvttps_epi32(f)); + __m128 r = _mm_add_ss(t, _mm_and_ps(_mm_cmplt_ss(t, f), _mm_set_ss(1.0f))); + return _mm_cvtss_f32(r); + #endif + } + +#elif defined(STBIR_NEON) + + #include + + #define stbir__simdf float32x4_t + #define stbir__simdi uint32x4_t + + #define stbir_simdi_castf( reg ) vreinterpretq_u32_f32(reg) + #define stbir_simdf_casti( reg ) vreinterpretq_f32_u32(reg) + + #define stbir__simdf_load( reg, ptr ) (reg) = vld1q_f32( (float const*)(ptr) ) + #define stbir__simdi_load( reg, ptr ) (reg) = vld1q_u32( (uint32_t const*)(ptr) ) + #define stbir__simdf_load1( out, ptr ) (out) = vld1q_dup_f32( (float const*)(ptr) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdi_load1( out, ptr ) (out) = vld1q_dup_u32( (uint32_t const*)(ptr) ) + #define stbir__simdf_load1z( out, ptr ) (out) = vld1q_lane_f32( (float const*)(ptr), vdupq_n_f32(0), 0 ) // top values must be zero + #define stbir__simdf_frep4( fvar ) vdupq_n_f32( fvar ) + #define stbir__simdf_load1frep4( out, fvar ) (out) = vdupq_n_f32( fvar ) + #define stbir__simdf_load2( out, ptr ) (out) = vcombine_f32( vld1_f32( (float const*)(ptr) ), vcreate_f32(0) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdf_load2z( out, ptr ) (out) = vcombine_f32( vld1_f32( (float const*)(ptr) ), vcreate_f32(0) ) // top values must be zero + #define stbir__simdf_load2hmerge( out, reg, ptr ) (out) = vcombine_f32( vget_low_f32(reg), vld1_f32( (float const*)(ptr) ) ) + + #define stbir__simdf_zeroP() vdupq_n_f32(0) + #define stbir__simdf_zero( reg ) (reg) = vdupq_n_f32(0) + + #define stbir__simdf_store( ptr, reg ) vst1q_f32( (float*)(ptr), reg ) + #define stbir__simdf_store1( ptr, reg ) vst1q_lane_f32( (float*)(ptr), reg, 0) + #define stbir__simdf_store2( ptr, reg ) vst1_f32( (float*)(ptr), vget_low_f32(reg) ) + #define stbir__simdf_store2h( ptr, reg ) vst1_f32( (float*)(ptr), vget_high_f32(reg) ) + + #define stbir__simdi_store( ptr, reg ) vst1q_u32( (uint32_t*)(ptr), reg ) + #define stbir__simdi_store1( ptr, reg ) vst1q_lane_u32( (uint32_t*)(ptr), reg, 0 ) + #define stbir__simdi_store2( ptr, reg ) vst1_u32( (uint32_t*)(ptr), vget_low_u32(reg) ) + + #define stbir__prefetch( ptr ) + + #define stbir__simdi_expand_u8_to_u32(out0,out1,out2,out3,ireg) \ + { \ + uint16x8_t l = vmovl_u8( vget_low_u8 ( vreinterpretq_u8_u32(ireg) ) ); \ + uint16x8_t h = vmovl_u8( vget_high_u8( vreinterpretq_u8_u32(ireg) ) ); \ + out0 = vmovl_u16( vget_low_u16 ( l ) ); \ + out1 = vmovl_u16( vget_high_u16( l ) ); \ + out2 = vmovl_u16( vget_low_u16 ( h ) ); \ + out3 = vmovl_u16( vget_high_u16( h ) ); \ + } + + #define stbir__simdi_expand_u8_to_1u32(out,ireg) \ + { \ + uint16x8_t tmp = vmovl_u8( vget_low_u8( vreinterpretq_u8_u32(ireg) ) ); \ + out = vmovl_u16( vget_low_u16( tmp ) ); \ + } + + #define stbir__simdi_expand_u16_to_u32(out0,out1,ireg) \ + { \ + uint16x8_t tmp = vreinterpretq_u16_u32(ireg); \ + out0 = vmovl_u16( vget_low_u16 ( tmp ) ); \ + out1 = vmovl_u16( vget_high_u16( tmp ) ); \ + } + + #define stbir__simdf_convert_float_to_i32( i, f ) (i) = vreinterpretq_u32_s32( vcvtq_s32_f32(f) ) + #define stbir__simdf_convert_float_to_int( f ) vgetq_lane_s32(vcvtq_s32_f32(f), 0) + #define stbir__simdi_to_int( i ) (int)vgetq_lane_u32(i, 0) + #define stbir__simdf_convert_float_to_uint8( f ) ((unsigned char)vgetq_lane_s32(vcvtq_s32_f32(vmaxq_f32(vminq_f32(f,STBIR__CONSTF(STBIR_max_uint8_as_float)),vdupq_n_f32(0))), 0)) + #define stbir__simdf_convert_float_to_short( f ) ((unsigned short)vgetq_lane_s32(vcvtq_s32_f32(vmaxq_f32(vminq_f32(f,STBIR__CONSTF(STBIR_max_uint16_as_float)),vdupq_n_f32(0))), 0)) + #define stbir__simdi_convert_i32_to_float(out, ireg) (out) = vcvtq_f32_s32( vreinterpretq_s32_u32(ireg) ) + #define stbir__simdf_add( out, reg0, reg1 ) (out) = vaddq_f32( reg0, reg1 ) + #define stbir__simdf_mult( out, reg0, reg1 ) (out) = vmulq_f32( reg0, reg1 ) + #define stbir__simdf_mult_mem( out, reg, ptr ) (out) = vmulq_f32( reg, vld1q_f32( (float const*)(ptr) ) ) + #define stbir__simdf_mult1_mem( out, reg, ptr ) (out) = vmulq_f32( reg, vld1q_dup_f32( (float const*)(ptr) ) ) + #define stbir__simdf_add_mem( out, reg, ptr ) (out) = vaddq_f32( reg, vld1q_f32( (float const*)(ptr) ) ) + #define stbir__simdf_add1_mem( out, reg, ptr ) (out) = vaddq_f32( reg, vld1q_dup_f32( (float const*)(ptr) ) ) + + #ifdef STBIR_USE_FMA // not on by default to maintain bit identical simd to non-simd (and also x64 no madd to arm madd) + #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = vfmaq_f32( add, mul1, mul2 ) + #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = vfmaq_f32( add, mul1, mul2 ) + #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = vfmaq_f32( add, mul, vld1q_f32( (float const*)(ptr) ) ) + #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = vfmaq_f32( add, mul, vld1q_dup_f32( (float const*)(ptr) ) ) + #else + #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = vaddq_f32( add, vmulq_f32( mul1, mul2 ) ) + #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = vaddq_f32( add, vmulq_f32( mul1, mul2 ) ) + #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = vaddq_f32( add, vmulq_f32( mul, vld1q_f32( (float const*)(ptr) ) ) ) + #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = vaddq_f32( add, vmulq_f32( mul, vld1q_dup_f32( (float const*)(ptr) ) ) ) + #endif + + #define stbir__simdf_add1( out, reg0, reg1 ) (out) = vaddq_f32( reg0, reg1 ) + #define stbir__simdf_mult1( out, reg0, reg1 ) (out) = vmulq_f32( reg0, reg1 ) + + #define stbir__simdf_and( out, reg0, reg1 ) (out) = vreinterpretq_f32_u32( vandq_u32( vreinterpretq_u32_f32(reg0), vreinterpretq_u32_f32(reg1) ) ) + #define stbir__simdf_or( out, reg0, reg1 ) (out) = vreinterpretq_f32_u32( vorrq_u32( vreinterpretq_u32_f32(reg0), vreinterpretq_u32_f32(reg1) ) ) + + #define stbir__simdf_min( out, reg0, reg1 ) (out) = vminq_f32( reg0, reg1 ) + #define stbir__simdf_max( out, reg0, reg1 ) (out) = vmaxq_f32( reg0, reg1 ) + #define stbir__simdf_min1( out, reg0, reg1 ) (out) = vminq_f32( reg0, reg1 ) + #define stbir__simdf_max1( out, reg0, reg1 ) (out) = vmaxq_f32( reg0, reg1 ) + + #define stbir__simdf_0123ABCDto3ABx( out, reg0, reg1 ) (out) = vextq_f32( reg0, reg1, 3 ) + #define stbir__simdf_0123ABCDto23Ax( out, reg0, reg1 ) (out) = vextq_f32( reg0, reg1, 2 ) + + #define stbir__simdf_a1a1( out, alp, ones ) (out) = vzipq_f32(vuzpq_f32(alp, alp).val[1], ones).val[0] + #define stbir__simdf_1a1a( out, alp, ones ) (out) = vzipq_f32(ones, vuzpq_f32(alp, alp).val[0]).val[0] + + #if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) + + #define stbir__simdf_aaa1( out, alp, ones ) (out) = vcopyq_laneq_f32(vdupq_n_f32(vgetq_lane_f32(alp, 3)), 3, ones, 3) + #define stbir__simdf_1aaa( out, alp, ones ) (out) = vcopyq_laneq_f32(vdupq_n_f32(vgetq_lane_f32(alp, 0)), 0, ones, 0) + + #if defined( _MSC_VER ) && !defined(__clang__) + #define stbir_make16(a,b,c,d) vcombine_u8( \ + vcreate_u8( (4*a+0) | ((4*a+1)<<8) | ((4*a+2)<<16) | ((4*a+3)<<24) | \ + ((stbir_uint64)(4*b+0)<<32) | ((stbir_uint64)(4*b+1)<<40) | ((stbir_uint64)(4*b+2)<<48) | ((stbir_uint64)(4*b+3)<<56)), \ + vcreate_u8( (4*c+0) | ((4*c+1)<<8) | ((4*c+2)<<16) | ((4*c+3)<<24) | \ + ((stbir_uint64)(4*d+0)<<32) | ((stbir_uint64)(4*d+1)<<40) | ((stbir_uint64)(4*d+2)<<48) | ((stbir_uint64)(4*d+3)<<56) ) ) + #else + #define stbir_make16(a,b,c,d) (uint8x16_t){4*a+0,4*a+1,4*a+2,4*a+3,4*b+0,4*b+1,4*b+2,4*b+3,4*c+0,4*c+1,4*c+2,4*c+3,4*d+0,4*d+1,4*d+2,4*d+3} + #endif + + #define stbir__simdf_swiz( reg, one, two, three, four ) vreinterpretq_f32_u8( vqtbl1q_u8( vreinterpretq_u8_f32(reg), stbir_make16(one, two, three, four) ) ) + + #define stbir__simdi_16madd( out, reg0, reg1 ) \ + { \ + int16x8_t r0 = vreinterpretq_s16_u32(reg0); \ + int16x8_t r1 = vreinterpretq_s16_u32(reg1); \ + int32x4_t tmp0 = vmull_s16( vget_low_s16(r0), vget_low_s16(r1) ); \ + int32x4_t tmp1 = vmull_s16( vget_high_s16(r0), vget_high_s16(r1) ); \ + (out) = vreinterpretq_u32_s32( vpaddq_s32(tmp0, tmp1) ); \ + } + + #else + + #define stbir__simdf_aaa1( out, alp, ones ) (out) = vsetq_lane_f32(1.0f, vdupq_n_f32(vgetq_lane_f32(alp, 3)), 3) + #define stbir__simdf_1aaa( out, alp, ones ) (out) = vsetq_lane_f32(1.0f, vdupq_n_f32(vgetq_lane_f32(alp, 0)), 0) + + #if defined( _MSC_VER ) && !defined(__clang__) + static stbir__inline uint8x8x2_t stbir_make8x2(float32x4_t reg) + { + uint8x8x2_t r = { { vget_low_u8(vreinterpretq_u8_f32(reg)), vget_high_u8(vreinterpretq_u8_f32(reg)) } }; + return r; + } + #define stbir_make8(a,b) vcreate_u8( \ + (4*a+0) | ((4*a+1)<<8) | ((4*a+2)<<16) | ((4*a+3)<<24) | \ + ((stbir_uint64)(4*b+0)<<32) | ((stbir_uint64)(4*b+1)<<40) | ((stbir_uint64)(4*b+2)<<48) | ((stbir_uint64)(4*b+3)<<56) ) + #else + #define stbir_make8x2(reg) (uint8x8x2_t){ { vget_low_u8(vreinterpretq_u8_f32(reg)), vget_high_u8(vreinterpretq_u8_f32(reg)) } } + #define stbir_make8(a,b) (uint8x8_t){4*a+0,4*a+1,4*a+2,4*a+3,4*b+0,4*b+1,4*b+2,4*b+3} + #endif + + #define stbir__simdf_swiz( reg, one, two, three, four ) vreinterpretq_f32_u8( vcombine_u8( \ + vtbl2_u8( stbir_make8x2( reg ), stbir_make8( one, two ) ), \ + vtbl2_u8( stbir_make8x2( reg ), stbir_make8( three, four ) ) ) ) + + #define stbir__simdi_16madd( out, reg0, reg1 ) \ + { \ + int16x8_t r0 = vreinterpretq_s16_u32(reg0); \ + int16x8_t r1 = vreinterpretq_s16_u32(reg1); \ + int32x4_t tmp0 = vmull_s16( vget_low_s16(r0), vget_low_s16(r1) ); \ + int32x4_t tmp1 = vmull_s16( vget_high_s16(r0), vget_high_s16(r1) ); \ + int32x2_t out0 = vpadd_s32( vget_low_s32(tmp0), vget_high_s32(tmp0) ); \ + int32x2_t out1 = vpadd_s32( vget_low_s32(tmp1), vget_high_s32(tmp1) ); \ + (out) = vreinterpretq_u32_s32( vcombine_s32(out0, out1) ); \ + } + + #endif + + #define stbir__simdi_and( out, reg0, reg1 ) (out) = vandq_u32( reg0, reg1 ) + #define stbir__simdi_or( out, reg0, reg1 ) (out) = vorrq_u32( reg0, reg1 ) + + #define stbir__simdf_pack_to_8bytes(out,aa,bb) \ + { \ + float32x4_t af = vmaxq_f32( vminq_f32(aa,STBIR__CONSTF(STBIR_max_uint8_as_float) ), vdupq_n_f32(0) ); \ + float32x4_t bf = vmaxq_f32( vminq_f32(bb,STBIR__CONSTF(STBIR_max_uint8_as_float) ), vdupq_n_f32(0) ); \ + int16x4_t ai = vqmovn_s32( vcvtq_s32_f32( af ) ); \ + int16x4_t bi = vqmovn_s32( vcvtq_s32_f32( bf ) ); \ + uint8x8_t out8 = vqmovun_s16( vcombine_s16(ai, bi) ); \ + out = vreinterpretq_u32_u8( vcombine_u8(out8, out8) ); \ + } + + #define stbir__simdf_pack_to_8words(out,aa,bb) \ + { \ + float32x4_t af = vmaxq_f32( vminq_f32(aa,STBIR__CONSTF(STBIR_max_uint16_as_float) ), vdupq_n_f32(0) ); \ + float32x4_t bf = vmaxq_f32( vminq_f32(bb,STBIR__CONSTF(STBIR_max_uint16_as_float) ), vdupq_n_f32(0) ); \ + int32x4_t ai = vcvtq_s32_f32( af ); \ + int32x4_t bi = vcvtq_s32_f32( bf ); \ + out = vreinterpretq_u32_u16( vcombine_u16(vqmovun_s32(ai), vqmovun_s32(bi)) ); \ + } + + #define stbir__interleave_pack_and_store_16_u8( ptr, r0, r1, r2, r3 ) \ + { \ + int16x4x2_t tmp0 = vzip_s16( vqmovn_s32(vreinterpretq_s32_u32(r0)), vqmovn_s32(vreinterpretq_s32_u32(r2)) ); \ + int16x4x2_t tmp1 = vzip_s16( vqmovn_s32(vreinterpretq_s32_u32(r1)), vqmovn_s32(vreinterpretq_s32_u32(r3)) ); \ + uint8x8x2_t out = \ + { { \ + vqmovun_s16( vcombine_s16(tmp0.val[0], tmp0.val[1]) ), \ + vqmovun_s16( vcombine_s16(tmp1.val[0], tmp1.val[1]) ), \ + } }; \ + vst2_u8(ptr, out); \ + } + + #define stbir__simdf_load4_transposed( o0, o1, o2, o3, ptr ) \ + { \ + float32x4x4_t tmp = vld4q_f32(ptr); \ + o0 = tmp.val[0]; \ + o1 = tmp.val[1]; \ + o2 = tmp.val[2]; \ + o3 = tmp.val[3]; \ + } + + #define stbir__simdi_32shr( out, reg, imm ) out = vshrq_n_u32( reg, imm ) + + #if defined( _MSC_VER ) && !defined(__clang__) + #define STBIR__SIMDF_CONST(var, x) __declspec(align(8)) float var[] = { x, x, x, x } + #define STBIR__SIMDI_CONST(var, x) __declspec(align(8)) uint32_t var[] = { x, x, x, x } + #define STBIR__CONSTF(var) (*(const float32x4_t*)var) + #define STBIR__CONSTI(var) (*(const uint32x4_t*)var) + #else + #define STBIR__SIMDF_CONST(var, x) stbir__simdf var = { x, x, x, x } + #define STBIR__SIMDI_CONST(var, x) stbir__simdi var = { x, x, x, x } + #define STBIR__CONSTF(var) (var) + #define STBIR__CONSTI(var) (var) + #endif + + #ifdef STBIR_FLOORF + #undef STBIR_FLOORF + #endif + #define STBIR_FLOORF stbir_simd_floorf + static stbir__inline float stbir_simd_floorf(float x) + { + #if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) + return vget_lane_f32( vrndm_f32( vdup_n_f32(x) ), 0); + #else + float32x2_t f = vdup_n_f32(x); + float32x2_t t = vcvt_f32_s32(vcvt_s32_f32(f)); + uint32x2_t a = vclt_f32(f, t); + uint32x2_t b = vreinterpret_u32_f32(vdup_n_f32(-1.0f)); + float32x2_t r = vadd_f32(t, vreinterpret_f32_u32(vand_u32(a, b))); + return vget_lane_f32(r, 0); + #endif + } + + #ifdef STBIR_CEILF + #undef STBIR_CEILF + #endif + #define STBIR_CEILF stbir_simd_ceilf + static stbir__inline float stbir_simd_ceilf(float x) + { + #if defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) + return vget_lane_f32( vrndp_f32( vdup_n_f32(x) ), 0); + #else + float32x2_t f = vdup_n_f32(x); + float32x2_t t = vcvt_f32_s32(vcvt_s32_f32(f)); + uint32x2_t a = vclt_f32(t, f); + uint32x2_t b = vreinterpret_u32_f32(vdup_n_f32(1.0f)); + float32x2_t r = vadd_f32(t, vreinterpret_f32_u32(vand_u32(a, b))); + return vget_lane_f32(r, 0); + #endif + } + + #define STBIR_SIMD + +#elif defined(STBIR_WASM) + + #include + + #define stbir__simdf v128_t + #define stbir__simdi v128_t + + #define stbir_simdi_castf( reg ) (reg) + #define stbir_simdf_casti( reg ) (reg) + + #define stbir__simdf_load( reg, ptr ) (reg) = wasm_v128_load( (void const*)(ptr) ) + #define stbir__simdi_load( reg, ptr ) (reg) = wasm_v128_load( (void const*)(ptr) ) + #define stbir__simdf_load1( out, ptr ) (out) = wasm_v128_load32_splat( (void const*)(ptr) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdi_load1( out, ptr ) (out) = wasm_v128_load32_splat( (void const*)(ptr) ) + #define stbir__simdf_load1z( out, ptr ) (out) = wasm_v128_load32_zero( (void const*)(ptr) ) // top values must be zero + #define stbir__simdf_frep4( fvar ) wasm_f32x4_splat( fvar ) + #define stbir__simdf_load1frep4( out, fvar ) (out) = wasm_f32x4_splat( fvar ) + #define stbir__simdf_load2( out, ptr ) (out) = wasm_v128_load64_splat( (void const*)(ptr) ) // top values can be random (not denormal or nan for perf) + #define stbir__simdf_load2z( out, ptr ) (out) = wasm_v128_load64_zero( (void const*)(ptr) ) // top values must be zero + #define stbir__simdf_load2hmerge( out, reg, ptr ) (out) = wasm_v128_load64_lane( (void const*)(ptr), reg, 1 ) + + #define stbir__simdf_zeroP() wasm_f32x4_const_splat(0) + #define stbir__simdf_zero( reg ) (reg) = wasm_f32x4_const_splat(0) + + #define stbir__simdf_store( ptr, reg ) wasm_v128_store( (void*)(ptr), reg ) + #define stbir__simdf_store1( ptr, reg ) wasm_v128_store32_lane( (void*)(ptr), reg, 0 ) + #define stbir__simdf_store2( ptr, reg ) wasm_v128_store64_lane( (void*)(ptr), reg, 0 ) + #define stbir__simdf_store2h( ptr, reg ) wasm_v128_store64_lane( (void*)(ptr), reg, 1 ) + + #define stbir__simdi_store( ptr, reg ) wasm_v128_store( (void*)(ptr), reg ) + #define stbir__simdi_store1( ptr, reg ) wasm_v128_store32_lane( (void*)(ptr), reg, 0 ) + #define stbir__simdi_store2( ptr, reg ) wasm_v128_store64_lane( (void*)(ptr), reg, 0 ) + + #define stbir__prefetch( ptr ) + + #define stbir__simdi_expand_u8_to_u32(out0,out1,out2,out3,ireg) \ + { \ + v128_t l = wasm_u16x8_extend_low_u8x16 ( ireg ); \ + v128_t h = wasm_u16x8_extend_high_u8x16( ireg ); \ + out0 = wasm_u32x4_extend_low_u16x8 ( l ); \ + out1 = wasm_u32x4_extend_high_u16x8( l ); \ + out2 = wasm_u32x4_extend_low_u16x8 ( h ); \ + out3 = wasm_u32x4_extend_high_u16x8( h ); \ + } + + #define stbir__simdi_expand_u8_to_1u32(out,ireg) \ + { \ + v128_t tmp = wasm_u16x8_extend_low_u8x16(ireg); \ + out = wasm_u32x4_extend_low_u16x8(tmp); \ + } + + #define stbir__simdi_expand_u16_to_u32(out0,out1,ireg) \ + { \ + out0 = wasm_u32x4_extend_low_u16x8 ( ireg ); \ + out1 = wasm_u32x4_extend_high_u16x8( ireg ); \ + } + + #define stbir__simdf_convert_float_to_i32( i, f ) (i) = wasm_i32x4_trunc_sat_f32x4(f) + #define stbir__simdf_convert_float_to_int( f ) wasm_i32x4_extract_lane(wasm_i32x4_trunc_sat_f32x4(f), 0) + #define stbir__simdi_to_int( i ) wasm_i32x4_extract_lane(i, 0) + #define stbir__simdf_convert_float_to_uint8( f ) ((unsigned char)wasm_i32x4_extract_lane(wasm_i32x4_trunc_sat_f32x4(wasm_f32x4_max(wasm_f32x4_min(f,STBIR_max_uint8_as_float),wasm_f32x4_const_splat(0))), 0)) + #define stbir__simdf_convert_float_to_short( f ) ((unsigned short)wasm_i32x4_extract_lane(wasm_i32x4_trunc_sat_f32x4(wasm_f32x4_max(wasm_f32x4_min(f,STBIR_max_uint16_as_float),wasm_f32x4_const_splat(0))), 0)) + #define stbir__simdi_convert_i32_to_float(out, ireg) (out) = wasm_f32x4_convert_i32x4(ireg) + #define stbir__simdf_add( out, reg0, reg1 ) (out) = wasm_f32x4_add( reg0, reg1 ) + #define stbir__simdf_mult( out, reg0, reg1 ) (out) = wasm_f32x4_mul( reg0, reg1 ) + #define stbir__simdf_mult_mem( out, reg, ptr ) (out) = wasm_f32x4_mul( reg, wasm_v128_load( (void const*)(ptr) ) ) + #define stbir__simdf_mult1_mem( out, reg, ptr ) (out) = wasm_f32x4_mul( reg, wasm_v128_load32_splat( (void const*)(ptr) ) ) + #define stbir__simdf_add_mem( out, reg, ptr ) (out) = wasm_f32x4_add( reg, wasm_v128_load( (void const*)(ptr) ) ) + #define stbir__simdf_add1_mem( out, reg, ptr ) (out) = wasm_f32x4_add( reg, wasm_v128_load32_splat( (void const*)(ptr) ) ) + + #define stbir__simdf_madd( out, add, mul1, mul2 ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul1, mul2 ) ) + #define stbir__simdf_madd1( out, add, mul1, mul2 ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul1, mul2 ) ) + #define stbir__simdf_madd_mem( out, add, mul, ptr ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul, wasm_v128_load( (void const*)(ptr) ) ) ) + #define stbir__simdf_madd1_mem( out, add, mul, ptr ) (out) = wasm_f32x4_add( add, wasm_f32x4_mul( mul, wasm_v128_load32_splat( (void const*)(ptr) ) ) ) + + #define stbir__simdf_add1( out, reg0, reg1 ) (out) = wasm_f32x4_add( reg0, reg1 ) + #define stbir__simdf_mult1( out, reg0, reg1 ) (out) = wasm_f32x4_mul( reg0, reg1 ) + + #define stbir__simdf_and( out, reg0, reg1 ) (out) = wasm_v128_and( reg0, reg1 ) + #define stbir__simdf_or( out, reg0, reg1 ) (out) = wasm_v128_or( reg0, reg1 ) + + #define stbir__simdf_min( out, reg0, reg1 ) (out) = wasm_f32x4_min( reg0, reg1 ) + #define stbir__simdf_max( out, reg0, reg1 ) (out) = wasm_f32x4_max( reg0, reg1 ) + #define stbir__simdf_min1( out, reg0, reg1 ) (out) = wasm_f32x4_min( reg0, reg1 ) + #define stbir__simdf_max1( out, reg0, reg1 ) (out) = wasm_f32x4_max( reg0, reg1 ) + + #define stbir__simdf_0123ABCDto3ABx( out, reg0, reg1 ) (out) = wasm_i32x4_shuffle( reg0, reg1, 3, 4, 5, -1 ) + #define stbir__simdf_0123ABCDto23Ax( out, reg0, reg1 ) (out) = wasm_i32x4_shuffle( reg0, reg1, 2, 3, 4, -1 ) + + #define stbir__simdf_aaa1(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 3, 3, 3, 4) + #define stbir__simdf_1aaa(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 4, 0, 0, 0) + #define stbir__simdf_a1a1(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 1, 4, 3, 4) + #define stbir__simdf_1a1a(out,alp,ones) (out) = wasm_i32x4_shuffle(alp, ones, 4, 0, 4, 2) + + #define stbir__simdf_swiz( reg, one, two, three, four ) wasm_i32x4_shuffle(reg, reg, one, two, three, four) + + #define stbir__simdi_and( out, reg0, reg1 ) (out) = wasm_v128_and( reg0, reg1 ) + #define stbir__simdi_or( out, reg0, reg1 ) (out) = wasm_v128_or( reg0, reg1 ) + #define stbir__simdi_16madd( out, reg0, reg1 ) (out) = wasm_i32x4_dot_i16x8( reg0, reg1 ) + + #define stbir__simdf_pack_to_8bytes(out,aa,bb) \ + { \ + v128_t af = wasm_f32x4_max( wasm_f32x4_min(aa, STBIR_max_uint8_as_float), wasm_f32x4_const_splat(0) ); \ + v128_t bf = wasm_f32x4_max( wasm_f32x4_min(bb, STBIR_max_uint8_as_float), wasm_f32x4_const_splat(0) ); \ + v128_t ai = wasm_i32x4_trunc_sat_f32x4( af ); \ + v128_t bi = wasm_i32x4_trunc_sat_f32x4( bf ); \ + v128_t out16 = wasm_i16x8_narrow_i32x4( ai, bi ); \ + out = wasm_u8x16_narrow_i16x8( out16, out16 ); \ + } + + #define stbir__simdf_pack_to_8words(out,aa,bb) \ + { \ + v128_t af = wasm_f32x4_max( wasm_f32x4_min(aa, STBIR_max_uint16_as_float), wasm_f32x4_const_splat(0)); \ + v128_t bf = wasm_f32x4_max( wasm_f32x4_min(bb, STBIR_max_uint16_as_float), wasm_f32x4_const_splat(0)); \ + v128_t ai = wasm_i32x4_trunc_sat_f32x4( af ); \ + v128_t bi = wasm_i32x4_trunc_sat_f32x4( bf ); \ + out = wasm_u16x8_narrow_i32x4( ai, bi ); \ + } + + #define stbir__interleave_pack_and_store_16_u8( ptr, r0, r1, r2, r3 ) \ + { \ + v128_t tmp0 = wasm_i16x8_narrow_i32x4(r0, r1); \ + v128_t tmp1 = wasm_i16x8_narrow_i32x4(r2, r3); \ + v128_t tmp = wasm_u8x16_narrow_i16x8(tmp0, tmp1); \ + tmp = wasm_i8x16_shuffle(tmp, tmp, 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15); \ + wasm_v128_store( (void*)(ptr), tmp); \ + } + + #define stbir__simdf_load4_transposed( o0, o1, o2, o3, ptr ) \ + { \ + v128_t t0 = wasm_v128_load( ptr ); \ + v128_t t1 = wasm_v128_load( ptr+4 ); \ + v128_t t2 = wasm_v128_load( ptr+8 ); \ + v128_t t3 = wasm_v128_load( ptr+12 ); \ + v128_t s0 = wasm_i32x4_shuffle(t0, t1, 0, 4, 2, 6); \ + v128_t s1 = wasm_i32x4_shuffle(t0, t1, 1, 5, 3, 7); \ + v128_t s2 = wasm_i32x4_shuffle(t2, t3, 0, 4, 2, 6); \ + v128_t s3 = wasm_i32x4_shuffle(t2, t3, 1, 5, 3, 7); \ + o0 = wasm_i32x4_shuffle(s0, s2, 0, 1, 4, 5); \ + o1 = wasm_i32x4_shuffle(s1, s3, 0, 1, 4, 5); \ + o2 = wasm_i32x4_shuffle(s0, s2, 2, 3, 6, 7); \ + o3 = wasm_i32x4_shuffle(s1, s3, 2, 3, 6, 7); \ + } + + #define stbir__simdi_32shr( out, reg, imm ) out = wasm_u32x4_shr( reg, imm ) + + typedef float stbir__f32x4 __attribute__((__vector_size__(16), __aligned__(16))); + #define STBIR__SIMDF_CONST(var, x) stbir__simdf var = (v128_t)(stbir__f32x4){ x, x, x, x } + #define STBIR__SIMDI_CONST(var, x) stbir__simdi var = { x, x, x, x } + #define STBIR__CONSTF(var) (var) + #define STBIR__CONSTI(var) (var) + + #ifdef STBIR_FLOORF + #undef STBIR_FLOORF + #endif + #define STBIR_FLOORF stbir_simd_floorf + static stbir__inline float stbir_simd_floorf(float x) + { + return wasm_f32x4_extract_lane( wasm_f32x4_floor( wasm_f32x4_splat(x) ), 0); + } + + #ifdef STBIR_CEILF + #undef STBIR_CEILF + #endif + #define STBIR_CEILF stbir_simd_ceilf + static stbir__inline float stbir_simd_ceilf(float x) + { + return wasm_f32x4_extract_lane( wasm_f32x4_ceil( wasm_f32x4_splat(x) ), 0); + } + + #define STBIR_SIMD + +#endif // SSE2/NEON/WASM + +#endif // NO SIMD + +#ifdef STBIR_SIMD8 + #define stbir__simdfX stbir__simdf8 + #define stbir__simdiX stbir__simdi8 + #define stbir__simdfX_load stbir__simdf8_load + #define stbir__simdiX_load stbir__simdi8_load + #define stbir__simdfX_mult stbir__simdf8_mult + #define stbir__simdfX_add_mem stbir__simdf8_add_mem + #define stbir__simdfX_madd_mem stbir__simdf8_madd_mem + #define stbir__simdfX_store stbir__simdf8_store + #define stbir__simdiX_store stbir__simdi8_store + #define stbir__simdf_frepX stbir__simdf8_frep8 + #define stbir__simdfX_madd stbir__simdf8_madd + #define stbir__simdfX_min stbir__simdf8_min + #define stbir__simdfX_max stbir__simdf8_max + #define stbir__simdfX_aaa1 stbir__simdf8_aaa1 + #define stbir__simdfX_1aaa stbir__simdf8_1aaa + #define stbir__simdfX_a1a1 stbir__simdf8_a1a1 + #define stbir__simdfX_1a1a stbir__simdf8_1a1a + #define stbir__simdfX_convert_float_to_i32 stbir__simdf8_convert_float_to_i32 + #define stbir__simdfX_pack_to_words stbir__simdf8_pack_to_16words + #define stbir__simdfX_zero stbir__simdf8_zero + #define STBIR_onesX STBIR_ones8 + #define STBIR_max_uint8_as_floatX STBIR_max_uint8_as_float8 + #define STBIR_max_uint16_as_floatX STBIR_max_uint16_as_float8 + #define STBIR_simd_point5X STBIR_simd_point58 + #define stbir__simdfX_float_count 8 + #define stbir__simdfX_0123to1230 stbir__simdf8_0123to12301230 + #define stbir__simdfX_0123to2103 stbir__simdf8_0123to21032103 + static const stbir__simdf8 STBIR_max_uint16_as_float_inverted8 = { stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted,stbir__max_uint16_as_float_inverted }; + static const stbir__simdf8 STBIR_max_uint8_as_float_inverted8 = { stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted,stbir__max_uint8_as_float_inverted }; + static const stbir__simdf8 STBIR_ones8 = { 1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0 }; + static const stbir__simdf8 STBIR_simd_point58 = { 0.5,0.5,0.5,0.5,0.5,0.5,0.5,0.5 }; + static const stbir__simdf8 STBIR_max_uint8_as_float8 = { stbir__max_uint8_as_float,stbir__max_uint8_as_float,stbir__max_uint8_as_float,stbir__max_uint8_as_float, stbir__max_uint8_as_float,stbir__max_uint8_as_float,stbir__max_uint8_as_float,stbir__max_uint8_as_float }; + static const stbir__simdf8 STBIR_max_uint16_as_float8 = { stbir__max_uint16_as_float,stbir__max_uint16_as_float,stbir__max_uint16_as_float,stbir__max_uint16_as_float, stbir__max_uint16_as_float,stbir__max_uint16_as_float,stbir__max_uint16_as_float,stbir__max_uint16_as_float }; +#else + #define stbir__simdfX stbir__simdf + #define stbir__simdiX stbir__simdi + #define stbir__simdfX_load stbir__simdf_load + #define stbir__simdiX_load stbir__simdi_load + #define stbir__simdfX_mult stbir__simdf_mult + #define stbir__simdfX_add_mem stbir__simdf_add_mem + #define stbir__simdfX_madd_mem stbir__simdf_madd_mem + #define stbir__simdfX_store stbir__simdf_store + #define stbir__simdiX_store stbir__simdi_store + #define stbir__simdf_frepX stbir__simdf_frep4 + #define stbir__simdfX_madd stbir__simdf_madd + #define stbir__simdfX_min stbir__simdf_min + #define stbir__simdfX_max stbir__simdf_max + #define stbir__simdfX_aaa1 stbir__simdf_aaa1 + #define stbir__simdfX_1aaa stbir__simdf_1aaa + #define stbir__simdfX_a1a1 stbir__simdf_a1a1 + #define stbir__simdfX_1a1a stbir__simdf_1a1a + #define stbir__simdfX_convert_float_to_i32 stbir__simdf_convert_float_to_i32 + #define stbir__simdfX_pack_to_words stbir__simdf_pack_to_8words + #define stbir__simdfX_zero stbir__simdf_zero + #define STBIR_onesX STBIR__CONSTF(STBIR_ones) + #define STBIR_simd_point5X STBIR__CONSTF(STBIR_simd_point5) + #define STBIR_max_uint8_as_floatX STBIR__CONSTF(STBIR_max_uint8_as_float) + #define STBIR_max_uint16_as_floatX STBIR__CONSTF(STBIR_max_uint16_as_float) + #define stbir__simdfX_float_count 4 + #define stbir__if_simdf8_cast_to_simdf4( val ) ( val ) + #define stbir__simdfX_0123to1230 stbir__simdf_0123to1230 + #define stbir__simdfX_0123to2103 stbir__simdf_0123to2103 +#endif + + +#if defined(STBIR_NEON) && !defined(_M_ARM) + + #if defined( _MSC_VER ) && !defined(__clang__) + typedef __int16 stbir__FP16; + #else + typedef float16_t stbir__FP16; + #endif + +#else // no NEON, or 32-bit ARM for MSVC + + typedef union stbir__FP16 + { + unsigned short u; + } stbir__FP16; + +#endif + +#if !defined(STBIR_NEON) && !defined(STBIR_FP16C) || defined(STBIR_NEON) && defined(_M_ARM) + + // Fabian's half float routines, see: https://gist.github.com/rygorous/2156668 + + static stbir__inline float stbir__half_to_float( stbir__FP16 h ) + { + static const stbir__FP32 magic = { (254 - 15) << 23 }; + static const stbir__FP32 was_infnan = { (127 + 16) << 23 }; + stbir__FP32 o; + + o.u = (h.u & 0x7fff) << 13; // exponent/mantissa bits + o.f *= magic.f; // exponent adjust + if (o.f >= was_infnan.f) // make sure Inf/NaN survive + o.u |= 255 << 23; + o.u |= (h.u & 0x8000) << 16; // sign bit + return o.f; + } + + static stbir__inline stbir__FP16 stbir__float_to_half(float val) + { + stbir__FP32 f32infty = { 255 << 23 }; + stbir__FP32 f16max = { (127 + 16) << 23 }; + stbir__FP32 denorm_magic = { ((127 - 15) + (23 - 10) + 1) << 23 }; + unsigned int sign_mask = 0x80000000u; + stbir__FP16 o = { 0 }; + stbir__FP32 f; + unsigned int sign; + + f.f = val; + sign = f.u & sign_mask; + f.u ^= sign; + + if (f.u >= f16max.u) // result is Inf or NaN (all exponent bits set) + o.u = (f.u > f32infty.u) ? 0x7e00 : 0x7c00; // NaN->qNaN and Inf->Inf + else // (De)normalized number or zero + { + if (f.u < (113 << 23)) // resulting FP16 is subnormal or zero + { + // use a magic value to align our 10 mantissa bits at the bottom of + // the float. as long as FP addition is round-to-nearest-even this + // just works. + f.f += denorm_magic.f; + // and one integer subtract of the bias later, we have our final float! + o.u = (unsigned short) ( f.u - denorm_magic.u ); + } + else + { + unsigned int mant_odd = (f.u >> 13) & 1; // resulting mantissa is odd + // update exponent, rounding bias part 1 + f.u = f.u + ((15u - 127) << 23) + 0xfff; + // rounding bias part 2 + f.u += mant_odd; + // take the bits! + o.u = (unsigned short) ( f.u >> 13 ); + } + } + + o.u |= sign >> 16; + return o; + } + +#endif + + +#if defined(STBIR_FP16C) + + #include + + static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input) + { + _mm256_storeu_ps( (float*)output, _mm256_cvtph_ps( _mm_loadu_si128( (__m128i const* )input ) ) ); + } + + static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input) + { + _mm_storeu_si128( (__m128i*)output, _mm256_cvtps_ph( _mm256_loadu_ps( input ), 0 ) ); + } + + static stbir__inline float stbir__half_to_float( stbir__FP16 h ) + { + return _mm_cvtss_f32( _mm_cvtph_ps( _mm_cvtsi32_si128( (int)h.u ) ) ); + } + + static stbir__inline stbir__FP16 stbir__float_to_half( float f ) + { + stbir__FP16 h; + h.u = (unsigned short) _mm_cvtsi128_si32( _mm_cvtps_ph( _mm_set_ss( f ), 0 ) ); + return h; + } + +#elif defined(STBIR_SSE2) + + // Fabian's half float routines, see: https://gist.github.com/rygorous/2156668 + stbir__inline static void stbir__half_to_float_SIMD(float * output, void const * input) + { + static const STBIR__SIMDI_CONST(mask_nosign, 0x7fff); + static const STBIR__SIMDI_CONST(smallest_normal, 0x0400); + static const STBIR__SIMDI_CONST(infinity, 0x7c00); + static const STBIR__SIMDI_CONST(expadjust_normal, (127 - 15) << 23); + static const STBIR__SIMDI_CONST(magic_denorm, 113 << 23); + + __m128i i = _mm_loadu_si128 ( (__m128i const*)(input) ); + __m128i h = _mm_unpacklo_epi16 ( i, _mm_setzero_si128() ); + __m128i mnosign = STBIR__CONSTI(mask_nosign); + __m128i eadjust = STBIR__CONSTI(expadjust_normal); + __m128i smallest = STBIR__CONSTI(smallest_normal); + __m128i infty = STBIR__CONSTI(infinity); + __m128i expmant = _mm_and_si128(mnosign, h); + __m128i justsign = _mm_xor_si128(h, expmant); + __m128i b_notinfnan = _mm_cmpgt_epi32(infty, expmant); + __m128i b_isdenorm = _mm_cmpgt_epi32(smallest, expmant); + __m128i shifted = _mm_slli_epi32(expmant, 13); + __m128i adj_infnan = _mm_andnot_si128(b_notinfnan, eadjust); + __m128i adjusted = _mm_add_epi32(eadjust, shifted); + __m128i den1 = _mm_add_epi32(shifted, STBIR__CONSTI(magic_denorm)); + __m128i adjusted2 = _mm_add_epi32(adjusted, adj_infnan); + __m128 den2 = _mm_sub_ps(_mm_castsi128_ps(den1), *(const __m128 *)&magic_denorm); + __m128 adjusted3 = _mm_and_ps(den2, _mm_castsi128_ps(b_isdenorm)); + __m128 adjusted4 = _mm_andnot_ps(_mm_castsi128_ps(b_isdenorm), _mm_castsi128_ps(adjusted2)); + __m128 adjusted5 = _mm_or_ps(adjusted3, adjusted4); + __m128i sign = _mm_slli_epi32(justsign, 16); + __m128 final = _mm_or_ps(adjusted5, _mm_castsi128_ps(sign)); + stbir__simdf_store( output + 0, final ); + + h = _mm_unpackhi_epi16 ( i, _mm_setzero_si128() ); + expmant = _mm_and_si128(mnosign, h); + justsign = _mm_xor_si128(h, expmant); + b_notinfnan = _mm_cmpgt_epi32(infty, expmant); + b_isdenorm = _mm_cmpgt_epi32(smallest, expmant); + shifted = _mm_slli_epi32(expmant, 13); + adj_infnan = _mm_andnot_si128(b_notinfnan, eadjust); + adjusted = _mm_add_epi32(eadjust, shifted); + den1 = _mm_add_epi32(shifted, STBIR__CONSTI(magic_denorm)); + adjusted2 = _mm_add_epi32(adjusted, adj_infnan); + den2 = _mm_sub_ps(_mm_castsi128_ps(den1), *(const __m128 *)&magic_denorm); + adjusted3 = _mm_and_ps(den2, _mm_castsi128_ps(b_isdenorm)); + adjusted4 = _mm_andnot_ps(_mm_castsi128_ps(b_isdenorm), _mm_castsi128_ps(adjusted2)); + adjusted5 = _mm_or_ps(adjusted3, adjusted4); + sign = _mm_slli_epi32(justsign, 16); + final = _mm_or_ps(adjusted5, _mm_castsi128_ps(sign)); + stbir__simdf_store( output + 4, final ); + + // ~38 SSE2 ops for 8 values + } + + // Fabian's round-to-nearest-even float to half + // ~48 SSE2 ops for 8 output + stbir__inline static void stbir__float_to_half_SIMD(void * output, float const * input) + { + static const STBIR__SIMDI_CONST(mask_sign, 0x80000000u); + static const STBIR__SIMDI_CONST(c_f16max, (127 + 16) << 23); // all FP32 values >=this round to +inf + static const STBIR__SIMDI_CONST(c_nanbit, 0x200); + static const STBIR__SIMDI_CONST(c_infty_as_fp16, 0x7c00); + static const STBIR__SIMDI_CONST(c_min_normal, (127 - 14) << 23); // smallest FP32 that yields a normalized FP16 + static const STBIR__SIMDI_CONST(c_subnorm_magic, ((127 - 15) + (23 - 10) + 1) << 23); + static const STBIR__SIMDI_CONST(c_normal_bias, 0xfff - ((127 - 15) << 23)); // adjust exponent and add mantissa rounding + + __m128 f = _mm_loadu_ps(input); + __m128 msign = _mm_castsi128_ps(STBIR__CONSTI(mask_sign)); + __m128 justsign = _mm_and_ps(msign, f); + __m128 absf = _mm_xor_ps(f, justsign); + __m128i absf_int = _mm_castps_si128(absf); // the cast is "free" (extra bypass latency, but no thruput hit) + __m128i f16max = STBIR__CONSTI(c_f16max); + __m128 b_isnan = _mm_cmpunord_ps(absf, absf); // is this a NaN? + __m128i b_isregular = _mm_cmpgt_epi32(f16max, absf_int); // (sub)normalized or special? + __m128i nanbit = _mm_and_si128(_mm_castps_si128(b_isnan), STBIR__CONSTI(c_nanbit)); + __m128i inf_or_nan = _mm_or_si128(nanbit, STBIR__CONSTI(c_infty_as_fp16)); // output for specials + + __m128i min_normal = STBIR__CONSTI(c_min_normal); + __m128i b_issub = _mm_cmpgt_epi32(min_normal, absf_int); + + // "result is subnormal" path + __m128 subnorm1 = _mm_add_ps(absf, _mm_castsi128_ps(STBIR__CONSTI(c_subnorm_magic))); // magic value to round output mantissa + __m128i subnorm2 = _mm_sub_epi32(_mm_castps_si128(subnorm1), STBIR__CONSTI(c_subnorm_magic)); // subtract out bias + + // "result is normal" path + __m128i mantoddbit = _mm_slli_epi32(absf_int, 31 - 13); // shift bit 13 (mantissa LSB) to sign + __m128i mantodd = _mm_srai_epi32(mantoddbit, 31); // -1 if FP16 mantissa odd, else 0 + + __m128i round1 = _mm_add_epi32(absf_int, STBIR__CONSTI(c_normal_bias)); + __m128i round2 = _mm_sub_epi32(round1, mantodd); // if mantissa LSB odd, bias towards rounding up (RTNE) + __m128i normal = _mm_srli_epi32(round2, 13); // rounded result + + // combine the two non-specials + __m128i nonspecial = _mm_or_si128(_mm_and_si128(subnorm2, b_issub), _mm_andnot_si128(b_issub, normal)); + + // merge in specials as well + __m128i joined = _mm_or_si128(_mm_and_si128(nonspecial, b_isregular), _mm_andnot_si128(b_isregular, inf_or_nan)); + + __m128i sign_shift = _mm_srai_epi32(_mm_castps_si128(justsign), 16); + __m128i final2, final= _mm_or_si128(joined, sign_shift); + + f = _mm_loadu_ps(input+4); + justsign = _mm_and_ps(msign, f); + absf = _mm_xor_ps(f, justsign); + absf_int = _mm_castps_si128(absf); // the cast is "free" (extra bypass latency, but no thruput hit) + b_isnan = _mm_cmpunord_ps(absf, absf); // is this a NaN? + b_isregular = _mm_cmpgt_epi32(f16max, absf_int); // (sub)normalized or special? + nanbit = _mm_and_si128(_mm_castps_si128(b_isnan), c_nanbit); + inf_or_nan = _mm_or_si128(nanbit, STBIR__CONSTI(c_infty_as_fp16)); // output for specials + + b_issub = _mm_cmpgt_epi32(min_normal, absf_int); + + // "result is subnormal" path + subnorm1 = _mm_add_ps(absf, _mm_castsi128_ps(STBIR__CONSTI(c_subnorm_magic))); // magic value to round output mantissa + subnorm2 = _mm_sub_epi32(_mm_castps_si128(subnorm1), STBIR__CONSTI(c_subnorm_magic)); // subtract out bias + + // "result is normal" path + mantoddbit = _mm_slli_epi32(absf_int, 31 - 13); // shift bit 13 (mantissa LSB) to sign + mantodd = _mm_srai_epi32(mantoddbit, 31); // -1 if FP16 mantissa odd, else 0 + + round1 = _mm_add_epi32(absf_int, STBIR__CONSTI(c_normal_bias)); + round2 = _mm_sub_epi32(round1, mantodd); // if mantissa LSB odd, bias towards rounding up (RTNE) + normal = _mm_srli_epi32(round2, 13); // rounded result + + // combine the two non-specials + nonspecial = _mm_or_si128(_mm_and_si128(subnorm2, b_issub), _mm_andnot_si128(b_issub, normal)); + + // merge in specials as well + joined = _mm_or_si128(_mm_and_si128(nonspecial, b_isregular), _mm_andnot_si128(b_isregular, inf_or_nan)); + + sign_shift = _mm_srai_epi32(_mm_castps_si128(justsign), 16); + final2 = _mm_or_si128(joined, sign_shift); + final = _mm_packs_epi32(final, final2); + stbir__simdi_store( output,final ); + } + +#elif defined(STBIR_WASM) || (defined(STBIR_NEON) && defined(_MSC_VER) && defined(_M_ARM)) // WASM or 32-bit ARM on MSVC/clang + + static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input) + { + for (int i=0; i<8; i++) + { + output[i] = stbir__half_to_float(input[i]); + } + } + + static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input) + { + for (int i=0; i<8; i++) + { + output[i] = stbir__float_to_half(input[i]); + } + } + +#elif defined(STBIR_NEON) && defined(_MSC_VER) && defined(_M_ARM64) && !defined(__clang__) // 64-bit ARM on MSVC (not clang) + + static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input) + { + float16x4_t in0 = vld1_f16(input + 0); + float16x4_t in1 = vld1_f16(input + 4); + vst1q_f32(output + 0, vcvt_f32_f16(in0)); + vst1q_f32(output + 4, vcvt_f32_f16(in1)); + } + + static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input) + { + float16x4_t out0 = vcvt_f16_f32(vld1q_f32(input + 0)); + float16x4_t out1 = vcvt_f16_f32(vld1q_f32(input + 4)); + vst1_f16(output+0, out0); + vst1_f16(output+4, out1); + } + + static stbir__inline float stbir__half_to_float( stbir__FP16 h ) + { + return vgetq_lane_f32(vcvt_f32_f16(vld1_dup_f16(&h)), 0); + } + + static stbir__inline stbir__FP16 stbir__float_to_half( float f ) + { + return vget_lane_f16(vcvt_f16_f32(vdupq_n_f32(f)), 0).n16_u16[0]; + } + +#elif defined(STBIR_NEON) // 64-bit ARM + + static stbir__inline void stbir__half_to_float_SIMD(float * output, stbir__FP16 const * input) + { + float16x8_t in = vld1q_f16(input); + vst1q_f32(output + 0, vcvt_f32_f16(vget_low_f16(in))); + vst1q_f32(output + 4, vcvt_f32_f16(vget_high_f16(in))); + } + + static stbir__inline void stbir__float_to_half_SIMD(stbir__FP16 * output, float const * input) + { + float16x4_t out0 = vcvt_f16_f32(vld1q_f32(input + 0)); + float16x4_t out1 = vcvt_f16_f32(vld1q_f32(input + 4)); + vst1q_f16(output, vcombine_f16(out0, out1)); + } + + static stbir__inline float stbir__half_to_float( stbir__FP16 h ) + { + return vgetq_lane_f32(vcvt_f32_f16(vdup_n_f16(h)), 0); + } + + static stbir__inline stbir__FP16 stbir__float_to_half( float f ) + { + return vget_lane_f16(vcvt_f16_f32(vdupq_n_f32(f)), 0); + } + +#endif + + +#ifdef STBIR_SIMD + +#define stbir__simdf_0123to3333( out, reg ) (out) = stbir__simdf_swiz( reg, 3,3,3,3 ) +#define stbir__simdf_0123to2222( out, reg ) (out) = stbir__simdf_swiz( reg, 2,2,2,2 ) +#define stbir__simdf_0123to1111( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,1,1 ) +#define stbir__simdf_0123to0000( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,0,0 ) +#define stbir__simdf_0123to0003( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,0,3 ) +#define stbir__simdf_0123to0001( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,0,1 ) +#define stbir__simdf_0123to1122( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,2,2 ) +#define stbir__simdf_0123to2333( out, reg ) (out) = stbir__simdf_swiz( reg, 2,3,3,3 ) +#define stbir__simdf_0123to0023( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,2,3 ) +#define stbir__simdf_0123to1230( out, reg ) (out) = stbir__simdf_swiz( reg, 1,2,3,0 ) +#define stbir__simdf_0123to2103( out, reg ) (out) = stbir__simdf_swiz( reg, 2,1,0,3 ) +#define stbir__simdf_0123to3210( out, reg ) (out) = stbir__simdf_swiz( reg, 3,2,1,0 ) +#define stbir__simdf_0123to2301( out, reg ) (out) = stbir__simdf_swiz( reg, 2,3,0,1 ) +#define stbir__simdf_0123to3012( out, reg ) (out) = stbir__simdf_swiz( reg, 3,0,1,2 ) +#define stbir__simdf_0123to0011( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,1,1 ) +#define stbir__simdf_0123to1100( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,0,0 ) +#define stbir__simdf_0123to2233( out, reg ) (out) = stbir__simdf_swiz( reg, 2,2,3,3 ) +#define stbir__simdf_0123to1133( out, reg ) (out) = stbir__simdf_swiz( reg, 1,1,3,3 ) +#define stbir__simdf_0123to0022( out, reg ) (out) = stbir__simdf_swiz( reg, 0,0,2,2 ) +#define stbir__simdf_0123to1032( out, reg ) (out) = stbir__simdf_swiz( reg, 1,0,3,2 ) + +typedef union stbir__simdi_u32 +{ + stbir_uint32 m128i_u32[4]; + int m128i_i32[4]; + stbir__simdi m128i_i128; +} stbir__simdi_u32; + +static const int STBIR_mask[9] = { 0,0,0,-1,-1,-1,0,0,0 }; + +static const STBIR__SIMDF_CONST(STBIR_max_uint8_as_float, stbir__max_uint8_as_float); +static const STBIR__SIMDF_CONST(STBIR_max_uint16_as_float, stbir__max_uint16_as_float); +static const STBIR__SIMDF_CONST(STBIR_max_uint8_as_float_inverted, stbir__max_uint8_as_float_inverted); +static const STBIR__SIMDF_CONST(STBIR_max_uint16_as_float_inverted, stbir__max_uint16_as_float_inverted); + +static const STBIR__SIMDF_CONST(STBIR_simd_point5, 0.5f); +static const STBIR__SIMDF_CONST(STBIR_ones, 1.0f); +static const STBIR__SIMDI_CONST(STBIR_almost_zero, (127 - 13) << 23); +static const STBIR__SIMDI_CONST(STBIR_almost_one, 0x3f7fffff); +static const STBIR__SIMDI_CONST(STBIR_mastissa_mask, 0xff); +static const STBIR__SIMDI_CONST(STBIR_topscale, 0x02000000); + +// Basically, in simd mode, we unroll the proper amount, and we don't want +// the non-simd remnant loops to be unroll because they only run a few times +// Adding this switch saves about 5K on clang which is Captain Unroll the 3rd. +#define STBIR_SIMD_STREAMOUT_PTR( star ) STBIR_STREAMOUT_PTR( star ) +#define STBIR_SIMD_NO_UNROLL(ptr) STBIR_NO_UNROLL(ptr) + +#ifdef STBIR_MEMCPY +#undef STBIR_MEMCPY +#define STBIR_MEMCPY stbir_simd_memcpy +#endif + +// override normal use of memcpy with much simpler copy (faster and smaller with our sized copies) +static void stbir_simd_memcpy( void * dest, void const * src, size_t bytes ) +{ + char STBIR_SIMD_STREAMOUT_PTR (*) d = (char*) dest; + char STBIR_SIMD_STREAMOUT_PTR( * ) d_end = ((char*) dest) + bytes; + ptrdiff_t ofs_to_src = (char*)src - (char*)dest; + + // check overlaps + STBIR_ASSERT( ( ( d >= ( (char*)src) + bytes ) ) || ( ( d + bytes ) <= (char*)src ) ); + + if ( bytes < (16*stbir__simdfX_float_count) ) + { + if ( bytes < 16 ) + { + if ( bytes ) + { + do + { + STBIR_SIMD_NO_UNROLL(d); + d[ 0 ] = d[ ofs_to_src ]; + ++d; + } while ( d < d_end ); + } + } + else + { + stbir__simdf x; + // do one unaligned to get us aligned for the stream out below + stbir__simdf_load( x, ( d + ofs_to_src ) ); + stbir__simdf_store( d, x ); + d = (char*)( ( ( (ptrdiff_t)d ) + 16 ) & ~15 ); + + for(;;) + { + STBIR_SIMD_NO_UNROLL(d); + + if ( d > ( d_end - 16 ) ) + { + if ( d == d_end ) + return; + d = d_end - 16; + } + + stbir__simdf_load( x, ( d + ofs_to_src ) ); + stbir__simdf_store( d, x ); + d += 16; + } + } + } + else + { + stbir__simdfX x0,x1,x2,x3; + + // do one unaligned to get us aligned for the stream out below + stbir__simdfX_load( x0, ( d + ofs_to_src ) + 0*stbir__simdfX_float_count ); + stbir__simdfX_load( x1, ( d + ofs_to_src ) + 4*stbir__simdfX_float_count ); + stbir__simdfX_load( x2, ( d + ofs_to_src ) + 8*stbir__simdfX_float_count ); + stbir__simdfX_load( x3, ( d + ofs_to_src ) + 12*stbir__simdfX_float_count ); + stbir__simdfX_store( d + 0*stbir__simdfX_float_count, x0 ); + stbir__simdfX_store( d + 4*stbir__simdfX_float_count, x1 ); + stbir__simdfX_store( d + 8*stbir__simdfX_float_count, x2 ); + stbir__simdfX_store( d + 12*stbir__simdfX_float_count, x3 ); + d = (char*)( ( ( (ptrdiff_t)d ) + (16*stbir__simdfX_float_count) ) & ~((16*stbir__simdfX_float_count)-1) ); + + for(;;) + { + STBIR_SIMD_NO_UNROLL(d); + + if ( d > ( d_end - (16*stbir__simdfX_float_count) ) ) + { + if ( d == d_end ) + return; + d = d_end - (16*stbir__simdfX_float_count); + } + + stbir__simdfX_load( x0, ( d + ofs_to_src ) + 0*stbir__simdfX_float_count ); + stbir__simdfX_load( x1, ( d + ofs_to_src ) + 4*stbir__simdfX_float_count ); + stbir__simdfX_load( x2, ( d + ofs_to_src ) + 8*stbir__simdfX_float_count ); + stbir__simdfX_load( x3, ( d + ofs_to_src ) + 12*stbir__simdfX_float_count ); + stbir__simdfX_store( d + 0*stbir__simdfX_float_count, x0 ); + stbir__simdfX_store( d + 4*stbir__simdfX_float_count, x1 ); + stbir__simdfX_store( d + 8*stbir__simdfX_float_count, x2 ); + stbir__simdfX_store( d + 12*stbir__simdfX_float_count, x3 ); + d += (16*stbir__simdfX_float_count); + } + } +} + +// memcpy that is specically intentionally overlapping (src is smaller then dest, so can be +// a normal forward copy, bytes is divisible by 4 and bytes is greater than or equal to +// the diff between dest and src) +static void stbir_overlapping_memcpy( void * dest, void const * src, size_t bytes ) +{ + char STBIR_SIMD_STREAMOUT_PTR (*) sd = (char*) src; + char STBIR_SIMD_STREAMOUT_PTR( * ) s_end = ((char*) src) + bytes; + ptrdiff_t ofs_to_dest = (char*)dest - (char*)src; + + if ( ofs_to_dest >= 16 ) // is the overlap more than 16 away? + { + char STBIR_SIMD_STREAMOUT_PTR( * ) s_end16 = ((char*) src) + (bytes&~15); + do + { + stbir__simdf x; + STBIR_SIMD_NO_UNROLL(sd); + stbir__simdf_load( x, sd ); + stbir__simdf_store( ( sd + ofs_to_dest ), x ); + sd += 16; + } while ( sd < s_end16 ); + + if ( sd == s_end ) + return; + } + + do + { + STBIR_SIMD_NO_UNROLL(sd); + *(int*)( sd + ofs_to_dest ) = *(int*) sd; + sd += 4; + } while ( sd < s_end ); +} + +#else // no SSE2 + +// when in scalar mode, we let unrolling happen, so this macro just does the __restrict +#define STBIR_SIMD_STREAMOUT_PTR( star ) STBIR_STREAMOUT_PTR( star ) +#define STBIR_SIMD_NO_UNROLL(ptr) + +#endif // SSE2 + + +#ifdef STBIR_PROFILE + +#if defined(_x86_64) || defined( __x86_64__ ) || defined( _M_X64 ) || defined(__x86_64) || defined(__SSE2__) || defined(STBIR_SSE) || defined( _M_IX86_FP ) || defined(__i386) || defined( __i386__ ) || defined( _M_IX86 ) || defined( _X86_ ) + +#ifdef _MSC_VER + + STBIRDEF stbir_uint64 __rdtsc(); + #define STBIR_PROFILE_FUNC() __rdtsc() + +#else // non msvc + + static stbir__inline stbir_uint64 STBIR_PROFILE_FUNC() + { + stbir_uint32 lo, hi; + asm volatile ("rdtsc" : "=a" (lo), "=d" (hi) ); + return ( ( (stbir_uint64) hi ) << 32 ) | ( (stbir_uint64) lo ); + } + +#endif // msvc + +#elif defined( _M_ARM64 ) || defined( __aarch64__ ) || defined( __arm64__ ) || defined(__ARM_NEON__) + +#if defined( _MSC_VER ) && !defined(__clang__) + + #define STBIR_PROFILE_FUNC() _ReadStatusReg(ARM64_CNTVCT) + +#else + + static stbir__inline stbir_uint64 STBIR_PROFILE_FUNC() + { + stbir_uint64 tsc; + asm volatile("mrs %0, cntvct_el0" : "=r" (tsc)); + return tsc; + } + +#endif + +#else // x64, arm + +#error Unknown platform for profiling. + +#endif //x64 and + + +#define STBIR_ONLY_PROFILE_GET_SPLIT_INFO ,stbir__per_split_info * split_info +#define STBIR_ONLY_PROFILE_SET_SPLIT_INFO ,split_info + +#define STBIR_ONLY_PROFILE_BUILD_GET_INFO ,stbir__info * profile_info +#define STBIR_ONLY_PROFILE_BUILD_SET_INFO ,profile_info + +// super light-weight micro profiler +#define STBIR_PROFILE_START_ll( info, wh ) { stbir_uint64 wh##thiszonetime = STBIR_PROFILE_FUNC(); stbir_uint64 * wh##save_parent_excluded_ptr = info->current_zone_excluded_ptr; stbir_uint64 wh##current_zone_excluded = 0; info->current_zone_excluded_ptr = &wh##current_zone_excluded; +#define STBIR_PROFILE_END_ll( info, wh ) wh##thiszonetime = STBIR_PROFILE_FUNC() - wh##thiszonetime; info->profile.named.wh += wh##thiszonetime - wh##current_zone_excluded; *wh##save_parent_excluded_ptr += wh##thiszonetime; info->current_zone_excluded_ptr = wh##save_parent_excluded_ptr; } +#define STBIR_PROFILE_FIRST_START_ll( info, wh ) { int i; info->current_zone_excluded_ptr = &info->profile.named.total; for(i=0;iprofile.array);i++) info->profile.array[i]=0; } STBIR_PROFILE_START_ll( info, wh ); +#define STBIR_PROFILE_CLEAR_EXTRAS_ll( info, num ) { int extra; for(extra=1;extra<(num);extra++) { int i; for(i=0;iprofile.array);i++) (info)[extra].profile.array[i]=0; } } + +// for thread data +#define STBIR_PROFILE_START( wh ) STBIR_PROFILE_START_ll( split_info, wh ) +#define STBIR_PROFILE_END( wh ) STBIR_PROFILE_END_ll( split_info, wh ) +#define STBIR_PROFILE_FIRST_START( wh ) STBIR_PROFILE_FIRST_START_ll( split_info, wh ) +#define STBIR_PROFILE_CLEAR_EXTRAS() STBIR_PROFILE_CLEAR_EXTRAS_ll( split_info, split_count ) + +// for build data +#define STBIR_PROFILE_BUILD_START( wh ) STBIR_PROFILE_START_ll( profile_info, wh ) +#define STBIR_PROFILE_BUILD_END( wh ) STBIR_PROFILE_END_ll( profile_info, wh ) +#define STBIR_PROFILE_BUILD_FIRST_START( wh ) STBIR_PROFILE_FIRST_START_ll( profile_info, wh ) +#define STBIR_PROFILE_BUILD_CLEAR( info ) { int i; for(i=0;iprofile.array);i++) info->profile.array[i]=0; } + +#else // no profile + +#define STBIR_ONLY_PROFILE_GET_SPLIT_INFO +#define STBIR_ONLY_PROFILE_SET_SPLIT_INFO + +#define STBIR_ONLY_PROFILE_BUILD_GET_INFO +#define STBIR_ONLY_PROFILE_BUILD_SET_INFO + +#define STBIR_PROFILE_START( wh ) +#define STBIR_PROFILE_END( wh ) +#define STBIR_PROFILE_FIRST_START( wh ) +#define STBIR_PROFILE_CLEAR_EXTRAS( ) + +#define STBIR_PROFILE_BUILD_START( wh ) +#define STBIR_PROFILE_BUILD_END( wh ) +#define STBIR_PROFILE_BUILD_FIRST_START( wh ) +#define STBIR_PROFILE_BUILD_CLEAR( info ) + +#endif // stbir_profile + +#ifndef STBIR_CEILF +#include +#if _MSC_VER <= 1200 // support VC6 for Sean +#define STBIR_CEILF(x) ((float)ceil((float)(x))) +#define STBIR_FLOORF(x) ((float)floor((float)(x))) +#else +#define STBIR_CEILF(x) ceilf(x) +#define STBIR_FLOORF(x) floorf(x) +#endif +#endif + +#ifndef STBIR_MEMCPY +// For memcpy +#include +#define STBIR_MEMCPY( dest, src, len ) memcpy( dest, src, len ) +#endif + +#ifndef STBIR_SIMD + +// memcpy that is specically intentionally overlapping (src is smaller then dest, so can be +// a normal forward copy, bytes is divisible by 4 and bytes is greater than or equal to +// the diff between dest and src) +static void stbir_overlapping_memcpy( void * dest, void const * src, size_t bytes ) +{ + char STBIR_SIMD_STREAMOUT_PTR (*) sd = (char*) src; + char STBIR_SIMD_STREAMOUT_PTR( * ) s_end = ((char*) src) + bytes; + ptrdiff_t ofs_to_dest = (char*)dest - (char*)src; + + if ( ofs_to_dest >= 8 ) // is the overlap more than 8 away? + { + char STBIR_SIMD_STREAMOUT_PTR( * ) s_end8 = ((char*) src) + (bytes&~7); + do + { + STBIR_NO_UNROLL(sd); + *(stbir_uint64*)( sd + ofs_to_dest ) = *(stbir_uint64*) sd; + sd += 8; + } while ( sd < s_end8 ); + + if ( sd == s_end ) + return; + } + + do + { + STBIR_NO_UNROLL(sd); + *(int*)( sd + ofs_to_dest ) = *(int*) sd; + sd += 4; + } while ( sd < s_end ); +} + +#endif + +static float stbir__filter_trapezoid(float x, float scale, void * user_data) +{ + float halfscale = scale / 2; + float t = 0.5f + halfscale; + STBIR_ASSERT(scale <= 1); + STBIR__UNUSED(user_data); + + if ( x < 0.0f ) x = -x; + + if (x >= t) + return 0.0f; + else + { + float r = 0.5f - halfscale; + if (x <= r) + return 1.0f; + else + return (t - x) / scale; + } +} + +static float stbir__support_trapezoid(float scale, void * user_data) +{ + STBIR__UNUSED(user_data); + return 0.5f + scale / 2.0f; +} + +static float stbir__filter_triangle(float x, float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + + if ( x < 0.0f ) x = -x; + + if (x <= 1.0f) + return 1.0f - x; + else + return 0.0f; +} + +static float stbir__filter_point(float x, float s, void * user_data) +{ + STBIR__UNUSED(x); + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + + return 1.0f; +} + +static float stbir__filter_cubic(float x, float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + + if ( x < 0.0f ) x = -x; + + if (x < 1.0f) + return (4.0f + x*x*(3.0f*x - 6.0f))/6.0f; + else if (x < 2.0f) + return (8.0f + x*(-12.0f + x*(6.0f - x)))/6.0f; + + return (0.0f); +} + +static float stbir__filter_catmullrom(float x, float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + + if ( x < 0.0f ) x = -x; + + if (x < 1.0f) + return 1.0f - x*x*(2.5f - 1.5f*x); + else if (x < 2.0f) + return 2.0f - x*(4.0f + x*(0.5f*x - 2.5f)); + + return (0.0f); +} + +static float stbir__filter_mitchell(float x, float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + + if ( x < 0.0f ) x = -x; + + if (x < 1.0f) + return (16.0f + x*x*(21.0f * x - 36.0f))/18.0f; + else if (x < 2.0f) + return (32.0f + x*(-60.0f + x*(36.0f - 7.0f*x)))/18.0f; + + return (0.0f); +} + +static float stbir__support_zero(float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + return 0; +} + +static float stbir__support_zeropoint5(float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + return 0.5f; +} + +static float stbir__support_one(float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + return 1; +} + +static float stbir__support_two(float s, void * user_data) +{ + STBIR__UNUSED(s); + STBIR__UNUSED(user_data); + return 2; +} + +// This is the maximum number of input samples that can affect an output sample +// with the given filter from the output pixel's perspective +static int stbir__get_filter_pixel_width(stbir__support_callback * support, float scale, void * user_data) +{ + STBIR_ASSERT(support != 0); + + if ( scale >= ( 1.0f-stbir__small_float ) ) // upscale + return (int)STBIR_CEILF(support(1.0f/scale,user_data) * 2.0f); + else + return (int)STBIR_CEILF(support(scale,user_data) * 2.0f / scale); +} + +// this is how many coefficents per run of the filter (which is different +// from the filter_pixel_width depending on if we are scattering or gathering) +static int stbir__get_coefficient_width(stbir__sampler * samp, int is_gather, void * user_data) +{ + float scale = samp->scale_info.scale; + stbir__support_callback * support = samp->filter_support; + + switch( is_gather ) + { + case 1: + return (int)STBIR_CEILF(support(1.0f / scale, user_data) * 2.0f); + case 2: + return (int)STBIR_CEILF(support(scale, user_data) * 2.0f / scale); + case 0: + return (int)STBIR_CEILF(support(scale, user_data) * 2.0f); + default: + STBIR_ASSERT( (is_gather >= 0 ) && (is_gather <= 2 ) ); + return 0; + } +} + +static int stbir__get_contributors(stbir__sampler * samp, int is_gather) +{ + if (is_gather) + return samp->scale_info.output_sub_size; + else + return (samp->scale_info.input_full_size + samp->filter_pixel_margin * 2); +} + +static int stbir__edge_zero_full( int n, int max ) +{ + STBIR__UNUSED(n); + STBIR__UNUSED(max); + return 0; // NOTREACHED +} + +static int stbir__edge_clamp_full( int n, int max ) +{ + if (n < 0) + return 0; + + if (n >= max) + return max - 1; + + return n; // NOTREACHED +} + +static int stbir__edge_reflect_full( int n, int max ) +{ + if (n < 0) + { + if (n > -max) + return -n; + else + return max - 1; + } + + if (n >= max) + { + int max2 = max * 2; + if (n >= max2) + return 0; + else + return max2 - n - 1; + } + + return n; // NOTREACHED +} + +static int stbir__edge_wrap_full( int n, int max ) +{ + if (n >= 0) + return (n % max); + else + { + int m = (-n) % max; + + if (m != 0) + m = max - m; + + return (m); + } +} + +typedef int stbir__edge_wrap_func( int n, int max ); +static stbir__edge_wrap_func * stbir__edge_wrap_slow[] = +{ + stbir__edge_clamp_full, // STBIR_EDGE_CLAMP + stbir__edge_reflect_full, // STBIR_EDGE_REFLECT + stbir__edge_wrap_full, // STBIR_EDGE_WRAP + stbir__edge_zero_full, // STBIR_EDGE_ZERO +}; + +stbir__inline static int stbir__edge_wrap(stbir_edge edge, int n, int max) +{ + // avoid per-pixel switch + if (n >= 0 && n < max) + return n; + return stbir__edge_wrap_slow[edge]( n, max ); +} + +#define STBIR__MERGE_RUNS_PIXEL_THRESHOLD 16 + +// get information on the extents of a sampler +static void stbir__get_extents( stbir__sampler * samp, stbir__extents * scanline_extents ) +{ + int j, stop; + int left_margin, right_margin; + int min_n = 0x7fffffff, max_n = -0x7fffffff; + int min_left = 0x7fffffff, max_left = -0x7fffffff; + int min_right = 0x7fffffff, max_right = -0x7fffffff; + stbir_edge edge = samp->edge; + stbir__contributors* contributors = samp->contributors; + int output_sub_size = samp->scale_info.output_sub_size; + int input_full_size = samp->scale_info.input_full_size; + int filter_pixel_margin = samp->filter_pixel_margin; + + STBIR_ASSERT( samp->is_gather ); + + stop = output_sub_size; + for (j = 0; j < stop; j++ ) + { + STBIR_ASSERT( contributors[j].n1 >= contributors[j].n0 ); + if ( contributors[j].n0 < min_n ) + { + min_n = contributors[j].n0; + stop = j + filter_pixel_margin; // if we find a new min, only scan another filter width + if ( stop > output_sub_size ) stop = output_sub_size; + } + } + + stop = 0; + for (j = output_sub_size - 1; j >= stop; j-- ) + { + STBIR_ASSERT( contributors[j].n1 >= contributors[j].n0 ); + if ( contributors[j].n1 > max_n ) + { + max_n = contributors[j].n1; + stop = j - filter_pixel_margin; // if we find a new max, only scan another filter width + if (stop<0) stop = 0; + } + } + + STBIR_ASSERT( scanline_extents->conservative.n0 <= min_n ); + STBIR_ASSERT( scanline_extents->conservative.n1 >= max_n ); + + // now calculate how much into the margins we really read + left_margin = 0; + if ( min_n < 0 ) + { + left_margin = -min_n; + min_n = 0; + } + + right_margin = 0; + if ( max_n >= input_full_size ) + { + right_margin = max_n - input_full_size + 1; + max_n = input_full_size - 1; + } + + // index 1 is margin pixel extents (how many pixels we hang over the edge) + scanline_extents->edge_sizes[0] = left_margin; + scanline_extents->edge_sizes[1] = right_margin; + + // index 2 is pixels read from the input + scanline_extents->spans[0].n0 = min_n; + scanline_extents->spans[0].n1 = max_n; + scanline_extents->spans[0].pixel_offset_for_input = min_n; + + // default to no other input range + scanline_extents->spans[1].n0 = 0; + scanline_extents->spans[1].n1 = -1; + scanline_extents->spans[1].pixel_offset_for_input = 0; + + // don't have to do edge calc for zero clamp + if ( edge == STBIR_EDGE_ZERO ) + return; + + // convert margin pixels to the pixels within the input (min and max) + for( j = -left_margin ; j < 0 ; j++ ) + { + int p = stbir__edge_wrap( edge, j, input_full_size ); + if ( p < min_left ) + min_left = p; + if ( p > max_left ) + max_left = p; + } + + for( j = input_full_size ; j < (input_full_size + right_margin) ; j++ ) + { + int p = stbir__edge_wrap( edge, j, input_full_size ); + if ( p < min_right ) + min_right = p; + if ( p > max_right ) + max_right = p; + } + + // merge the left margin pixel region if it connects within 4 pixels of main pixel region + if ( min_left != 0x7fffffff ) + { + if ( ( ( min_left <= min_n ) && ( ( max_left + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= min_n ) ) || + ( ( min_n <= min_left ) && ( ( max_n + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= max_left ) ) ) + { + scanline_extents->spans[0].n0 = min_n = stbir__min( min_n, min_left ); + scanline_extents->spans[0].n1 = max_n = stbir__max( max_n, max_left ); + scanline_extents->spans[0].pixel_offset_for_input = min_n; + left_margin = 0; + } + } + + // merge the right margin pixel region if it connects within 4 pixels of main pixel region + if ( min_right != 0x7fffffff ) + { + if ( ( ( min_right <= min_n ) && ( ( max_right + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= min_n ) ) || + ( ( min_n <= min_right ) && ( ( max_n + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= max_right ) ) ) + { + scanline_extents->spans[0].n0 = min_n = stbir__min( min_n, min_right ); + scanline_extents->spans[0].n1 = max_n = stbir__max( max_n, max_right ); + scanline_extents->spans[0].pixel_offset_for_input = min_n; + right_margin = 0; + } + } + + STBIR_ASSERT( scanline_extents->conservative.n0 <= min_n ); + STBIR_ASSERT( scanline_extents->conservative.n1 >= max_n ); + + // you get two ranges when you have the WRAP edge mode and you are doing just the a piece of the resize + // so you need to get a second run of pixels from the opposite side of the scanline (which you + // wouldn't need except for WRAP) + + + // if we can't merge the min_left range, add it as a second range + if ( ( left_margin ) && ( min_left != 0x7fffffff ) ) + { + stbir__span * newspan = scanline_extents->spans + 1; + STBIR_ASSERT( right_margin == 0 ); + if ( min_left < scanline_extents->spans[0].n0 ) + { + scanline_extents->spans[1].pixel_offset_for_input = scanline_extents->spans[0].n0; + scanline_extents->spans[1].n0 = scanline_extents->spans[0].n0; + scanline_extents->spans[1].n1 = scanline_extents->spans[0].n1; + --newspan; + } + newspan->pixel_offset_for_input = min_left; + newspan->n0 = -left_margin; + newspan->n1 = ( max_left - min_left ) - left_margin; + scanline_extents->edge_sizes[0] = 0; // don't need to copy the left margin, since we are directly decoding into the margin + return; + } + + // if we can't merge the min_left range, add it as a second range + if ( ( right_margin ) && ( min_right != 0x7fffffff ) ) + { + stbir__span * newspan = scanline_extents->spans + 1; + if ( min_right < scanline_extents->spans[0].n0 ) + { + scanline_extents->spans[1].pixel_offset_for_input = scanline_extents->spans[0].n0; + scanline_extents->spans[1].n0 = scanline_extents->spans[0].n0; + scanline_extents->spans[1].n1 = scanline_extents->spans[0].n1; + --newspan; + } + newspan->pixel_offset_for_input = min_right; + newspan->n0 = scanline_extents->spans[1].n1 + 1; + newspan->n1 = scanline_extents->spans[1].n1 + 1 + ( max_right - min_right ); + scanline_extents->edge_sizes[1] = 0; // don't need to copy the right margin, since we are directly decoding into the margin + return; + } +} + +static void stbir__calculate_in_pixel_range( int * first_pixel, int * last_pixel, float out_pixel_center, float out_filter_radius, float inv_scale, float out_shift, int input_size, stbir_edge edge ) +{ + int first, last; + float out_pixel_influence_lowerbound = out_pixel_center - out_filter_radius; + float out_pixel_influence_upperbound = out_pixel_center + out_filter_radius; + + float in_pixel_influence_lowerbound = (out_pixel_influence_lowerbound + out_shift) * inv_scale; + float in_pixel_influence_upperbound = (out_pixel_influence_upperbound + out_shift) * inv_scale; + + first = (int)(STBIR_FLOORF(in_pixel_influence_lowerbound + 0.5f)); + last = (int)(STBIR_FLOORF(in_pixel_influence_upperbound - 0.5f)); + + if ( edge == STBIR_EDGE_WRAP ) + { + if ( first <= -input_size ) + first = -(input_size-1); + if ( last >= (input_size*2)) + last = (input_size*2) - 1; + } + + *first_pixel = first; + *last_pixel = last; +} + +static void stbir__calculate_coefficients_for_gather_upsample( float out_filter_radius, stbir__kernel_callback * kernel, stbir__scale_info * scale_info, int num_contributors, stbir__contributors* contributors, float* coefficient_group, int coefficient_width, stbir_edge edge, void * user_data ) +{ + int n, end; + float inv_scale = scale_info->inv_scale; + float out_shift = scale_info->pixel_shift; + int input_size = scale_info->input_full_size; + int numerator = scale_info->scale_numerator; + int polyphase = ( ( scale_info->scale_is_rational ) && ( numerator < num_contributors ) ); + + // Looping through out pixels + end = num_contributors; if ( polyphase ) end = numerator; + for (n = 0; n < end; n++) + { + int i; + int last_non_zero; + float out_pixel_center = (float)n + 0.5f; + float in_center_of_out = (out_pixel_center + out_shift) * inv_scale; + + int in_first_pixel, in_last_pixel; + + stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, out_pixel_center, out_filter_radius, inv_scale, out_shift, input_size, edge ); + + last_non_zero = -1; + for (i = 0; i <= in_last_pixel - in_first_pixel; i++) + { + float in_pixel_center = (float)(i + in_first_pixel) + 0.5f; + float coeff = kernel(in_center_of_out - in_pixel_center, inv_scale, user_data); + + // kill denormals + if ( ( ( coeff < stbir__small_float ) && ( coeff > -stbir__small_float ) ) ) + { + if ( i == 0 ) // if we're at the front, just eat zero contributors + { + STBIR_ASSERT ( ( in_last_pixel - in_first_pixel ) != 0 ); // there should be at least one contrib + ++in_first_pixel; + i--; + continue; + } + coeff = 0; // make sure is fully zero (should keep denormals away) + } + else + last_non_zero = i; + + coefficient_group[i] = coeff; + } + + in_last_pixel = last_non_zero+in_first_pixel; // kills trailing zeros + contributors->n0 = in_first_pixel; + contributors->n1 = in_last_pixel; + + STBIR_ASSERT(contributors->n1 >= contributors->n0); + + ++contributors; + coefficient_group += coefficient_width; + } +} + +static void stbir__insert_coeff( stbir__contributors * contribs, float * coeffs, int new_pixel, float new_coeff ) +{ + if ( new_pixel <= contribs->n1 ) // before the end + { + if ( new_pixel < contribs->n0 ) // before the front? + { + int j, o = contribs->n0 - new_pixel; + for ( j = contribs->n1 - contribs->n0 ; j <= 0 ; j-- ) + coeffs[ j + o ] = coeffs[ j ]; + for ( j = 1 ; j < o ; j-- ) + coeffs[ j ] = coeffs[ 0 ]; + coeffs[ 0 ] = new_coeff; + contribs->n0 = new_pixel; + } + else + { + coeffs[ new_pixel - contribs->n0 ] += new_coeff; + } + } + else + { + int j, e = new_pixel - contribs->n0; + for( j = ( contribs->n1 - contribs->n0 ) + 1 ; j < e ; j++ ) // clear in-betweens coeffs if there are any + coeffs[j] = 0; + + coeffs[ e ] = new_coeff; + contribs->n1 = new_pixel; + } +} + +static void stbir__calculate_out_pixel_range( int * first_pixel, int * last_pixel, float in_pixel_center, float in_pixels_radius, float scale, float out_shift, int out_size ) +{ + float in_pixel_influence_lowerbound = in_pixel_center - in_pixels_radius; + float in_pixel_influence_upperbound = in_pixel_center + in_pixels_radius; + float out_pixel_influence_lowerbound = in_pixel_influence_lowerbound * scale - out_shift; + float out_pixel_influence_upperbound = in_pixel_influence_upperbound * scale - out_shift; + int out_first_pixel = (int)(STBIR_FLOORF(out_pixel_influence_lowerbound + 0.5f)); + int out_last_pixel = (int)(STBIR_FLOORF(out_pixel_influence_upperbound - 0.5f)); + + if ( out_first_pixel < 0 ) + out_first_pixel = 0; + if ( out_last_pixel >= out_size ) + out_last_pixel = out_size - 1; + *first_pixel = out_first_pixel; + *last_pixel = out_last_pixel; +} + +static void stbir__calculate_coefficients_for_gather_downsample( int start, int end, float in_pixels_radius, stbir__kernel_callback * kernel, stbir__scale_info * scale_info, int coefficient_width, int num_contributors, stbir__contributors * contributors, float * coefficient_group, void * user_data ) +{ + int in_pixel; + int i; + int first_out_inited = -1; + float scale = scale_info->scale; + float out_shift = scale_info->pixel_shift; + int out_size = scale_info->output_sub_size; + int numerator = scale_info->scale_numerator; + int polyphase = ( ( scale_info->scale_is_rational ) && ( numerator < out_size ) ); + + STBIR__UNUSED(num_contributors); + + // Loop through the input pixels + for (in_pixel = start; in_pixel < end; in_pixel++) + { + float in_pixel_center = (float)in_pixel + 0.5f; + float out_center_of_in = in_pixel_center * scale - out_shift; + int out_first_pixel, out_last_pixel; + + stbir__calculate_out_pixel_range( &out_first_pixel, &out_last_pixel, in_pixel_center, in_pixels_radius, scale, out_shift, out_size ); + + if ( out_first_pixel > out_last_pixel ) + continue; + + // clamp or exit if we are using polyphase filtering, and the limit is up + if ( polyphase ) + { + // when polyphase, you only have to do coeffs up to the numerator count + if ( out_first_pixel == numerator ) + break; + + // don't do any extra work, clamp last pixel at numerator too + if ( out_last_pixel >= numerator ) + out_last_pixel = numerator - 1; + } + + for (i = 0; i <= out_last_pixel - out_first_pixel; i++) + { + float out_pixel_center = (float)(i + out_first_pixel) + 0.5f; + float x = out_pixel_center - out_center_of_in; + float coeff = kernel(x, scale, user_data) * scale; + + // kill the coeff if it's too small (avoid denormals) + if ( ( ( coeff < stbir__small_float ) && ( coeff > -stbir__small_float ) ) ) + coeff = 0.0f; + + { + int out = i + out_first_pixel; + float * coeffs = coefficient_group + out * coefficient_width; + stbir__contributors * contribs = contributors + out; + + // is this the first time this output pixel has been seen? Init it. + if ( out > first_out_inited ) + { + STBIR_ASSERT( out == ( first_out_inited + 1 ) ); // ensure we have only advanced one at time + first_out_inited = out; + contribs->n0 = in_pixel; + contribs->n1 = in_pixel; + coeffs[0] = coeff; + } + else + { + // insert on end (always in order) + if ( coeffs[0] == 0.0f ) // if the first coefficent is zero, then zap it for this coeffs + { + STBIR_ASSERT( ( in_pixel - contribs->n0 ) == 1 ); // ensure that when we zap, we're at the 2nd pos + contribs->n0 = in_pixel; + } + contribs->n1 = in_pixel; + STBIR_ASSERT( ( in_pixel - contribs->n0 ) < coefficient_width ); + coeffs[in_pixel - contribs->n0] = coeff; + } + } + } + } +} + +static void stbir__cleanup_gathered_coefficients( stbir_edge edge, stbir__filter_extent_info* filter_info, stbir__scale_info * scale_info, int num_contributors, stbir__contributors* contributors, float * coefficient_group, int coefficient_width ) +{ + int input_size = scale_info->input_full_size; + int input_last_n1 = input_size - 1; + int n, end; + int lowest = 0x7fffffff; + int highest = -0x7fffffff; + int widest = -1; + int numerator = scale_info->scale_numerator; + int denominator = scale_info->scale_denominator; + int polyphase = ( ( scale_info->scale_is_rational ) && ( numerator < num_contributors ) ); + float * coeffs; + stbir__contributors * contribs; + + // weight all the coeffs for each sample + coeffs = coefficient_group; + contribs = contributors; + end = num_contributors; if ( polyphase ) end = numerator; + for (n = 0; n < end; n++) + { + int i; + float filter_scale, total_filter = 0; + int e; + + // add all contribs + e = contribs->n1 - contribs->n0; + for( i = 0 ; i <= e ; i++ ) + { + total_filter += coeffs[i]; + STBIR_ASSERT( ( coeffs[i] >= -2.0f ) && ( coeffs[i] <= 2.0f ) ); // check for wonky weights + } + + // rescale + if ( ( total_filter < stbir__small_float ) && ( total_filter > -stbir__small_float ) ) + { + // all coeffs are extremely small, just zero it + contribs->n1 = contribs->n0; + coeffs[0] = 0.0f; + } + else + { + // if the total isn't 1.0, rescale everything + if ( ( total_filter < (1.0f-stbir__small_float) ) || ( total_filter > (1.0f+stbir__small_float) ) ) + { + filter_scale = 1.0f / total_filter; + // scale them all + for (i = 0; i <= e; i++) + coeffs[i] *= filter_scale; + } + } + ++contribs; + coeffs += coefficient_width; + } + + // if we have a rational for the scale, we can exploit the polyphaseness to not calculate + // most of the coefficients, so we copy them here + if ( polyphase ) + { + stbir__contributors * prev_contribs = contributors; + stbir__contributors * cur_contribs = contributors + numerator; + + for( n = numerator ; n < num_contributors ; n++ ) + { + cur_contribs->n0 = prev_contribs->n0 + denominator; + cur_contribs->n1 = prev_contribs->n1 + denominator; + ++cur_contribs; + ++prev_contribs; + } + stbir_overlapping_memcpy( coefficient_group + numerator * coefficient_width, coefficient_group, ( num_contributors - numerator ) * coefficient_width * sizeof( coeffs[ 0 ] ) ); + } + + coeffs = coefficient_group; + contribs = contributors; + for (n = 0; n < num_contributors; n++) + { + int i; + + // in zero edge mode, just remove out of bounds contribs completely (since their weights are accounted for now) + if ( edge == STBIR_EDGE_ZERO ) + { + // shrink the right side if necessary + if ( contribs->n1 > input_last_n1 ) + contribs->n1 = input_last_n1; + + // shrink the left side + if ( contribs->n0 < 0 ) + { + int j, left, skips = 0; + + skips = -contribs->n0; + contribs->n0 = 0; + + // now move down the weights + left = contribs->n1 - contribs->n0 + 1; + if ( left > 0 ) + { + for( j = 0 ; j < left ; j++ ) + coeffs[ j ] = coeffs[ j + skips ]; + } + } + } + else if ( ( edge == STBIR_EDGE_CLAMP ) || ( edge == STBIR_EDGE_REFLECT ) ) + { + // for clamp and reflect, calculate the true inbounds position (based on edge type) and just add that to the existing weight + + // right hand side first + if ( contribs->n1 > input_last_n1 ) + { + int start = contribs->n0; + int endi = contribs->n1; + contribs->n1 = input_last_n1; + for( i = input_size; i <= endi; i++ ) + stbir__insert_coeff( contribs, coeffs, stbir__edge_wrap_slow[edge]( i, input_size ), coeffs[i-start] ); + } + + // now check left hand edge + if ( contribs->n0 < 0 ) + { + int save_n0; + float save_n0_coeff; + float * c = coeffs - ( contribs->n0 + 1 ); + + // reinsert the coeffs with it reflected or clamped (insert accumulates, if the coeffs exist) + for( i = -1 ; i > contribs->n0 ; i-- ) + stbir__insert_coeff( contribs, coeffs, stbir__edge_wrap_slow[edge]( i, input_size ), *c-- ); + save_n0 = contribs->n0; + save_n0_coeff = c[0]; // save it, since we didn't do the final one (i==n0), because there might be too many coeffs to hold (before we resize)! + + // now slide all the coeffs down (since we have accumulated them in the positive contribs) and reset the first contrib + contribs->n0 = 0; + for(i = 0 ; i <= contribs->n1 ; i++ ) + coeffs[i] = coeffs[i-save_n0]; + + // now that we have shrunk down the contribs, we insert the first one safely + stbir__insert_coeff( contribs, coeffs, stbir__edge_wrap_slow[edge]( save_n0, input_size ), save_n0_coeff ); + } + } + + if ( contribs->n0 <= contribs->n1 ) + { + int diff = contribs->n1 - contribs->n0 + 1; + while ( diff && ( coeffs[ diff-1 ] == 0.0f ) ) + --diff; + contribs->n1 = contribs->n0 + diff - 1; + + if ( contribs->n0 <= contribs->n1 ) + { + if ( contribs->n0 < lowest ) + lowest = contribs->n0; + if ( contribs->n1 > highest ) + highest = contribs->n1; + if ( diff > widest ) + widest = diff; + } + + // re-zero out unused coefficients (if any) + for( i = diff ; i < coefficient_width ; i++ ) + coeffs[i] = 0.0f; + } + + ++contribs; + coeffs += coefficient_width; + } + filter_info->lowest = lowest; + filter_info->highest = highest; + filter_info->widest = widest; +} + +static int stbir__pack_coefficients( int num_contributors, stbir__contributors* contributors, float * coefficents, int coefficient_width, int widest, int row_width ) +{ + #define STBIR_MOVE_1( dest, src ) { STBIR_NO_UNROLL(dest); ((stbir_uint32*)(dest))[0] = ((stbir_uint32*)(src))[0]; } + #define STBIR_MOVE_2( dest, src ) { STBIR_NO_UNROLL(dest); ((stbir_uint64*)(dest))[0] = ((stbir_uint64*)(src))[0]; } + #ifdef STBIR_SIMD + #define STBIR_MOVE_4( dest, src ) { stbir__simdf t; STBIR_NO_UNROLL(dest); stbir__simdf_load( t, src ); stbir__simdf_store( dest, t ); } + #else + #define STBIR_MOVE_4( dest, src ) { STBIR_NO_UNROLL(dest); ((stbir_uint64*)(dest))[0] = ((stbir_uint64*)(src))[0]; ((stbir_uint64*)(dest))[1] = ((stbir_uint64*)(src))[1]; } + #endif + if ( coefficient_width != widest ) + { + float * pc = coefficents; + float * coeffs = coefficents; + float * pc_end = coefficents + num_contributors * widest; + switch( widest ) + { + case 1: + do { + STBIR_MOVE_1( pc, coeffs ); + ++pc; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 2: + do { + STBIR_MOVE_2( pc, coeffs ); + pc += 2; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 3: + do { + STBIR_MOVE_2( pc, coeffs ); + STBIR_MOVE_1( pc+2, coeffs+2 ); + pc += 3; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 4: + do { + STBIR_MOVE_4( pc, coeffs ); + pc += 4; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 5: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_1( pc+4, coeffs+4 ); + pc += 5; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 6: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_2( pc+4, coeffs+4 ); + pc += 6; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 7: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_2( pc+4, coeffs+4 ); + STBIR_MOVE_1( pc+6, coeffs+6 ); + pc += 7; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 8: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_4( pc+4, coeffs+4 ); + pc += 8; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 9: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_4( pc+4, coeffs+4 ); + STBIR_MOVE_1( pc+8, coeffs+8 ); + pc += 9; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 10: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_4( pc+4, coeffs+4 ); + STBIR_MOVE_2( pc+8, coeffs+8 ); + pc += 10; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 11: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_4( pc+4, coeffs+4 ); + STBIR_MOVE_2( pc+8, coeffs+8 ); + STBIR_MOVE_1( pc+10, coeffs+10 ); + pc += 11; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + case 12: + do { + STBIR_MOVE_4( pc, coeffs ); + STBIR_MOVE_4( pc+4, coeffs+4 ); + STBIR_MOVE_4( pc+8, coeffs+8 ); + pc += 12; + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + default: + do { + float * copy_end = pc + widest - 4; + float * c = coeffs; + do { + STBIR_NO_UNROLL( pc ); + STBIR_MOVE_4( pc, c ); + pc += 4; + c += 4; + } while ( pc <= copy_end ); + copy_end += 4; + while ( pc < copy_end ) + { + STBIR_MOVE_1( pc, c ); + ++pc; ++c; + } + coeffs += coefficient_width; + } while ( pc < pc_end ); + break; + } + } + + // some horizontal routines read one float off the end (which is then masked off), so put in a sentinal so we don't read an snan or denormal + coefficents[ widest * num_contributors ] = 8888.0f; + + // the minimum we might read for unrolled filters widths is 12. So, we need to + // make sure we never read outside the decode buffer, by possibly moving + // the sample area back into the scanline, and putting zeros weights first. + // we start on the right edge and check until we're well past the possible + // clip area (2*widest). + { + stbir__contributors * contribs = contributors + num_contributors - 1; + float * coeffs = coefficents + widest * ( num_contributors - 1 ); + + // go until no chance of clipping (this is usually less than 8 lops) + while ( ( ( contribs->n0 + widest*2 ) >= row_width ) && ( contribs >= contributors ) ) + { + // might we clip?? + if ( ( contribs->n0 + widest ) > row_width ) + { + int stop_range = widest; + + // if range is larger than 12, it will be handled by generic loops that can terminate on the exact length + // of this contrib n1, instead of a fixed widest amount - so calculate this + if ( widest > 12 ) + { + int mod; + + // how far will be read in the n_coeff loop (which depends on the widest count mod4); + mod = widest & 3; + stop_range = ( ( ( contribs->n1 - contribs->n0 + 1 ) - mod + 3 ) & ~3 ) + mod; + + // the n_coeff loops do a minimum amount of coeffs, so factor that in! + if ( stop_range < ( 8 + mod ) ) stop_range = 8 + mod; + } + + // now see if we still clip with the refined range + if ( ( contribs->n0 + stop_range ) > row_width ) + { + int new_n0 = row_width - stop_range; + int num = contribs->n1 - contribs->n0 + 1; + int backup = contribs->n0 - new_n0; + float * from_co = coeffs + num - 1; + float * to_co = from_co + backup; + + STBIR_ASSERT( ( new_n0 >= 0 ) && ( new_n0 < contribs->n0 ) ); + + // move the coeffs over + while( num ) + { + *to_co-- = *from_co--; + --num; + } + // zero new positions + while ( to_co >= coeffs ) + *to_co-- = 0; + // set new start point + contribs->n0 = new_n0; + if ( widest > 12 ) + { + int mod; + + // how far will be read in the n_coeff loop (which depends on the widest count mod4); + mod = widest & 3; + stop_range = ( ( ( contribs->n1 - contribs->n0 + 1 ) - mod + 3 ) & ~3 ) + mod; + + // the n_coeff loops do a minimum amount of coeffs, so factor that in! + if ( stop_range < ( 8 + mod ) ) stop_range = 8 + mod; + } + } + } + --contribs; + coeffs -= widest; + } + } + + return widest; + #undef STBIR_MOVE_1 + #undef STBIR_MOVE_2 + #undef STBIR_MOVE_4 +} + +static void stbir__calculate_filters( stbir__sampler * samp, stbir__sampler * other_axis_for_pivot, void * user_data STBIR_ONLY_PROFILE_BUILD_GET_INFO ) +{ + int n; + float scale = samp->scale_info.scale; + stbir__kernel_callback * kernel = samp->filter_kernel; + stbir__support_callback * support = samp->filter_support; + float inv_scale = samp->scale_info.inv_scale; + int input_full_size = samp->scale_info.input_full_size; + int gather_num_contributors = samp->num_contributors; + stbir__contributors* gather_contributors = samp->contributors; + float * gather_coeffs = samp->coefficients; + int gather_coefficient_width = samp->coefficient_width; + + switch ( samp->is_gather ) + { + case 1: // gather upsample + { + float out_pixels_radius = support(inv_scale,user_data) * scale; + + stbir__calculate_coefficients_for_gather_upsample( out_pixels_radius, kernel, &samp->scale_info, gather_num_contributors, gather_contributors, gather_coeffs, gather_coefficient_width, samp->edge, user_data ); + + STBIR_PROFILE_BUILD_START( cleanup ); + stbir__cleanup_gathered_coefficients( samp->edge, &samp->extent_info, &samp->scale_info, gather_num_contributors, gather_contributors, gather_coeffs, gather_coefficient_width ); + STBIR_PROFILE_BUILD_END( cleanup ); + } + break; + + case 0: // scatter downsample (only on vertical) + case 2: // gather downsample + { + float in_pixels_radius = support(scale,user_data) * inv_scale; + int filter_pixel_margin = samp->filter_pixel_margin; + int input_end = input_full_size + filter_pixel_margin; + + // if this is a scatter, we do a downsample gather to get the coeffs, and then pivot after + if ( !samp->is_gather ) + { + // check if we are using the same gather downsample on the horizontal as this vertical, + // if so, then we don't have to generate them, we can just pivot from the horizontal. + if ( other_axis_for_pivot ) + { + gather_contributors = other_axis_for_pivot->contributors; + gather_coeffs = other_axis_for_pivot->coefficients; + gather_coefficient_width = other_axis_for_pivot->coefficient_width; + gather_num_contributors = other_axis_for_pivot->num_contributors; + samp->extent_info.lowest = other_axis_for_pivot->extent_info.lowest; + samp->extent_info.highest = other_axis_for_pivot->extent_info.highest; + samp->extent_info.widest = other_axis_for_pivot->extent_info.widest; + goto jump_right_to_pivot; + } + + gather_contributors = samp->gather_prescatter_contributors; + gather_coeffs = samp->gather_prescatter_coefficients; + gather_coefficient_width = samp->gather_prescatter_coefficient_width; + gather_num_contributors = samp->gather_prescatter_num_contributors; + } + + stbir__calculate_coefficients_for_gather_downsample( -filter_pixel_margin, input_end, in_pixels_radius, kernel, &samp->scale_info, gather_coefficient_width, gather_num_contributors, gather_contributors, gather_coeffs, user_data ); + + STBIR_PROFILE_BUILD_START( cleanup ); + stbir__cleanup_gathered_coefficients( samp->edge, &samp->extent_info, &samp->scale_info, gather_num_contributors, gather_contributors, gather_coeffs, gather_coefficient_width ); + STBIR_PROFILE_BUILD_END( cleanup ); + + if ( !samp->is_gather ) + { + // if this is a scatter (vertical only), then we need to pivot the coeffs + stbir__contributors * scatter_contributors; + int highest_set; + + jump_right_to_pivot: + + STBIR_PROFILE_BUILD_START( pivot ); + + highest_set = (-filter_pixel_margin) - 1; + for (n = 0; n < gather_num_contributors; n++) + { + int k; + int gn0 = gather_contributors->n0, gn1 = gather_contributors->n1; + int scatter_coefficient_width = samp->coefficient_width; + float * scatter_coeffs = samp->coefficients + ( gn0 + filter_pixel_margin ) * scatter_coefficient_width; + float * g_coeffs = gather_coeffs; + scatter_contributors = samp->contributors + ( gn0 + filter_pixel_margin ); + + for (k = gn0 ; k <= gn1 ; k++ ) + { + float gc = *g_coeffs++; + if ( ( k > highest_set ) || ( scatter_contributors->n0 > scatter_contributors->n1 ) ) + { + { + // if we are skipping over several contributors, we need to clear the skipped ones + stbir__contributors * clear_contributors = samp->contributors + ( highest_set + filter_pixel_margin + 1); + while ( clear_contributors < scatter_contributors ) + { + clear_contributors->n0 = 0; + clear_contributors->n1 = -1; + ++clear_contributors; + } + } + scatter_contributors->n0 = n; + scatter_contributors->n1 = n; + scatter_coeffs[0] = gc; + highest_set = k; + } + else + { + stbir__insert_coeff( scatter_contributors, scatter_coeffs, n, gc ); + } + ++scatter_contributors; + scatter_coeffs += scatter_coefficient_width; + } + + ++gather_contributors; + gather_coeffs += gather_coefficient_width; + } + + // now clear any unset contribs + { + stbir__contributors * clear_contributors = samp->contributors + ( highest_set + filter_pixel_margin + 1); + stbir__contributors * end_contributors = samp->contributors + samp->num_contributors; + while ( clear_contributors < end_contributors ) + { + clear_contributors->n0 = 0; + clear_contributors->n1 = -1; + ++clear_contributors; + } + } + + STBIR_PROFILE_BUILD_END( pivot ); + } + } + break; + } +} + + +//======================================================================================================== +// scanline decoders and encoders + +#define stbir__coder_min_num 1 +#define STB_IMAGE_RESIZE_DO_CODERS +#include STBIR__HEADER_FILENAME + +#define stbir__decode_suffix BGRA +#define stbir__decode_swizzle +#define stbir__decode_order0 2 +#define stbir__decode_order1 1 +#define stbir__decode_order2 0 +#define stbir__decode_order3 3 +#define stbir__encode_order0 2 +#define stbir__encode_order1 1 +#define stbir__encode_order2 0 +#define stbir__encode_order3 3 +#define stbir__coder_min_num 4 +#define STB_IMAGE_RESIZE_DO_CODERS +#include STBIR__HEADER_FILENAME + +#define stbir__decode_suffix ARGB +#define stbir__decode_swizzle +#define stbir__decode_order0 1 +#define stbir__decode_order1 2 +#define stbir__decode_order2 3 +#define stbir__decode_order3 0 +#define stbir__encode_order0 3 +#define stbir__encode_order1 0 +#define stbir__encode_order2 1 +#define stbir__encode_order3 2 +#define stbir__coder_min_num 4 +#define STB_IMAGE_RESIZE_DO_CODERS +#include STBIR__HEADER_FILENAME + +#define stbir__decode_suffix ABGR +#define stbir__decode_swizzle +#define stbir__decode_order0 3 +#define stbir__decode_order1 2 +#define stbir__decode_order2 1 +#define stbir__decode_order3 0 +#define stbir__encode_order0 3 +#define stbir__encode_order1 2 +#define stbir__encode_order2 1 +#define stbir__encode_order3 0 +#define stbir__coder_min_num 4 +#define STB_IMAGE_RESIZE_DO_CODERS +#include STBIR__HEADER_FILENAME + +#define stbir__decode_suffix AR +#define stbir__decode_swizzle +#define stbir__decode_order0 1 +#define stbir__decode_order1 0 +#define stbir__decode_order2 3 +#define stbir__decode_order3 2 +#define stbir__encode_order0 1 +#define stbir__encode_order1 0 +#define stbir__encode_order2 3 +#define stbir__encode_order3 2 +#define stbir__coder_min_num 2 +#define STB_IMAGE_RESIZE_DO_CODERS +#include STBIR__HEADER_FILENAME + + +// fancy alpha means we expand to keep both premultipied and non-premultiplied color channels +static void stbir__fancy_alpha_weight_4ch( float * out_buffer, int width_times_channels ) +{ + float STBIR_STREAMOUT_PTR(*) out = out_buffer; + float const * end_decode = out_buffer + ( width_times_channels / 4 ) * 7; // decode buffer aligned to end of out_buffer + float STBIR_STREAMOUT_PTR(*) decode = (float*)end_decode - width_times_channels; + + // fancy alpha is stored internally as R G B A Rpm Gpm Bpm + + #ifdef STBIR_SIMD + + #ifdef STBIR_SIMD8 + decode += 16; + while ( decode <= end_decode ) + { + stbir__simdf8 d0,d1,a0,a1,p0,p1; + STBIR_NO_UNROLL(decode); + stbir__simdf8_load( d0, decode-16 ); + stbir__simdf8_load( d1, decode-16+8 ); + stbir__simdf8_0123to33333333( a0, d0 ); + stbir__simdf8_0123to33333333( a1, d1 ); + stbir__simdf8_mult( p0, a0, d0 ); + stbir__simdf8_mult( p1, a1, d1 ); + stbir__simdf8_bot4s( a0, d0, p0 ); + stbir__simdf8_bot4s( a1, d1, p1 ); + stbir__simdf8_top4s( d0, d0, p0 ); + stbir__simdf8_top4s( d1, d1, p1 ); + stbir__simdf8_store ( out, a0 ); + stbir__simdf8_store ( out+7, d0 ); + stbir__simdf8_store ( out+14, a1 ); + stbir__simdf8_store ( out+21, d1 ); + decode += 16; + out += 28; + } + decode -= 16; + #else + decode += 8; + while ( decode <= end_decode ) + { + stbir__simdf d0,a0,d1,a1,p0,p1; + STBIR_NO_UNROLL(decode); + stbir__simdf_load( d0, decode-8 ); + stbir__simdf_load( d1, decode-8+4 ); + stbir__simdf_0123to3333( a0, d0 ); + stbir__simdf_0123to3333( a1, d1 ); + stbir__simdf_mult( p0, a0, d0 ); + stbir__simdf_mult( p1, a1, d1 ); + stbir__simdf_store ( out, d0 ); + stbir__simdf_store ( out+4, p0 ); + stbir__simdf_store ( out+7, d1 ); + stbir__simdf_store ( out+7+4, p1 ); + decode += 8; + out += 14; + } + decode -= 8; + #endif + + // might be one last odd pixel + #ifdef STBIR_SIMD8 + while ( decode < end_decode ) + #else + if ( decode < end_decode ) + #endif + { + stbir__simdf d,a,p; + stbir__simdf_load( d, decode ); + stbir__simdf_0123to3333( a, d ); + stbir__simdf_mult( p, a, d ); + stbir__simdf_store ( out, d ); + stbir__simdf_store ( out+4, p ); + decode += 4; + out += 7; + } + + #else + + while( decode < end_decode ) + { + float r = decode[0], g = decode[1], b = decode[2], alpha = decode[3]; + out[0] = r; + out[1] = g; + out[2] = b; + out[3] = alpha; + out[4] = r * alpha; + out[5] = g * alpha; + out[6] = b * alpha; + out += 7; + decode += 4; + } + + #endif +} + +static void stbir__fancy_alpha_weight_2ch( float * out_buffer, int width_times_channels ) +{ + float STBIR_STREAMOUT_PTR(*) out = out_buffer; + float const * end_decode = out_buffer + ( width_times_channels / 2 ) * 3; + float STBIR_STREAMOUT_PTR(*) decode = (float*)end_decode - width_times_channels; + + // for fancy alpha, turns into: [X A Xpm][X A Xpm],etc + + #ifdef STBIR_SIMD + + decode += 8; + if ( decode <= end_decode ) + { + do { + #ifdef STBIR_SIMD8 + stbir__simdf8 d0,a0,p0; + STBIR_NO_UNROLL(decode); + stbir__simdf8_load( d0, decode-8 ); + stbir__simdf8_0123to11331133( p0, d0 ); + stbir__simdf8_0123to00220022( a0, d0 ); + stbir__simdf8_mult( p0, p0, a0 ); + + stbir__simdf_store2( out, stbir__if_simdf8_cast_to_simdf4( d0 ) ); + stbir__simdf_store( out+2, stbir__if_simdf8_cast_to_simdf4( p0 ) ); + stbir__simdf_store2h( out+3, stbir__if_simdf8_cast_to_simdf4( d0 ) ); + + stbir__simdf_store2( out+6, stbir__simdf8_gettop4( d0 ) ); + stbir__simdf_store( out+8, stbir__simdf8_gettop4( p0 ) ); + stbir__simdf_store2h( out+9, stbir__simdf8_gettop4( d0 ) ); + #else + stbir__simdf d0,a0,d1,a1,p0,p1; + STBIR_NO_UNROLL(decode); + stbir__simdf_load( d0, decode-8 ); + stbir__simdf_load( d1, decode-8+4 ); + stbir__simdf_0123to1133( p0, d0 ); + stbir__simdf_0123to1133( p1, d1 ); + stbir__simdf_0123to0022( a0, d0 ); + stbir__simdf_0123to0022( a1, d1 ); + stbir__simdf_mult( p0, p0, a0 ); + stbir__simdf_mult( p1, p1, a1 ); + + stbir__simdf_store2( out, d0 ); + stbir__simdf_store( out+2, p0 ); + stbir__simdf_store2h( out+3, d0 ); + + stbir__simdf_store2( out+6, d1 ); + stbir__simdf_store( out+8, p1 ); + stbir__simdf_store2h( out+9, d1 ); + #endif + decode += 8; + out += 12; + } while ( decode <= end_decode ); + } + decode -= 8; + #endif + + while( decode < end_decode ) + { + float x = decode[0], y = decode[1]; + STBIR_SIMD_NO_UNROLL(decode); + out[0] = x; + out[1] = y; + out[2] = x * y; + out += 3; + decode += 2; + } +} + +static void stbir__fancy_alpha_unweight_4ch( float * encode_buffer, int width_times_channels ) +{ + float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer; + float STBIR_SIMD_STREAMOUT_PTR(*) input = encode_buffer; + float const * end_output = encode_buffer + width_times_channels; + + // fancy RGBA is stored internally as R G B A Rpm Gpm Bpm + + do { + float alpha = input[3]; +#ifdef STBIR_SIMD + stbir__simdf i,ia; + STBIR_SIMD_NO_UNROLL(encode); + if ( alpha < stbir__small_float ) + { + stbir__simdf_load( i, input ); + stbir__simdf_store( encode, i ); + } + else + { + stbir__simdf_load1frep4( ia, 1.0f / alpha ); + stbir__simdf_load( i, input+4 ); + stbir__simdf_mult( i, i, ia ); + stbir__simdf_store( encode, i ); + encode[3] = alpha; + } +#else + if ( alpha < stbir__small_float ) + { + encode[0] = input[0]; + encode[1] = input[1]; + encode[2] = input[2]; + } + else + { + float ialpha = 1.0f / alpha; + encode[0] = input[4] * ialpha; + encode[1] = input[5] * ialpha; + encode[2] = input[6] * ialpha; + } + encode[3] = alpha; +#endif + + input += 7; + encode += 4; + } while ( encode < end_output ); +} + +// format: [X A Xpm][X A Xpm] etc +static void stbir__fancy_alpha_unweight_2ch( float * encode_buffer, int width_times_channels ) +{ + float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer; + float STBIR_SIMD_STREAMOUT_PTR(*) input = encode_buffer; + float const * end_output = encode_buffer + width_times_channels; + + do { + float alpha = input[1]; + encode[0] = input[0]; + if ( alpha >= stbir__small_float ) + encode[0] = input[2] / alpha; + encode[1] = alpha; + + input += 3; + encode += 2; + } while ( encode < end_output ); +} + +static void stbir__simple_alpha_weight_4ch( float * decode_buffer, int width_times_channels ) +{ + float STBIR_STREAMOUT_PTR(*) decode = decode_buffer; + float const * end_decode = decode_buffer + width_times_channels; + + #ifdef STBIR_SIMD + { + decode += 2 * stbir__simdfX_float_count; + while ( decode <= end_decode ) + { + stbir__simdfX d0,a0,d1,a1; + STBIR_NO_UNROLL(decode); + stbir__simdfX_load( d0, decode-2*stbir__simdfX_float_count ); + stbir__simdfX_load( d1, decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count ); + stbir__simdfX_aaa1( a0, d0, STBIR_onesX ); + stbir__simdfX_aaa1( a1, d1, STBIR_onesX ); + stbir__simdfX_mult( d0, d0, a0 ); + stbir__simdfX_mult( d1, d1, a1 ); + stbir__simdfX_store ( decode-2*stbir__simdfX_float_count, d0 ); + stbir__simdfX_store ( decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count, d1 ); + decode += 2 * stbir__simdfX_float_count; + } + decode -= 2 * stbir__simdfX_float_count; + + // few last pixels remnants + #ifdef STBIR_SIMD8 + while ( decode < end_decode ) + #else + if ( decode < end_decode ) + #endif + { + stbir__simdf d,a; + stbir__simdf_load( d, decode ); + stbir__simdf_aaa1( a, d, STBIR__CONSTF(STBIR_ones) ); + stbir__simdf_mult( d, d, a ); + stbir__simdf_store ( decode, d ); + decode += 4; + } + } + + #else + + while( decode < end_decode ) + { + float alpha = decode[3]; + decode[0] *= alpha; + decode[1] *= alpha; + decode[2] *= alpha; + decode += 4; + } + + #endif +} + +static void stbir__simple_alpha_weight_2ch( float * decode_buffer, int width_times_channels ) +{ + float STBIR_STREAMOUT_PTR(*) decode = decode_buffer; + float const * end_decode = decode_buffer + width_times_channels; + + #ifdef STBIR_SIMD + decode += 2 * stbir__simdfX_float_count; + while ( decode <= end_decode ) + { + stbir__simdfX d0,a0,d1,a1; + STBIR_NO_UNROLL(decode); + stbir__simdfX_load( d0, decode-2*stbir__simdfX_float_count ); + stbir__simdfX_load( d1, decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count ); + stbir__simdfX_a1a1( a0, d0, STBIR_onesX ); + stbir__simdfX_a1a1( a1, d1, STBIR_onesX ); + stbir__simdfX_mult( d0, d0, a0 ); + stbir__simdfX_mult( d1, d1, a1 ); + stbir__simdfX_store ( decode-2*stbir__simdfX_float_count, d0 ); + stbir__simdfX_store ( decode-2*stbir__simdfX_float_count+stbir__simdfX_float_count, d1 ); + decode += 2 * stbir__simdfX_float_count; + } + decode -= 2 * stbir__simdfX_float_count; + #endif + + while( decode < end_decode ) + { + float alpha = decode[1]; + STBIR_SIMD_NO_UNROLL(decode); + decode[0] *= alpha; + decode += 2; + } +} + +static void stbir__simple_alpha_unweight_4ch( float * encode_buffer, int width_times_channels ) +{ + float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer; + float const * end_output = encode_buffer + width_times_channels; + + do { + float alpha = encode[3]; + +#ifdef STBIR_SIMD + stbir__simdf i,ia; + STBIR_SIMD_NO_UNROLL(encode); + if ( alpha >= stbir__small_float ) + { + stbir__simdf_load1frep4( ia, 1.0f / alpha ); + stbir__simdf_load( i, encode ); + stbir__simdf_mult( i, i, ia ); + stbir__simdf_store( encode, i ); + encode[3] = alpha; + } +#else + if ( alpha >= stbir__small_float ) + { + float ialpha = 1.0f / alpha; + encode[0] *= ialpha; + encode[1] *= ialpha; + encode[2] *= ialpha; + } +#endif + encode += 4; + } while ( encode < end_output ); +} + +static void stbir__simple_alpha_unweight_2ch( float * encode_buffer, int width_times_channels ) +{ + float STBIR_SIMD_STREAMOUT_PTR(*) encode = encode_buffer; + float const * end_output = encode_buffer + width_times_channels; + + do { + float alpha = encode[1]; + if ( alpha >= stbir__small_float ) + encode[0] /= alpha; + encode += 2; + } while ( encode < end_output ); +} + + +// only used in RGB->BGR or BGR->RGB +static void stbir__simple_flip_3ch( float * decode_buffer, int width_times_channels ) +{ + float STBIR_STREAMOUT_PTR(*) decode = decode_buffer; + float const * end_decode = decode_buffer + width_times_channels; + + decode += 12; + while( decode <= end_decode ) + { + float t0,t1,t2,t3; + STBIR_NO_UNROLL(decode); + t0 = decode[0]; t1 = decode[3]; t2 = decode[6]; t3 = decode[9]; + decode[0] = decode[2]; decode[3] = decode[5]; decode[6] = decode[8]; decode[9] = decode[11]; + decode[2] = t0; decode[5] = t1; decode[8] = t2; decode[11] = t3; + decode += 12; + } + decode -= 12; + + while( decode < end_decode ) + { + float t = decode[0]; + STBIR_NO_UNROLL(decode); + decode[0] = decode[2]; + decode[2] = t; + decode += 3; + } +} + + + +static void stbir__decode_scanline(stbir__info const * stbir_info, int n, float * output_buffer STBIR_ONLY_PROFILE_GET_SPLIT_INFO ) +{ + int channels = stbir_info->channels; + int effective_channels = stbir_info->effective_channels; + int input_sample_in_bytes = stbir__type_size[stbir_info->input_type] * channels; + stbir_edge edge_horizontal = stbir_info->horizontal.edge; + stbir_edge edge_vertical = stbir_info->vertical.edge; + int row = stbir__edge_wrap(edge_vertical, n, stbir_info->vertical.scale_info.input_full_size); + const void* input_plane_data = ( (char *) stbir_info->input_data ) + (ptrdiff_t)row * (ptrdiff_t) stbir_info->input_stride_bytes; + stbir__span const * spans = stbir_info->scanline_extents.spans; + float* full_decode_buffer = output_buffer - stbir_info->scanline_extents.conservative.n0 * effective_channels; + + // if we are on edge_zero, and we get in here with an out of bounds n, then the calculate filters has failed + STBIR_ASSERT( !(edge_vertical == STBIR_EDGE_ZERO && (n < 0 || n >= stbir_info->vertical.scale_info.input_full_size)) ); + + do + { + float * decode_buffer; + void const * input_data; + float * end_decode; + int width_times_channels; + int width; + + if ( spans->n1 < spans->n0 ) + break; + + width = spans->n1 + 1 - spans->n0; + decode_buffer = full_decode_buffer + spans->n0 * effective_channels; + end_decode = full_decode_buffer + ( spans->n1 + 1 ) * effective_channels; + width_times_channels = width * channels; + + // read directly out of input plane by default + input_data = ( (char*)input_plane_data ) + spans->pixel_offset_for_input * input_sample_in_bytes; + + // if we have an input callback, call it to get the input data + if ( stbir_info->in_pixels_cb ) + { + // call the callback with a temp buffer (that they can choose to use or not). the temp is just right aligned memory in the decode_buffer itself + input_data = stbir_info->in_pixels_cb( ( (char*) end_decode ) - ( width * input_sample_in_bytes ), input_plane_data, width, spans->pixel_offset_for_input, row, stbir_info->user_data ); + } + + STBIR_PROFILE_START( decode ); + // convert the pixels info the float decode_buffer, (we index from end_decode, so that when channelsdecode_pixels( (float*)end_decode - width_times_channels, width_times_channels, input_data ); + STBIR_PROFILE_END( decode ); + + if (stbir_info->alpha_weight) + { + STBIR_PROFILE_START( alpha ); + stbir_info->alpha_weight( decode_buffer, width_times_channels ); + STBIR_PROFILE_END( alpha ); + } + + ++spans; + } while ( spans <= ( &stbir_info->scanline_extents.spans[1] ) ); + + // handle the edge_wrap filter (all other types are handled back out at the calculate_filter stage) + // basically the idea here is that if we have the whole scanline in memory, we don't redecode the + // wrapped edge pixels, and instead just memcpy them from the scanline into the edge positions + if ( ( edge_horizontal == STBIR_EDGE_WRAP ) && ( stbir_info->scanline_extents.edge_sizes[0] | stbir_info->scanline_extents.edge_sizes[1] ) ) + { + // this code only runs if we're in edge_wrap, and we're doing the entire scanline + int e, start_x[2]; + int input_full_size = stbir_info->horizontal.scale_info.input_full_size; + + start_x[0] = -stbir_info->scanline_extents.edge_sizes[0]; // left edge start x + start_x[1] = input_full_size; // right edge + + for( e = 0; e < 2 ; e++ ) + { + // do each margin + int margin = stbir_info->scanline_extents.edge_sizes[e]; + if ( margin ) + { + int x = start_x[e]; + float * marg = full_decode_buffer + x * effective_channels; + float const * src = full_decode_buffer + stbir__edge_wrap(edge_horizontal, x, input_full_size) * effective_channels; + STBIR_MEMCPY( marg, src, margin * effective_channels * sizeof(float) ); + } + } + } +} + + +//================= +// Do 1 channel horizontal routines + +#ifdef STBIR_SIMD + +#define stbir__1_coeff_only() \ + stbir__simdf tot,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1( c, hc ); \ + stbir__simdf_mult1_mem( tot, c, decode ); + +#define stbir__2_coeff_only() \ + stbir__simdf tot,c,d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2z( c, hc ); \ + stbir__simdf_load2( d, decode ); \ + stbir__simdf_mult( tot, c, d ); \ + stbir__simdf_0123to1230( c, tot ); \ + stbir__simdf_add1( tot, tot, c ); + +#define stbir__3_coeff_only() \ + stbir__simdf tot,c,t; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( c, hc ); \ + stbir__simdf_mult_mem( tot, c, decode ); \ + stbir__simdf_0123to1230( c, tot ); \ + stbir__simdf_0123to2301( t, tot ); \ + stbir__simdf_add1( tot, tot, c ); \ + stbir__simdf_add1( tot, tot, t ); + +#define stbir__store_output_tiny() \ + stbir__simdf_store1( output, tot ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 1; + +#define stbir__4_coeff_start() \ + stbir__simdf tot,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( c, hc ); \ + stbir__simdf_mult_mem( tot, c, decode ); \ + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( c, hc + (ofs) ); \ + stbir__simdf_madd_mem( tot, tot, c, decode+(ofs) ); + +#define stbir__1_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + stbir__simdf_load1z( c, hc + (ofs) ); \ + stbir__simdf_load1( d, decode + (ofs) ); \ + stbir__simdf_madd( tot, tot, d, c ); } + +#define stbir__2_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + stbir__simdf_load2z( c, hc+(ofs) ); \ + stbir__simdf_load2( d, decode+(ofs) ); \ + stbir__simdf_madd( tot, tot, d, c ); } + +#define stbir__3_coeff_setup() \ + stbir__simdf mask; \ + stbir__simdf_load( mask, STBIR_mask + 3 ); + +#define stbir__3_coeff_remnant( ofs ) \ + stbir__simdf_load( c, hc+(ofs) ); \ + stbir__simdf_and( c, c, mask ); \ + stbir__simdf_madd_mem( tot, tot, c, decode+(ofs) ); + +#define stbir__store_output() \ + stbir__simdf_0123to2301( c, tot ); \ + stbir__simdf_add( tot, tot, c ); \ + stbir__simdf_0123to1230( c, tot ); \ + stbir__simdf_add1( tot, tot, c ); \ + stbir__simdf_store1( output, tot ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 1; + +#else + +#define stbir__1_coeff_only() \ + float tot; \ + tot = decode[0]*hc[0]; + +#define stbir__2_coeff_only() \ + float tot; \ + tot = decode[0] * hc[0]; \ + tot += decode[1] * hc[1]; + +#define stbir__3_coeff_only() \ + float tot; \ + tot = decode[0] * hc[0]; \ + tot += decode[1] * hc[1]; \ + tot += decode[2] * hc[2]; + +#define stbir__store_output_tiny() \ + output[0] = tot; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 1; + +#define stbir__4_coeff_start() \ + float tot0,tot1,tot2,tot3; \ + tot0 = decode[0] * hc[0]; \ + tot1 = decode[1] * hc[1]; \ + tot2 = decode[2] * hc[2]; \ + tot3 = decode[3] * hc[3]; + +#define stbir__4_coeff_continue_from_4( ofs ) \ + tot0 += decode[0+(ofs)] * hc[0+(ofs)]; \ + tot1 += decode[1+(ofs)] * hc[1+(ofs)]; \ + tot2 += decode[2+(ofs)] * hc[2+(ofs)]; \ + tot3 += decode[3+(ofs)] * hc[3+(ofs)]; + +#define stbir__1_coeff_remnant( ofs ) \ + tot0 += decode[0+(ofs)] * hc[0+(ofs)]; + +#define stbir__2_coeff_remnant( ofs ) \ + tot0 += decode[0+(ofs)] * hc[0+(ofs)]; \ + tot1 += decode[1+(ofs)] * hc[1+(ofs)]; \ + +#define stbir__3_coeff_remnant( ofs ) \ + tot0 += decode[0+(ofs)] * hc[0+(ofs)]; \ + tot1 += decode[1+(ofs)] * hc[1+(ofs)]; \ + tot2 += decode[2+(ofs)] * hc[2+(ofs)]; + +#define stbir__store_output() \ + output[0] = (tot0+tot2)+(tot1+tot3); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 1; + +#endif + +#define STBIR__horizontal_channels 1 +#define STB_IMAGE_RESIZE_DO_HORIZONTALS +#include STBIR__HEADER_FILENAME + + +//================= +// Do 2 channel horizontal routines + +#ifdef STBIR_SIMD + +#define stbir__1_coeff_only() \ + stbir__simdf tot,c,d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1z( c, hc ); \ + stbir__simdf_0123to0011( c, c ); \ + stbir__simdf_load2( d, decode ); \ + stbir__simdf_mult( tot, d, c ); + +#define stbir__2_coeff_only() \ + stbir__simdf tot,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( c, hc ); \ + stbir__simdf_0123to0011( c, c ); \ + stbir__simdf_mult_mem( tot, c, decode ); + +#define stbir__3_coeff_only() \ + stbir__simdf tot,c,cs,d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_mult_mem( tot, c, decode ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_load2z( d, decode+4 ); \ + stbir__simdf_madd( tot, tot, d, c ); + +#define stbir__store_output_tiny() \ + stbir__simdf_0123to2301( c, tot ); \ + stbir__simdf_add( tot, tot, c ); \ + stbir__simdf_store2( output, tot ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 2; + +#ifdef STBIR_SIMD8 + +#define stbir__4_coeff_start() \ + stbir__simdf8 tot0,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc ); \ + stbir__simdf8_0123to00112233( c, cs ); \ + stbir__simdf8_mult_mem( tot0, c, decode ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00112233( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*2 ); + +#define stbir__1_coeff_remnant( ofs ) \ + { stbir__simdf t; \ + stbir__simdf_load1z( t, hc + (ofs) ); \ + stbir__simdf_0123to0011( t, t ); \ + stbir__simdf_mult_mem( t, t, decode+(ofs)*2 ); \ + stbir__simdf8_add4( tot0, tot0, t ); } + +#define stbir__2_coeff_remnant( ofs ) \ + { stbir__simdf t; \ + stbir__simdf_load2( t, hc + (ofs) ); \ + stbir__simdf_0123to0011( t, t ); \ + stbir__simdf_mult_mem( t, t, decode+(ofs)*2 ); \ + stbir__simdf8_add4( tot0, tot0, t ); } + +#define stbir__3_coeff_remnant( ofs ) \ + { stbir__simdf8 d; \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00112233( c, cs ); \ + stbir__simdf8_load6z( d, decode+(ofs)*2 ); \ + stbir__simdf8_madd( tot0, tot0, c, d ); } + +#define stbir__store_output() \ + { stbir__simdf t,c; \ + stbir__simdf8_add4halves( t, stbir__if_simdf8_cast_to_simdf4(tot0), tot0 ); \ + stbir__simdf_0123to2301( c, t ); \ + stbir__simdf_add( t, t, c ); \ + stbir__simdf_store2( output, t ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 2; } + +#else + +#define stbir__4_coeff_start() \ + stbir__simdf tot0,tot1,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_0123to2233( c, cs ); \ + stbir__simdf_mult_mem( tot1, c, decode+4 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*2 ); \ + stbir__simdf_0123to2233( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*2+4 ); + +#define stbir__1_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + stbir__simdf_load1z( cs, hc + (ofs) ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_load2( d, decode + (ofs) * 2 ); \ + stbir__simdf_madd( tot0, tot0, d, c ); } + +#define stbir__2_coeff_remnant( ofs ) \ + stbir__simdf_load2( cs, hc + (ofs) ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*2 ); + +#define stbir__3_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0011( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*2 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_load2z( d, decode + (ofs) * 2 + 4 ); \ + stbir__simdf_madd( tot1, tot1, d, c ); } + +#define stbir__store_output() \ + stbir__simdf_add( tot0, tot0, tot1 ); \ + stbir__simdf_0123to2301( c, tot0 ); \ + stbir__simdf_add( tot0, tot0, c ); \ + stbir__simdf_store2( output, tot0 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 2; + +#endif + +#else + +#define stbir__1_coeff_only() \ + float tota,totb,c; \ + c = hc[0]; \ + tota = decode[0]*c; \ + totb = decode[1]*c; + +#define stbir__2_coeff_only() \ + float tota,totb,c; \ + c = hc[0]; \ + tota = decode[0]*c; \ + totb = decode[1]*c; \ + c = hc[1]; \ + tota += decode[2]*c; \ + totb += decode[3]*c; + +// this weird order of add matches the simd +#define stbir__3_coeff_only() \ + float tota,totb,c; \ + c = hc[0]; \ + tota = decode[0]*c; \ + totb = decode[1]*c; \ + c = hc[2]; \ + tota += decode[4]*c; \ + totb += decode[5]*c; \ + c = hc[1]; \ + tota += decode[2]*c; \ + totb += decode[3]*c; + +#define stbir__store_output_tiny() \ + output[0] = tota; \ + output[1] = totb; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 2; + +#define stbir__4_coeff_start() \ + float tota0,tota1,tota2,tota3,totb0,totb1,totb2,totb3,c; \ + c = hc[0]; \ + tota0 = decode[0]*c; \ + totb0 = decode[1]*c; \ + c = hc[1]; \ + tota1 = decode[2]*c; \ + totb1 = decode[3]*c; \ + c = hc[2]; \ + tota2 = decode[4]*c; \ + totb2 = decode[5]*c; \ + c = hc[3]; \ + tota3 = decode[6]*c; \ + totb3 = decode[7]*c; + +#define stbir__4_coeff_continue_from_4( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*2]*c; \ + totb0 += decode[1+(ofs)*2]*c; \ + c = hc[1+(ofs)]; \ + tota1 += decode[2+(ofs)*2]*c; \ + totb1 += decode[3+(ofs)*2]*c; \ + c = hc[2+(ofs)]; \ + tota2 += decode[4+(ofs)*2]*c; \ + totb2 += decode[5+(ofs)*2]*c; \ + c = hc[3+(ofs)]; \ + tota3 += decode[6+(ofs)*2]*c; \ + totb3 += decode[7+(ofs)*2]*c; + +#define stbir__1_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*2] * c; \ + totb0 += decode[1+(ofs)*2] * c; + +#define stbir__2_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*2] * c; \ + totb0 += decode[1+(ofs)*2] * c; \ + c = hc[1+(ofs)]; \ + tota1 += decode[2+(ofs)*2] * c; \ + totb1 += decode[3+(ofs)*2] * c; + +#define stbir__3_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*2] * c; \ + totb0 += decode[1+(ofs)*2] * c; \ + c = hc[1+(ofs)]; \ + tota1 += decode[2+(ofs)*2] * c; \ + totb1 += decode[3+(ofs)*2] * c; \ + c = hc[2+(ofs)]; \ + tota2 += decode[4+(ofs)*2] * c; \ + totb2 += decode[5+(ofs)*2] * c; + +#define stbir__store_output() \ + output[0] = (tota0+tota2)+(tota1+tota3); \ + output[1] = (totb0+totb2)+(totb1+totb3); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 2; + +#endif + +#define STBIR__horizontal_channels 2 +#define STB_IMAGE_RESIZE_DO_HORIZONTALS +#include STBIR__HEADER_FILENAME + + +//================= +// Do 3 channel horizontal routines + +#ifdef STBIR_SIMD + +#define stbir__1_coeff_only() \ + stbir__simdf tot,c,d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1z( c, hc ); \ + stbir__simdf_0123to0001( c, c ); \ + stbir__simdf_load( d, decode ); \ + stbir__simdf_mult( tot, d, c ); + +#define stbir__2_coeff_only() \ + stbir__simdf tot,c,cs,d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_load( d, decode ); \ + stbir__simdf_mult( tot, d, c ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_load( d, decode+3 ); \ + stbir__simdf_madd( tot, tot, d, c ); + +#define stbir__3_coeff_only() \ + stbir__simdf tot,c,d,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_load( d, decode ); \ + stbir__simdf_mult( tot, d, c ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_load( d, decode+3 ); \ + stbir__simdf_madd( tot, tot, d, c ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_load( d, decode+6 ); \ + stbir__simdf_madd( tot, tot, d, c ); + +#define stbir__store_output_tiny() \ + stbir__simdf_store2( output, tot ); \ + stbir__simdf_0123to2301( tot, tot ); \ + stbir__simdf_store1( output+2, tot ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 3; + +#ifdef STBIR_SIMD8 + +// we're loading from the XXXYYY decode by -1 to get the XXXYYY into different halves of the AVX reg fyi +#define stbir__4_coeff_start() \ + stbir__simdf8 tot0,tot1,c,cs; stbir__simdf t; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_mult_mem( tot0, c, decode - 1 ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_mult_mem( tot1, c, decode+6 - 1 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*3 - 1 ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*3 + 6 - 1 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1rep4( t, hc + (ofs) ); \ + stbir__simdf8_madd_mem4( tot0, tot0, t, decode+(ofs)*3 - 1 ); + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) - 2 ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*3 - 1 ); + + #define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*3 - 1 ); \ + stbir__simdf8_0123to2222( t, cs ); \ + stbir__simdf8_madd_mem4( tot1, tot1, t, decode+(ofs)*3 + 6 - 1 ); + +#define stbir__store_output() \ + stbir__simdf8_add( tot0, tot0, tot1 ); \ + stbir__simdf_0123to1230( t, stbir__if_simdf8_cast_to_simdf4( tot0 ) ); \ + stbir__simdf8_add4halves( t, t, tot0 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 3; \ + if ( output < output_end ) \ + { \ + stbir__simdf_store( output-3, t ); \ + continue; \ + } \ + { stbir__simdf tt; stbir__simdf_0123to2301( tt, t ); \ + stbir__simdf_store2( output-3, t ); \ + stbir__simdf_store1( output+2-3, tt ); } \ + break; + + +#else + +#define stbir__4_coeff_start() \ + stbir__simdf tot0,tot1,tot2,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0001( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_0123to1122( c, cs ); \ + stbir__simdf_mult_mem( tot1, c, decode+4 ); \ + stbir__simdf_0123to2333( c, cs ); \ + stbir__simdf_mult_mem( tot2, c, decode+8 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0001( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); \ + stbir__simdf_0123to1122( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*3+4 ); \ + stbir__simdf_0123to2333( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*3+8 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1z( c, hc + (ofs) ); \ + stbir__simdf_0123to0001( c, c ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); + +#define stbir__2_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2z( cs, hc + (ofs) ); \ + stbir__simdf_0123to0001( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); \ + stbir__simdf_0123to1122( c, cs ); \ + stbir__simdf_load2z( d, decode+(ofs)*3+4 ); \ + stbir__simdf_madd( tot1, tot1, c, d ); } + +#define stbir__3_coeff_remnant( ofs ) \ + { stbir__simdf d; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0001( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*3 ); \ + stbir__simdf_0123to1122( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*3+4 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_load1z( d, decode+(ofs)*3+8 ); \ + stbir__simdf_madd( tot2, tot2, c, d ); } + +#define stbir__store_output() \ + stbir__simdf_0123ABCDto3ABx( c, tot0, tot1 ); \ + stbir__simdf_0123ABCDto23Ax( cs, tot1, tot2 ); \ + stbir__simdf_0123to1230( tot2, tot2 ); \ + stbir__simdf_add( tot0, tot0, cs ); \ + stbir__simdf_add( c, c, tot2 ); \ + stbir__simdf_add( tot0, tot0, c ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 3; \ + if ( output < output_end ) \ + { \ + stbir__simdf_store( output-3, tot0 ); \ + continue; \ + } \ + stbir__simdf_0123to2301( tot1, tot0 ); \ + stbir__simdf_store2( output-3, tot0 ); \ + stbir__simdf_store1( output+2-3, tot1 ); \ + break; + +#endif + +#else + +#define stbir__1_coeff_only() \ + float tot0, tot1, tot2, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; + +#define stbir__2_coeff_only() \ + float tot0, tot1, tot2, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; \ + c = hc[1]; \ + tot0 += decode[3]*c; \ + tot1 += decode[4]*c; \ + tot2 += decode[5]*c; + +#define stbir__3_coeff_only() \ + float tot0, tot1, tot2, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; \ + c = hc[1]; \ + tot0 += decode[3]*c; \ + tot1 += decode[4]*c; \ + tot2 += decode[5]*c; \ + c = hc[2]; \ + tot0 += decode[6]*c; \ + tot1 += decode[7]*c; \ + tot2 += decode[8]*c; + +#define stbir__store_output_tiny() \ + output[0] = tot0; \ + output[1] = tot1; \ + output[2] = tot2; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 3; + +#define stbir__4_coeff_start() \ + float tota0,tota1,tota2,totb0,totb1,totb2,totc0,totc1,totc2,totd0,totd1,totd2,c; \ + c = hc[0]; \ + tota0 = decode[0]*c; \ + tota1 = decode[1]*c; \ + tota2 = decode[2]*c; \ + c = hc[1]; \ + totb0 = decode[3]*c; \ + totb1 = decode[4]*c; \ + totb2 = decode[5]*c; \ + c = hc[2]; \ + totc0 = decode[6]*c; \ + totc1 = decode[7]*c; \ + totc2 = decode[8]*c; \ + c = hc[3]; \ + totd0 = decode[9]*c; \ + totd1 = decode[10]*c; \ + totd2 = decode[11]*c; + +#define stbir__4_coeff_continue_from_4( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*3]*c; \ + tota1 += decode[1+(ofs)*3]*c; \ + tota2 += decode[2+(ofs)*3]*c; \ + c = hc[1+(ofs)]; \ + totb0 += decode[3+(ofs)*3]*c; \ + totb1 += decode[4+(ofs)*3]*c; \ + totb2 += decode[5+(ofs)*3]*c; \ + c = hc[2+(ofs)]; \ + totc0 += decode[6+(ofs)*3]*c; \ + totc1 += decode[7+(ofs)*3]*c; \ + totc2 += decode[8+(ofs)*3]*c; \ + c = hc[3+(ofs)]; \ + totd0 += decode[9+(ofs)*3]*c; \ + totd1 += decode[10+(ofs)*3]*c; \ + totd2 += decode[11+(ofs)*3]*c; + +#define stbir__1_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*3]*c; \ + tota1 += decode[1+(ofs)*3]*c; \ + tota2 += decode[2+(ofs)*3]*c; + +#define stbir__2_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*3]*c; \ + tota1 += decode[1+(ofs)*3]*c; \ + tota2 += decode[2+(ofs)*3]*c; \ + c = hc[1+(ofs)]; \ + totb0 += decode[3+(ofs)*3]*c; \ + totb1 += decode[4+(ofs)*3]*c; \ + totb2 += decode[5+(ofs)*3]*c; \ + +#define stbir__3_coeff_remnant( ofs ) \ + c = hc[0+(ofs)]; \ + tota0 += decode[0+(ofs)*3]*c; \ + tota1 += decode[1+(ofs)*3]*c; \ + tota2 += decode[2+(ofs)*3]*c; \ + c = hc[1+(ofs)]; \ + totb0 += decode[3+(ofs)*3]*c; \ + totb1 += decode[4+(ofs)*3]*c; \ + totb2 += decode[5+(ofs)*3]*c; \ + c = hc[2+(ofs)]; \ + totc0 += decode[6+(ofs)*3]*c; \ + totc1 += decode[7+(ofs)*3]*c; \ + totc2 += decode[8+(ofs)*3]*c; + +#define stbir__store_output() \ + output[0] = (tota0+totc0)+(totb0+totd0); \ + output[1] = (tota1+totc1)+(totb1+totd1); \ + output[2] = (tota2+totc2)+(totb2+totd2); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 3; + +#endif + +#define STBIR__horizontal_channels 3 +#define STB_IMAGE_RESIZE_DO_HORIZONTALS +#include STBIR__HEADER_FILENAME + +//================= +// Do 4 channel horizontal routines + +#ifdef STBIR_SIMD + +#define stbir__1_coeff_only() \ + stbir__simdf tot,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1( c, hc ); \ + stbir__simdf_0123to0000( c, c ); \ + stbir__simdf_mult_mem( tot, c, decode ); + +#define stbir__2_coeff_only() \ + stbir__simdf tot,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot, c, decode ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot, tot, c, decode+4 ); + +#define stbir__3_coeff_only() \ + stbir__simdf tot,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot, c, decode ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot, tot, c, decode+4 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot, tot, c, decode+8 ); + +#define stbir__store_output_tiny() \ + stbir__simdf_store( output, tot ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 4; + +#ifdef STBIR_SIMD8 + +#define stbir__4_coeff_start() \ + stbir__simdf8 tot0,c,cs; stbir__simdf t; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_mult_mem( tot0, c, decode ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+8 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4+8 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1rep4( t, hc + (ofs) ); \ + stbir__simdf8_madd_mem4( tot0, tot0, t, decode+(ofs)*4 ); + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) - 2 ); \ + stbir__simdf8_0123to22223333( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); + + #define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00001111( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \ + stbir__simdf8_0123to2222( t, cs ); \ + stbir__simdf8_madd_mem4( tot0, tot0, t, decode+(ofs)*4+8 ); + +#define stbir__store_output() \ + stbir__simdf8_add4halves( t, stbir__if_simdf8_cast_to_simdf4(tot0), tot0 ); \ + stbir__simdf_store( output, t ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 4; + +#else + +#define stbir__4_coeff_start() \ + stbir__simdf tot0,tot1,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_mult_mem( tot1, c, decode+4 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+8 ); \ + stbir__simdf_0123to3333( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+12 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+4 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4+8 ); \ + stbir__simdf_0123to3333( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+12 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1( c, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, c ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+4 ); + +#define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*4+4 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*4+8 ); + +#define stbir__store_output() \ + stbir__simdf_add( tot0, tot0, tot1 ); \ + stbir__simdf_store( output, tot0 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 4; + +#endif + +#else + +#define stbir__1_coeff_only() \ + float p0,p1,p2,p3,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0]; \ + p0 = decode[0] * c; \ + p1 = decode[1] * c; \ + p2 = decode[2] * c; \ + p3 = decode[3] * c; + +#define stbir__2_coeff_only() \ + float p0,p1,p2,p3,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0]; \ + p0 = decode[0] * c; \ + p1 = decode[1] * c; \ + p2 = decode[2] * c; \ + p3 = decode[3] * c; \ + c = hc[1]; \ + p0 += decode[4] * c; \ + p1 += decode[5] * c; \ + p2 += decode[6] * c; \ + p3 += decode[7] * c; + +#define stbir__3_coeff_only() \ + float p0,p1,p2,p3,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0]; \ + p0 = decode[0] * c; \ + p1 = decode[1] * c; \ + p2 = decode[2] * c; \ + p3 = decode[3] * c; \ + c = hc[1]; \ + p0 += decode[4] * c; \ + p1 += decode[5] * c; \ + p2 += decode[6] * c; \ + p3 += decode[7] * c; \ + c = hc[2]; \ + p0 += decode[8] * c; \ + p1 += decode[9] * c; \ + p2 += decode[10] * c; \ + p3 += decode[11] * c; + +#define stbir__store_output_tiny() \ + output[0] = p0; \ + output[1] = p1; \ + output[2] = p2; \ + output[3] = p3; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 4; + +#define stbir__4_coeff_start() \ + float x0,x1,x2,x3,y0,y1,y2,y3,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0]; \ + x0 = decode[0] * c; \ + x1 = decode[1] * c; \ + x2 = decode[2] * c; \ + x3 = decode[3] * c; \ + c = hc[1]; \ + y0 = decode[4] * c; \ + y1 = decode[5] * c; \ + y2 = decode[6] * c; \ + y3 = decode[7] * c; \ + c = hc[2]; \ + x0 += decode[8] * c; \ + x1 += decode[9] * c; \ + x2 += decode[10] * c; \ + x3 += decode[11] * c; \ + c = hc[3]; \ + y0 += decode[12] * c; \ + y1 += decode[13] * c; \ + y2 += decode[14] * c; \ + y3 += decode[15] * c; + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*4] * c; \ + x1 += decode[1+(ofs)*4] * c; \ + x2 += decode[2+(ofs)*4] * c; \ + x3 += decode[3+(ofs)*4] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[4+(ofs)*4] * c; \ + y1 += decode[5+(ofs)*4] * c; \ + y2 += decode[6+(ofs)*4] * c; \ + y3 += decode[7+(ofs)*4] * c; \ + c = hc[2+(ofs)]; \ + x0 += decode[8+(ofs)*4] * c; \ + x1 += decode[9+(ofs)*4] * c; \ + x2 += decode[10+(ofs)*4] * c; \ + x3 += decode[11+(ofs)*4] * c; \ + c = hc[3+(ofs)]; \ + y0 += decode[12+(ofs)*4] * c; \ + y1 += decode[13+(ofs)*4] * c; \ + y2 += decode[14+(ofs)*4] * c; \ + y3 += decode[15+(ofs)*4] * c; + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*4] * c; \ + x1 += decode[1+(ofs)*4] * c; \ + x2 += decode[2+(ofs)*4] * c; \ + x3 += decode[3+(ofs)*4] * c; + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*4] * c; \ + x1 += decode[1+(ofs)*4] * c; \ + x2 += decode[2+(ofs)*4] * c; \ + x3 += decode[3+(ofs)*4] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[4+(ofs)*4] * c; \ + y1 += decode[5+(ofs)*4] * c; \ + y2 += decode[6+(ofs)*4] * c; \ + y3 += decode[7+(ofs)*4] * c; + +#define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*4] * c; \ + x1 += decode[1+(ofs)*4] * c; \ + x2 += decode[2+(ofs)*4] * c; \ + x3 += decode[3+(ofs)*4] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[4+(ofs)*4] * c; \ + y1 += decode[5+(ofs)*4] * c; \ + y2 += decode[6+(ofs)*4] * c; \ + y3 += decode[7+(ofs)*4] * c; \ + c = hc[2+(ofs)]; \ + x0 += decode[8+(ofs)*4] * c; \ + x1 += decode[9+(ofs)*4] * c; \ + x2 += decode[10+(ofs)*4] * c; \ + x3 += decode[11+(ofs)*4] * c; + +#define stbir__store_output() \ + output[0] = x0 + y0; \ + output[1] = x1 + y1; \ + output[2] = x2 + y2; \ + output[3] = x3 + y3; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 4; + +#endif + +#define STBIR__horizontal_channels 4 +#define STB_IMAGE_RESIZE_DO_HORIZONTALS +#include STBIR__HEADER_FILENAME + + + +//================= +// Do 7 channel horizontal routines + +#ifdef STBIR_SIMD + +#define stbir__1_coeff_only() \ + stbir__simdf tot0,tot1,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1( c, hc ); \ + stbir__simdf_0123to0000( c, c ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_mult_mem( tot1, c, decode+3 ); + +#define stbir__2_coeff_only() \ + stbir__simdf tot0,tot1,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_mult_mem( tot1, c, decode+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c,decode+10 ); + +#define stbir__3_coeff_only() \ + stbir__simdf tot0,tot1,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_mult_mem( tot1, c, decode+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+10 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+14 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+17 ); + +#define stbir__store_output_tiny() \ + stbir__simdf_store( output+3, tot1 ); \ + stbir__simdf_store( output, tot0 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 7; + +#ifdef STBIR_SIMD8 + +#define stbir__4_coeff_start() \ + stbir__simdf8 tot0,tot1,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc ); \ + stbir__simdf8_0123to00000000( c, cs ); \ + stbir__simdf8_mult_mem( tot0, c, decode ); \ + stbir__simdf8_0123to11111111( c, cs ); \ + stbir__simdf8_mult_mem( tot1, c, decode+7 ); \ + stbir__simdf8_0123to22222222( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+14 ); \ + stbir__simdf8_0123to33333333( c, cs ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+21 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00000000( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf8_0123to11111111( c, cs ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+7 ); \ + stbir__simdf8_0123to22222222( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); \ + stbir__simdf8_0123to33333333( c, cs ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+21 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load1b( c, hc + (ofs) ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load1b( c, hc + (ofs) ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf8_load1b( c, hc + (ofs)+1 ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+7 ); + +#define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf8_load4b( cs, hc + (ofs) ); \ + stbir__simdf8_0123to00000000( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf8_0123to11111111( c, cs ); \ + stbir__simdf8_madd_mem( tot1, tot1, c, decode+(ofs)*7+7 ); \ + stbir__simdf8_0123to22222222( c, cs ); \ + stbir__simdf8_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); + +#define stbir__store_output() \ + stbir__simdf8_add( tot0, tot0, tot1 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 7; \ + if ( output < output_end ) \ + { \ + stbir__simdf8_store( output-7, tot0 ); \ + continue; \ + } \ + stbir__simdf_store( output-7+3, stbir__simdf_swiz(stbir__simdf8_gettop4(tot0),0,0,1,2) ); \ + stbir__simdf_store( output-7, stbir__if_simdf8_cast_to_simdf4(tot0) ); \ + break; + +#else + +#define stbir__4_coeff_start() \ + stbir__simdf tot0,tot1,tot2,tot3,c,cs; \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_mult_mem( tot0, c, decode ); \ + stbir__simdf_mult_mem( tot1, c, decode+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_mult_mem( tot2, c, decode+7 ); \ + stbir__simdf_mult_mem( tot3, c, decode+10 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+14 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+17 ); \ + stbir__simdf_0123to3333( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+21 ); \ + stbir__simdf_madd_mem( tot3, tot3, c, decode+24 ); + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+7 ); \ + stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+10 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+17 ); \ + stbir__simdf_0123to3333( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+21 ); \ + stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+24 ); + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load1( c, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, c ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \ + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load2( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+7 ); \ + stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+10 ); + +#define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + stbir__simdf_load( cs, hc + (ofs) ); \ + stbir__simdf_0123to0000( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+3 ); \ + stbir__simdf_0123to1111( c, cs ); \ + stbir__simdf_madd_mem( tot2, tot2, c, decode+(ofs)*7+7 ); \ + stbir__simdf_madd_mem( tot3, tot3, c, decode+(ofs)*7+10 ); \ + stbir__simdf_0123to2222( c, cs ); \ + stbir__simdf_madd_mem( tot0, tot0, c, decode+(ofs)*7+14 ); \ + stbir__simdf_madd_mem( tot1, tot1, c, decode+(ofs)*7+17 ); + +#define stbir__store_output() \ + stbir__simdf_add( tot0, tot0, tot2 ); \ + stbir__simdf_add( tot1, tot1, tot3 ); \ + stbir__simdf_store( output+3, tot1 ); \ + stbir__simdf_store( output, tot0 ); \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 7; + +#endif + +#else + +#define stbir__1_coeff_only() \ + float tot0, tot1, tot2, tot3, tot4, tot5, tot6, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; \ + tot3 = decode[3]*c; \ + tot4 = decode[4]*c; \ + tot5 = decode[5]*c; \ + tot6 = decode[6]*c; + +#define stbir__2_coeff_only() \ + float tot0, tot1, tot2, tot3, tot4, tot5, tot6, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; \ + tot3 = decode[3]*c; \ + tot4 = decode[4]*c; \ + tot5 = decode[5]*c; \ + tot6 = decode[6]*c; \ + c = hc[1]; \ + tot0 += decode[7]*c; \ + tot1 += decode[8]*c; \ + tot2 += decode[9]*c; \ + tot3 += decode[10]*c; \ + tot4 += decode[11]*c; \ + tot5 += decode[12]*c; \ + tot6 += decode[13]*c; \ + +#define stbir__3_coeff_only() \ + float tot0, tot1, tot2, tot3, tot4, tot5, tot6, c; \ + c = hc[0]; \ + tot0 = decode[0]*c; \ + tot1 = decode[1]*c; \ + tot2 = decode[2]*c; \ + tot3 = decode[3]*c; \ + tot4 = decode[4]*c; \ + tot5 = decode[5]*c; \ + tot6 = decode[6]*c; \ + c = hc[1]; \ + tot0 += decode[7]*c; \ + tot1 += decode[8]*c; \ + tot2 += decode[9]*c; \ + tot3 += decode[10]*c; \ + tot4 += decode[11]*c; \ + tot5 += decode[12]*c; \ + tot6 += decode[13]*c; \ + c = hc[2]; \ + tot0 += decode[14]*c; \ + tot1 += decode[15]*c; \ + tot2 += decode[16]*c; \ + tot3 += decode[17]*c; \ + tot4 += decode[18]*c; \ + tot5 += decode[19]*c; \ + tot6 += decode[20]*c; \ + +#define stbir__store_output_tiny() \ + output[0] = tot0; \ + output[1] = tot1; \ + output[2] = tot2; \ + output[3] = tot3; \ + output[4] = tot4; \ + output[5] = tot5; \ + output[6] = tot6; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 7; + +#define stbir__4_coeff_start() \ + float x0,x1,x2,x3,x4,x5,x6,y0,y1,y2,y3,y4,y5,y6,c; \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0]; \ + x0 = decode[0] * c; \ + x1 = decode[1] * c; \ + x2 = decode[2] * c; \ + x3 = decode[3] * c; \ + x4 = decode[4] * c; \ + x5 = decode[5] * c; \ + x6 = decode[6] * c; \ + c = hc[1]; \ + y0 = decode[7] * c; \ + y1 = decode[8] * c; \ + y2 = decode[9] * c; \ + y3 = decode[10] * c; \ + y4 = decode[11] * c; \ + y5 = decode[12] * c; \ + y6 = decode[13] * c; \ + c = hc[2]; \ + x0 += decode[14] * c; \ + x1 += decode[15] * c; \ + x2 += decode[16] * c; \ + x3 += decode[17] * c; \ + x4 += decode[18] * c; \ + x5 += decode[19] * c; \ + x6 += decode[20] * c; \ + c = hc[3]; \ + y0 += decode[21] * c; \ + y1 += decode[22] * c; \ + y2 += decode[23] * c; \ + y3 += decode[24] * c; \ + y4 += decode[25] * c; \ + y5 += decode[26] * c; \ + y6 += decode[27] * c; + +#define stbir__4_coeff_continue_from_4( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*7] * c; \ + x1 += decode[1+(ofs)*7] * c; \ + x2 += decode[2+(ofs)*7] * c; \ + x3 += decode[3+(ofs)*7] * c; \ + x4 += decode[4+(ofs)*7] * c; \ + x5 += decode[5+(ofs)*7] * c; \ + x6 += decode[6+(ofs)*7] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[7+(ofs)*7] * c; \ + y1 += decode[8+(ofs)*7] * c; \ + y2 += decode[9+(ofs)*7] * c; \ + y3 += decode[10+(ofs)*7] * c; \ + y4 += decode[11+(ofs)*7] * c; \ + y5 += decode[12+(ofs)*7] * c; \ + y6 += decode[13+(ofs)*7] * c; \ + c = hc[2+(ofs)]; \ + x0 += decode[14+(ofs)*7] * c; \ + x1 += decode[15+(ofs)*7] * c; \ + x2 += decode[16+(ofs)*7] * c; \ + x3 += decode[17+(ofs)*7] * c; \ + x4 += decode[18+(ofs)*7] * c; \ + x5 += decode[19+(ofs)*7] * c; \ + x6 += decode[20+(ofs)*7] * c; \ + c = hc[3+(ofs)]; \ + y0 += decode[21+(ofs)*7] * c; \ + y1 += decode[22+(ofs)*7] * c; \ + y2 += decode[23+(ofs)*7] * c; \ + y3 += decode[24+(ofs)*7] * c; \ + y4 += decode[25+(ofs)*7] * c; \ + y5 += decode[26+(ofs)*7] * c; \ + y6 += decode[27+(ofs)*7] * c; + +#define stbir__1_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*7] * c; \ + x1 += decode[1+(ofs)*7] * c; \ + x2 += decode[2+(ofs)*7] * c; \ + x3 += decode[3+(ofs)*7] * c; \ + x4 += decode[4+(ofs)*7] * c; \ + x5 += decode[5+(ofs)*7] * c; \ + x6 += decode[6+(ofs)*7] * c; \ + +#define stbir__2_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*7] * c; \ + x1 += decode[1+(ofs)*7] * c; \ + x2 += decode[2+(ofs)*7] * c; \ + x3 += decode[3+(ofs)*7] * c; \ + x4 += decode[4+(ofs)*7] * c; \ + x5 += decode[5+(ofs)*7] * c; \ + x6 += decode[6+(ofs)*7] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[7+(ofs)*7] * c; \ + y1 += decode[8+(ofs)*7] * c; \ + y2 += decode[9+(ofs)*7] * c; \ + y3 += decode[10+(ofs)*7] * c; \ + y4 += decode[11+(ofs)*7] * c; \ + y5 += decode[12+(ofs)*7] * c; \ + y6 += decode[13+(ofs)*7] * c; \ + +#define stbir__3_coeff_remnant( ofs ) \ + STBIR_SIMD_NO_UNROLL(decode); \ + c = hc[0+(ofs)]; \ + x0 += decode[0+(ofs)*7] * c; \ + x1 += decode[1+(ofs)*7] * c; \ + x2 += decode[2+(ofs)*7] * c; \ + x3 += decode[3+(ofs)*7] * c; \ + x4 += decode[4+(ofs)*7] * c; \ + x5 += decode[5+(ofs)*7] * c; \ + x6 += decode[6+(ofs)*7] * c; \ + c = hc[1+(ofs)]; \ + y0 += decode[7+(ofs)*7] * c; \ + y1 += decode[8+(ofs)*7] * c; \ + y2 += decode[9+(ofs)*7] * c; \ + y3 += decode[10+(ofs)*7] * c; \ + y4 += decode[11+(ofs)*7] * c; \ + y5 += decode[12+(ofs)*7] * c; \ + y6 += decode[13+(ofs)*7] * c; \ + c = hc[2+(ofs)]; \ + x0 += decode[14+(ofs)*7] * c; \ + x1 += decode[15+(ofs)*7] * c; \ + x2 += decode[16+(ofs)*7] * c; \ + x3 += decode[17+(ofs)*7] * c; \ + x4 += decode[18+(ofs)*7] * c; \ + x5 += decode[19+(ofs)*7] * c; \ + x6 += decode[20+(ofs)*7] * c; \ + +#define stbir__store_output() \ + output[0] = x0 + y0; \ + output[1] = x1 + y1; \ + output[2] = x2 + y2; \ + output[3] = x3 + y3; \ + output[4] = x4 + y4; \ + output[5] = x5 + y5; \ + output[6] = x6 + y6; \ + horizontal_coefficients += coefficient_width; \ + ++horizontal_contributors; \ + output += 7; + +#endif + +#define STBIR__horizontal_channels 7 +#define STB_IMAGE_RESIZE_DO_HORIZONTALS +#include STBIR__HEADER_FILENAME + + +// include all of the vertical resamplers (both scatter and gather versions) + +#define STBIR__vertical_channels 1 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 1 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 2 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 2 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 3 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 3 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 4 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 4 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 5 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 5 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 6 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 6 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 7 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 7 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 8 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#include STBIR__HEADER_FILENAME + +#define STBIR__vertical_channels 8 +#define STB_IMAGE_RESIZE_DO_VERTICALS +#define STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#include STBIR__HEADER_FILENAME + +typedef void STBIR_VERTICAL_GATHERFUNC( float * output, float const * coeffs, float const ** inputs, float const * input0_end ); + +static STBIR_VERTICAL_GATHERFUNC * stbir__vertical_gathers[ 8 ] = +{ + stbir__vertical_gather_with_1_coeffs,stbir__vertical_gather_with_2_coeffs,stbir__vertical_gather_with_3_coeffs,stbir__vertical_gather_with_4_coeffs,stbir__vertical_gather_with_5_coeffs,stbir__vertical_gather_with_6_coeffs,stbir__vertical_gather_with_7_coeffs,stbir__vertical_gather_with_8_coeffs +}; + +static STBIR_VERTICAL_GATHERFUNC * stbir__vertical_gathers_continues[ 8 ] = +{ + stbir__vertical_gather_with_1_coeffs_cont,stbir__vertical_gather_with_2_coeffs_cont,stbir__vertical_gather_with_3_coeffs_cont,stbir__vertical_gather_with_4_coeffs_cont,stbir__vertical_gather_with_5_coeffs_cont,stbir__vertical_gather_with_6_coeffs_cont,stbir__vertical_gather_with_7_coeffs_cont,stbir__vertical_gather_with_8_coeffs_cont +}; + +typedef void STBIR_VERTICAL_SCATTERFUNC( float ** outputs, float const * coeffs, float const * input, float const * input_end ); + +static STBIR_VERTICAL_SCATTERFUNC * stbir__vertical_scatter_sets[ 8 ] = +{ + stbir__vertical_scatter_with_1_coeffs,stbir__vertical_scatter_with_2_coeffs,stbir__vertical_scatter_with_3_coeffs,stbir__vertical_scatter_with_4_coeffs,stbir__vertical_scatter_with_5_coeffs,stbir__vertical_scatter_with_6_coeffs,stbir__vertical_scatter_with_7_coeffs,stbir__vertical_scatter_with_8_coeffs +}; + +static STBIR_VERTICAL_SCATTERFUNC * stbir__vertical_scatter_blends[ 8 ] = +{ + stbir__vertical_scatter_with_1_coeffs_cont,stbir__vertical_scatter_with_2_coeffs_cont,stbir__vertical_scatter_with_3_coeffs_cont,stbir__vertical_scatter_with_4_coeffs_cont,stbir__vertical_scatter_with_5_coeffs_cont,stbir__vertical_scatter_with_6_coeffs_cont,stbir__vertical_scatter_with_7_coeffs_cont,stbir__vertical_scatter_with_8_coeffs_cont +}; + + +static void stbir__encode_scanline( stbir__info const * stbir_info, void *output_buffer_data, float * encode_buffer, int row STBIR_ONLY_PROFILE_GET_SPLIT_INFO ) +{ + int num_pixels = stbir_info->horizontal.scale_info.output_sub_size; + int channels = stbir_info->channels; + int width_times_channels = num_pixels * channels; + void * output_buffer; + + // un-alpha weight if we need to + if ( stbir_info->alpha_unweight ) + { + STBIR_PROFILE_START( unalpha ); + stbir_info->alpha_unweight( encode_buffer, width_times_channels ); + STBIR_PROFILE_END( unalpha ); + } + + // write directly into output by default + output_buffer = output_buffer_data; + + // if we have an output callback, we first convert the decode buffer in place (and then hand that to the callback) + if ( stbir_info->out_pixels_cb ) + output_buffer = encode_buffer; + + STBIR_PROFILE_START( encode ); + // convert into the output buffer + stbir_info->encode_pixels( output_buffer, width_times_channels, encode_buffer ); + STBIR_PROFILE_END( encode ); + + // if we have an output callback, call it to send the data + if ( stbir_info->out_pixels_cb ) + stbir_info->out_pixels_cb( output_buffer_data, num_pixels, row, stbir_info->user_data ); +} + + +// Get the ring buffer pointer for an index +static float* stbir__get_ring_buffer_entry(stbir__info const * stbir_info, stbir__per_split_info const * split_info, int index ) +{ + STBIR_ASSERT( index < stbir_info->ring_buffer_num_entries ); + + #ifdef STBIR__SEPARATE_ALLOCATIONS + return split_info->ring_buffers[ index ]; + #else + return (float*) ( ( (char*) split_info->ring_buffer ) + ( index * stbir_info->ring_buffer_length_bytes ) ); + #endif +} + +// Get the specified scan line from the ring buffer +static float* stbir__get_ring_buffer_scanline(stbir__info const * stbir_info, stbir__per_split_info const * split_info, int get_scanline) +{ + int ring_buffer_index = (split_info->ring_buffer_begin_index + (get_scanline - split_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries; + return stbir__get_ring_buffer_entry( stbir_info, split_info, ring_buffer_index ); +} + +static void stbir__resample_horizontal_gather(stbir__info const * stbir_info, float* output_buffer, float const * input_buffer STBIR_ONLY_PROFILE_GET_SPLIT_INFO ) +{ + float const * decode_buffer = input_buffer - ( stbir_info->scanline_extents.conservative.n0 * stbir_info->effective_channels ); + + STBIR_PROFILE_START( horizontal ); + if ( ( stbir_info->horizontal.filter_enum == STBIR_FILTER_POINT_SAMPLE ) && ( stbir_info->horizontal.scale_info.scale == 1.0f ) ) + STBIR_MEMCPY( output_buffer, input_buffer, stbir_info->horizontal.scale_info.output_sub_size * sizeof( float ) * stbir_info->effective_channels ); + else + stbir_info->horizontal_gather_channels( output_buffer, stbir_info->horizontal.scale_info.output_sub_size, decode_buffer, stbir_info->horizontal.contributors, stbir_info->horizontal.coefficients, stbir_info->horizontal.coefficient_width ); + STBIR_PROFILE_END( horizontal ); +} + +static void stbir__resample_vertical_gather(stbir__info const * stbir_info, stbir__per_split_info* split_info, int n, int contrib_n0, int contrib_n1, float const * vertical_coefficients ) +{ + float* encode_buffer = split_info->vertical_buffer; + float* decode_buffer = split_info->decode_buffer; + int vertical_first = stbir_info->vertical_first; + int width = (vertical_first) ? ( stbir_info->scanline_extents.conservative.n1-stbir_info->scanline_extents.conservative.n0+1 ) : stbir_info->horizontal.scale_info.output_sub_size; + int width_times_channels = stbir_info->effective_channels * width; + + STBIR_ASSERT( stbir_info->vertical.is_gather ); + + // loop over the contributing scanlines and scale into the buffer + STBIR_PROFILE_START( vertical ); + { + int k = 0, total = contrib_n1 - contrib_n0 + 1; + STBIR_ASSERT( total > 0 ); + do { + float const * inputs[8]; + int i, cnt = total; if ( cnt > 8 ) cnt = 8; + for( i = 0 ; i < cnt ; i++ ) + inputs[ i ] = stbir__get_ring_buffer_scanline(stbir_info, split_info, k+i+contrib_n0 ); + + // call the N scanlines at a time function (up to 8 scanlines of blending at once) + ((k==0)?stbir__vertical_gathers:stbir__vertical_gathers_continues)[cnt-1]( (vertical_first) ? decode_buffer : encode_buffer, vertical_coefficients + k, inputs, inputs[0] + width_times_channels ); + k += cnt; + total -= cnt; + } while ( total ); + } + STBIR_PROFILE_END( vertical ); + + if ( vertical_first ) + { + // Now resample the gathered vertical data in the horizontal axis into the encode buffer + stbir__resample_horizontal_gather(stbir_info, encode_buffer, decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + } + + stbir__encode_scanline( stbir_info, ( (char *) stbir_info->output_data ) + ((ptrdiff_t)n * (ptrdiff_t)stbir_info->output_stride_bytes), + encode_buffer, n STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); +} + +static void stbir__decode_and_resample_for_vertical_gather_loop(stbir__info const * stbir_info, stbir__per_split_info* split_info, int n) +{ + int ring_buffer_index; + float* ring_buffer; + + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline( stbir_info, n, split_info->decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // update new end scanline + split_info->ring_buffer_last_scanline = n; + + // get ring buffer + ring_buffer_index = (split_info->ring_buffer_begin_index + (split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline)) % stbir_info->ring_buffer_num_entries; + ring_buffer = stbir__get_ring_buffer_entry(stbir_info, split_info, ring_buffer_index); + + // Now resample it into the ring buffer. + stbir__resample_horizontal_gather( stbir_info, ring_buffer, split_info->decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // Now it's sitting in the ring buffer ready to be used as source for the vertical sampling. +} + +static void stbir__vertical_gather_loop( stbir__info const * stbir_info, stbir__per_split_info* split_info, int split_count ) +{ + int y, start_output_y, end_output_y; + stbir__contributors* vertical_contributors = stbir_info->vertical.contributors; + float const * vertical_coefficients = stbir_info->vertical.coefficients; + + STBIR_ASSERT( stbir_info->vertical.is_gather ); + + start_output_y = split_info->start_output_y; + end_output_y = split_info[split_count-1].end_output_y; + + vertical_contributors += start_output_y; + vertical_coefficients += start_output_y * stbir_info->vertical.coefficient_width; + + // initialize the ring buffer for gathering + split_info->ring_buffer_begin_index = 0; + split_info->ring_buffer_first_scanline = stbir_info->vertical.extent_info.lowest; + split_info->ring_buffer_last_scanline = split_info->ring_buffer_first_scanline - 1; // means "empty" + + for (y = start_output_y; y < end_output_y; y++) + { + int in_first_scanline, in_last_scanline; + + in_first_scanline = vertical_contributors->n0; + in_last_scanline = vertical_contributors->n1; + + // make sure the indexing hasn't broken + STBIR_ASSERT( in_first_scanline >= split_info->ring_buffer_first_scanline ); + + // Load in new scanlines + while (in_last_scanline > split_info->ring_buffer_last_scanline) + { + STBIR_ASSERT( ( split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline + 1 ) <= stbir_info->ring_buffer_num_entries ); + + // make sure there was room in the ring buffer when we add new scanlines + if ( ( split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline + 1 ) == stbir_info->ring_buffer_num_entries ) + { + split_info->ring_buffer_first_scanline++; + split_info->ring_buffer_begin_index++; + } + + if ( stbir_info->vertical_first ) + { + float * ring_buffer = stbir__get_ring_buffer_scanline( stbir_info, split_info, ++split_info->ring_buffer_last_scanline ); + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline( stbir_info, split_info->ring_buffer_last_scanline, ring_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + } + else + { + stbir__decode_and_resample_for_vertical_gather_loop(stbir_info, split_info, split_info->ring_buffer_last_scanline + 1); + } + } + + // Now all buffers should be ready to write a row of vertical sampling, so do it. + stbir__resample_vertical_gather(stbir_info, split_info, y, in_first_scanline, in_last_scanline, vertical_coefficients ); + + ++vertical_contributors; + vertical_coefficients += stbir_info->vertical.coefficient_width; + } +} + +#define STBIR__FLOAT_EMPTY_MARKER 3.0e+38F +#define STBIR__FLOAT_BUFFER_IS_EMPTY(ptr) ((ptr)[0]==STBIR__FLOAT_EMPTY_MARKER) + +static void stbir__encode_first_scanline_from_scatter(stbir__info const * stbir_info, stbir__per_split_info* split_info) +{ + // evict a scanline out into the output buffer + float* ring_buffer_entry = stbir__get_ring_buffer_entry(stbir_info, split_info, split_info->ring_buffer_begin_index ); + + // dump the scanline out + stbir__encode_scanline( stbir_info, ( (char *)stbir_info->output_data ) + ( (ptrdiff_t)split_info->ring_buffer_first_scanline * (ptrdiff_t)stbir_info->output_stride_bytes ), ring_buffer_entry, split_info->ring_buffer_first_scanline STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // mark it as empty + ring_buffer_entry[ 0 ] = STBIR__FLOAT_EMPTY_MARKER; + + // advance the first scanline + split_info->ring_buffer_first_scanline++; + if ( ++split_info->ring_buffer_begin_index == stbir_info->ring_buffer_num_entries ) + split_info->ring_buffer_begin_index = 0; +} + +static void stbir__horizontal_resample_and_encode_first_scanline_from_scatter(stbir__info const * stbir_info, stbir__per_split_info* split_info) +{ + // evict a scanline out into the output buffer + + float* ring_buffer_entry = stbir__get_ring_buffer_entry(stbir_info, split_info, split_info->ring_buffer_begin_index ); + + // Now resample it into the buffer. + stbir__resample_horizontal_gather( stbir_info, split_info->vertical_buffer, ring_buffer_entry STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // dump the scanline out + stbir__encode_scanline( stbir_info, ( (char *)stbir_info->output_data ) + ( (ptrdiff_t)split_info->ring_buffer_first_scanline * (ptrdiff_t)stbir_info->output_stride_bytes ), split_info->vertical_buffer, split_info->ring_buffer_first_scanline STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // mark it as empty + ring_buffer_entry[ 0 ] = STBIR__FLOAT_EMPTY_MARKER; + + // advance the first scanline + split_info->ring_buffer_first_scanline++; + if ( ++split_info->ring_buffer_begin_index == stbir_info->ring_buffer_num_entries ) + split_info->ring_buffer_begin_index = 0; +} + +static void stbir__resample_vertical_scatter(stbir__info const * stbir_info, stbir__per_split_info* split_info, int n0, int n1, float const * vertical_coefficients, float const * vertical_buffer, float const * vertical_buffer_end ) +{ + STBIR_ASSERT( !stbir_info->vertical.is_gather ); + + STBIR_PROFILE_START( vertical ); + { + int k = 0, total = n1 - n0 + 1; + STBIR_ASSERT( total > 0 ); + do { + float * outputs[8]; + int i, n = total; if ( n > 8 ) n = 8; + for( i = 0 ; i < n ; i++ ) + { + outputs[ i ] = stbir__get_ring_buffer_scanline(stbir_info, split_info, k+i+n0 ); + if ( ( i ) && ( STBIR__FLOAT_BUFFER_IS_EMPTY( outputs[i] ) != STBIR__FLOAT_BUFFER_IS_EMPTY( outputs[0] ) ) ) // make sure runs are of the same type + { + n = i; + break; + } + } + // call the scatter to N scanlines at a time function (up to 8 scanlines of scattering at once) + ((STBIR__FLOAT_BUFFER_IS_EMPTY( outputs[0] ))?stbir__vertical_scatter_sets:stbir__vertical_scatter_blends)[n-1]( outputs, vertical_coefficients + k, vertical_buffer, vertical_buffer_end ); + k += n; + total -= n; + } while ( total ); + } + + STBIR_PROFILE_END( vertical ); +} + +typedef void stbir__handle_scanline_for_scatter_func(stbir__info const * stbir_info, stbir__per_split_info* split_info); + +static void stbir__vertical_scatter_loop( stbir__info const * stbir_info, stbir__per_split_info* split_info, int split_count ) +{ + int y, start_output_y, end_output_y, start_input_y, end_input_y; + stbir__contributors* vertical_contributors = stbir_info->vertical.contributors; + float const * vertical_coefficients = stbir_info->vertical.coefficients; + stbir__handle_scanline_for_scatter_func * handle_scanline_for_scatter; + void * scanline_scatter_buffer; + void * scanline_scatter_buffer_end; + int on_first_input_y, last_input_y; + + STBIR_ASSERT( !stbir_info->vertical.is_gather ); + + start_output_y = split_info->start_output_y; + end_output_y = split_info[split_count-1].end_output_y; // may do multiple split counts + + start_input_y = split_info->start_input_y; + end_input_y = split_info[split_count-1].end_input_y; + + // adjust for starting offset start_input_y + y = start_input_y + stbir_info->vertical.filter_pixel_margin; + vertical_contributors += y ; + vertical_coefficients += stbir_info->vertical.coefficient_width * y; + + if ( stbir_info->vertical_first ) + { + handle_scanline_for_scatter = stbir__horizontal_resample_and_encode_first_scanline_from_scatter; + scanline_scatter_buffer = split_info->decode_buffer; + scanline_scatter_buffer_end = ( (char*) scanline_scatter_buffer ) + sizeof( float ) * stbir_info->effective_channels * (stbir_info->scanline_extents.conservative.n1-stbir_info->scanline_extents.conservative.n0+1); + } + else + { + handle_scanline_for_scatter = stbir__encode_first_scanline_from_scatter; + scanline_scatter_buffer = split_info->vertical_buffer; + scanline_scatter_buffer_end = ( (char*) scanline_scatter_buffer ) + sizeof( float ) * stbir_info->effective_channels * stbir_info->horizontal.scale_info.output_sub_size; + } + + // initialize the ring buffer for scattering + split_info->ring_buffer_first_scanline = start_output_y; + split_info->ring_buffer_last_scanline = -1; + split_info->ring_buffer_begin_index = -1; + + // mark all the buffers as empty to start + for( y = 0 ; y < stbir_info->ring_buffer_num_entries ; y++ ) + stbir__get_ring_buffer_entry( stbir_info, split_info, y )[0] = STBIR__FLOAT_EMPTY_MARKER; // only used on scatter + + // do the loop in input space + on_first_input_y = 1; last_input_y = start_input_y; + for (y = start_input_y ; y < end_input_y; y++) + { + int out_first_scanline, out_last_scanline; + + out_first_scanline = vertical_contributors->n0; + out_last_scanline = vertical_contributors->n1; + + STBIR_ASSERT(out_last_scanline - out_first_scanline + 1 <= stbir_info->ring_buffer_num_entries); + + if ( ( out_last_scanline >= out_first_scanline ) && ( ( ( out_first_scanline >= start_output_y ) && ( out_first_scanline < end_output_y ) ) || ( ( out_last_scanline >= start_output_y ) && ( out_last_scanline < end_output_y ) ) ) ) + { + float const * vc = vertical_coefficients; + + // keep track of the range actually seen for the next resize + last_input_y = y; + if ( ( on_first_input_y ) && ( y > start_input_y ) ) + split_info->start_input_y = y; + on_first_input_y = 0; + + // clip the region + if ( out_first_scanline < start_output_y ) + { + vc += start_output_y - out_first_scanline; + out_first_scanline = start_output_y; + } + + if ( out_last_scanline >= end_output_y ) + out_last_scanline = end_output_y - 1; + + // if very first scanline, init the index + if (split_info->ring_buffer_begin_index < 0) + split_info->ring_buffer_begin_index = out_first_scanline - start_output_y; + + STBIR_ASSERT( split_info->ring_buffer_begin_index <= out_first_scanline ); + + // Decode the nth scanline from the source image into the decode buffer. + stbir__decode_scanline( stbir_info, y, split_info->decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // When horizontal first, we resample horizontally into the vertical buffer before we scatter it out + if ( !stbir_info->vertical_first ) + stbir__resample_horizontal_gather( stbir_info, split_info->vertical_buffer, split_info->decode_buffer STBIR_ONLY_PROFILE_SET_SPLIT_INFO ); + + // Now it's sitting in the buffer ready to be distributed into the ring buffers. + + // evict from the ringbuffer, if we need are full + if ( ( ( split_info->ring_buffer_last_scanline - split_info->ring_buffer_first_scanline + 1 ) == stbir_info->ring_buffer_num_entries ) && + ( out_last_scanline > split_info->ring_buffer_last_scanline ) ) + handle_scanline_for_scatter( stbir_info, split_info ); + + // Now the horizontal buffer is ready to write to all ring buffer rows, so do it. + stbir__resample_vertical_scatter(stbir_info, split_info, out_first_scanline, out_last_scanline, vc, (float*)scanline_scatter_buffer, (float*)scanline_scatter_buffer_end ); + + // update the end of the buffer + if ( out_last_scanline > split_info->ring_buffer_last_scanline ) + split_info->ring_buffer_last_scanline = out_last_scanline; + } + ++vertical_contributors; + vertical_coefficients += stbir_info->vertical.coefficient_width; + } + + // now evict the scanlines that are left over in the ring buffer + while ( split_info->ring_buffer_first_scanline < end_output_y ) + handle_scanline_for_scatter(stbir_info, split_info); + + // update the end_input_y if we do multiple resizes with the same data + ++last_input_y; + for( y = 0 ; y < split_count; y++ ) + if ( split_info[y].end_input_y > last_input_y ) + split_info[y].end_input_y = last_input_y; +} + + +static stbir__kernel_callback * stbir__builtin_kernels[] = { 0, stbir__filter_trapezoid, stbir__filter_triangle, stbir__filter_cubic, stbir__filter_catmullrom, stbir__filter_mitchell, stbir__filter_point }; +static stbir__support_callback * stbir__builtin_supports[] = { 0, stbir__support_trapezoid, stbir__support_one, stbir__support_two, stbir__support_two, stbir__support_two, stbir__support_zeropoint5 }; + +static void stbir__set_sampler(stbir__sampler * samp, stbir_filter filter, stbir__kernel_callback * kernel, stbir__support_callback * support, stbir_edge edge, stbir__scale_info * scale_info, int always_gather, void * user_data ) +{ + // set filter + if (filter == 0) + { + filter = STBIR_DEFAULT_FILTER_DOWNSAMPLE; // default to downsample + if (scale_info->scale >= ( 1.0f - stbir__small_float ) ) + { + if ( (scale_info->scale <= ( 1.0f + stbir__small_float ) ) && ( STBIR_CEILF(scale_info->pixel_shift) == scale_info->pixel_shift ) ) + filter = STBIR_FILTER_POINT_SAMPLE; + else + filter = STBIR_DEFAULT_FILTER_UPSAMPLE; + } + } + samp->filter_enum = filter; + + STBIR_ASSERT(samp->filter_enum != 0); + STBIR_ASSERT((unsigned)samp->filter_enum < STBIR_FILTER_OTHER); + samp->filter_kernel = stbir__builtin_kernels[ filter ]; + samp->filter_support = stbir__builtin_supports[ filter ]; + + if ( kernel && support ) + { + samp->filter_kernel = kernel; + samp->filter_support = support; + samp->filter_enum = STBIR_FILTER_OTHER; + } + + samp->edge = edge; + samp->filter_pixel_width = stbir__get_filter_pixel_width (samp->filter_support, scale_info->scale, user_data ); + // Gather is always better, but in extreme downsamples, you have to most or all of the data in memory + // For horizontal, we always have all the pixels, so we always use gather here (always_gather==1). + // For vertical, we use gather if scaling up (which means we will have samp->filter_pixel_width + // scanlines in memory at once). + samp->is_gather = 0; + if ( scale_info->scale >= ( 1.0f - stbir__small_float ) ) + samp->is_gather = 1; + else if ( ( always_gather ) || ( samp->filter_pixel_width <= STBIR_FORCE_GATHER_FILTER_SCANLINES_AMOUNT ) ) + samp->is_gather = 2; + + // pre calculate stuff based on the above + samp->coefficient_width = stbir__get_coefficient_width(samp, samp->is_gather, user_data); + + if ( edge == STBIR_EDGE_WRAP ) + if ( samp->filter_pixel_width > ( scale_info->input_full_size * 2 ) ) // this can only happen when shrinking to a single pixel + samp->filter_pixel_width = scale_info->input_full_size * 2; + + // This is how much to expand buffers to account for filters seeking outside + // the image boundaries. + samp->filter_pixel_margin = samp->filter_pixel_width / 2; + + samp->num_contributors = stbir__get_contributors(samp, samp->is_gather); + samp->contributors_size = samp->num_contributors * sizeof(stbir__contributors); + samp->coefficients_size = samp->num_contributors * samp->coefficient_width * sizeof(float) + sizeof(float); // extra sizeof(float) is padding + + samp->gather_prescatter_contributors = 0; + samp->gather_prescatter_coefficients = 0; + if ( samp->is_gather == 0 ) + { + samp->gather_prescatter_coefficient_width = samp->filter_pixel_width; + samp->gather_prescatter_num_contributors = stbir__get_contributors(samp, 2); + samp->gather_prescatter_contributors_size = samp->gather_prescatter_num_contributors * sizeof(stbir__contributors); + samp->gather_prescatter_coefficients_size = samp->gather_prescatter_num_contributors * samp->gather_prescatter_coefficient_width * sizeof(float); + } +} + +static void stbir__get_conservative_extents( stbir__sampler * samp, stbir__contributors * range, void * user_data ) +{ + float scale = samp->scale_info.scale; + float out_shift = samp->scale_info.pixel_shift; + stbir__support_callback * support = samp->filter_support; + int input_full_size = samp->scale_info.input_full_size; + stbir_edge edge = samp->edge; + float inv_scale = samp->scale_info.inv_scale; + + STBIR_ASSERT( samp->is_gather != 0 ); + + if ( samp->is_gather == 1 ) + { + int in_first_pixel, in_last_pixel; + float out_filter_radius = support(inv_scale, user_data) * scale; + + stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, 0.5, out_filter_radius, inv_scale, out_shift, input_full_size, edge ); + range->n0 = in_first_pixel; + stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, ( (float)(samp->scale_info.output_sub_size-1) ) + 0.5f, out_filter_radius, inv_scale, out_shift, input_full_size, edge ); + range->n1 = in_last_pixel; + } + else if ( samp->is_gather == 2 ) // downsample gather, refine + { + float in_pixels_radius = support(scale, user_data) * inv_scale; + int filter_pixel_margin = samp->filter_pixel_margin; + int output_sub_size = samp->scale_info.output_sub_size; + int input_end; + int n; + int in_first_pixel, in_last_pixel; + + // get a conservative area of the input range + stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, 0, 0, inv_scale, out_shift, input_full_size, edge ); + range->n0 = in_first_pixel; + stbir__calculate_in_pixel_range( &in_first_pixel, &in_last_pixel, (float)output_sub_size, 0, inv_scale, out_shift, input_full_size, edge ); + range->n1 = in_last_pixel; + + // now go through the margin to the start of area to find bottom + n = range->n0 + 1; + input_end = -filter_pixel_margin; + while( n >= input_end ) + { + int out_first_pixel, out_last_pixel; + stbir__calculate_out_pixel_range( &out_first_pixel, &out_last_pixel, ((float)n)+0.5f, in_pixels_radius, scale, out_shift, output_sub_size ); + if ( out_first_pixel > out_last_pixel ) + break; + + if ( ( out_first_pixel < output_sub_size ) || ( out_last_pixel >= 0 ) ) + range->n0 = n; + --n; + } + + // now go through the end of the area through the margin to find top + n = range->n1 - 1; + input_end = n + 1 + filter_pixel_margin; + while( n <= input_end ) + { + int out_first_pixel, out_last_pixel; + stbir__calculate_out_pixel_range( &out_first_pixel, &out_last_pixel, ((float)n)+0.5f, in_pixels_radius, scale, out_shift, output_sub_size ); + if ( out_first_pixel > out_last_pixel ) + break; + if ( ( out_first_pixel < output_sub_size ) || ( out_last_pixel >= 0 ) ) + range->n1 = n; + ++n; + } + } + + if ( samp->edge == STBIR_EDGE_WRAP ) + { + // if we are wrapping, and we are very close to the image size (so the edges might merge), just use the scanline up to the edge + if ( ( range->n0 > 0 ) && ( range->n1 >= input_full_size ) ) + { + int marg = range->n1 - input_full_size + 1; + if ( ( marg + STBIR__MERGE_RUNS_PIXEL_THRESHOLD ) >= range->n0 ) + range->n0 = 0; + } + if ( ( range->n0 < 0 ) && ( range->n1 < (input_full_size-1) ) ) + { + int marg = -range->n0; + if ( ( input_full_size - marg - STBIR__MERGE_RUNS_PIXEL_THRESHOLD - 1 ) <= range->n1 ) + range->n1 = input_full_size - 1; + } + } + else + { + // for non-edge-wrap modes, we never read over the edge, so clamp + if ( range->n0 < 0 ) + range->n0 = 0; + if ( range->n1 >= input_full_size ) + range->n1 = input_full_size - 1; + } +} + +static void stbir__get_split_info( stbir__per_split_info* split_info, int splits, int output_height, int vertical_pixel_margin, int input_full_height ) +{ + int i, cur; + int left = output_height; + + cur = 0; + for( i = 0 ; i < splits ; i++ ) + { + int each; + split_info[i].start_output_y = cur; + each = left / ( splits - i ); + split_info[i].end_output_y = cur + each; + cur += each; + left -= each; + + // scatter range (updated to minimum as you run it) + split_info[i].start_input_y = -vertical_pixel_margin; + split_info[i].end_input_y = input_full_height + vertical_pixel_margin; + } +} + +static void stbir__free_internal_mem( stbir__info *info ) +{ + #define STBIR__FREE_AND_CLEAR( ptr ) { if ( ptr ) { void * p = (ptr); (ptr) = 0; STBIR_FREE( p, info->user_data); } } + + if ( info ) + { + #ifndef STBIR__SEPARATE_ALLOCATIONS + STBIR__FREE_AND_CLEAR( info->alloced_mem ); + #else + int i,j; + + if ( ( info->vertical.gather_prescatter_contributors ) && ( (void*)info->vertical.gather_prescatter_contributors != (void*)info->split_info[0].decode_buffer ) ) + { + STBIR__FREE_AND_CLEAR( info->vertical.gather_prescatter_coefficients ); + STBIR__FREE_AND_CLEAR( info->vertical.gather_prescatter_contributors ); + } + for( i = 0 ; i < info->splits ; i++ ) + { + for( j = 0 ; j < info->alloc_ring_buffer_num_entries ; j++ ) + { + #ifdef STBIR_SIMD8 + if ( info->effective_channels == 3 ) + --info->split_info[i].ring_buffers[j]; // avx in 3 channel mode needs one float at the start of the buffer + #endif + STBIR__FREE_AND_CLEAR( info->split_info[i].ring_buffers[j] ); + } + + #ifdef STBIR_SIMD8 + if ( info->effective_channels == 3 ) + --info->split_info[i].decode_buffer; // avx in 3 channel mode needs one float at the start of the buffer + #endif + STBIR__FREE_AND_CLEAR( info->split_info[i].decode_buffer ); + STBIR__FREE_AND_CLEAR( info->split_info[i].ring_buffers ); + STBIR__FREE_AND_CLEAR( info->split_info[i].vertical_buffer ); + } + STBIR__FREE_AND_CLEAR( info->split_info ); + if ( info->vertical.coefficients != info->horizontal.coefficients ) + { + STBIR__FREE_AND_CLEAR( info->vertical.coefficients ); + STBIR__FREE_AND_CLEAR( info->vertical.contributors ); + } + STBIR__FREE_AND_CLEAR( info->horizontal.coefficients ); + STBIR__FREE_AND_CLEAR( info->horizontal.contributors ); + STBIR__FREE_AND_CLEAR( info->alloced_mem ); + STBIR__FREE_AND_CLEAR( info ); + #endif + } + + #undef STBIR__FREE_AND_CLEAR +} + +static int stbir__get_max_split( int splits, int height ) +{ + int i; + int max = 0; + + for( i = 0 ; i < splits ; i++ ) + { + int each = height / ( splits - i ); + if ( each > max ) + max = each; + height -= each; + } + return max; +} + +static stbir__horizontal_gather_channels_func ** stbir__horizontal_gather_n_coeffs_funcs[8] = +{ + 0, stbir__horizontal_gather_1_channels_with_n_coeffs_funcs, stbir__horizontal_gather_2_channels_with_n_coeffs_funcs, stbir__horizontal_gather_3_channels_with_n_coeffs_funcs, stbir__horizontal_gather_4_channels_with_n_coeffs_funcs, 0,0, stbir__horizontal_gather_7_channels_with_n_coeffs_funcs +}; + +static stbir__horizontal_gather_channels_func ** stbir__horizontal_gather_channels_funcs[8] = +{ + 0, stbir__horizontal_gather_1_channels_funcs, stbir__horizontal_gather_2_channels_funcs, stbir__horizontal_gather_3_channels_funcs, stbir__horizontal_gather_4_channels_funcs, 0,0, stbir__horizontal_gather_7_channels_funcs +}; + +// there are six resize classifications: 0 == vertical scatter, 1 == vertical gather < 1x scale, 2 == vertical gather 1x-2x scale, 4 == vertical gather < 3x scale, 4 == vertical gather > 3x scale, 5 == <=4 pixel height, 6 == <=4 pixel wide column +#define STBIR_RESIZE_CLASSIFICATIONS 8 + +static float stbir__compute_weights[5][STBIR_RESIZE_CLASSIFICATIONS][4]= // 5 = 0=1chan, 1=2chan, 2=3chan, 3=4chan, 4=7chan +{ + { + { 1.00000f, 1.00000f, 0.31250f, 1.00000f }, + { 0.56250f, 0.59375f, 0.00000f, 0.96875f }, + { 1.00000f, 0.06250f, 0.00000f, 1.00000f }, + { 0.00000f, 0.09375f, 1.00000f, 1.00000f }, + { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 0.03125f, 0.12500f, 1.00000f, 1.00000f }, + { 0.06250f, 0.12500f, 0.00000f, 1.00000f }, + { 0.00000f, 1.00000f, 0.00000f, 0.03125f }, + }, { + { 0.00000f, 0.84375f, 0.00000f, 0.03125f }, + { 0.09375f, 0.93750f, 0.00000f, 0.78125f }, + { 0.87500f, 0.21875f, 0.00000f, 0.96875f }, + { 0.09375f, 0.09375f, 1.00000f, 1.00000f }, + { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 0.03125f, 0.12500f, 1.00000f, 1.00000f }, + { 0.06250f, 0.12500f, 0.00000f, 1.00000f }, + { 0.00000f, 1.00000f, 0.00000f, 0.53125f }, + }, { + { 0.00000f, 0.53125f, 0.00000f, 0.03125f }, + { 0.06250f, 0.96875f, 0.00000f, 0.53125f }, + { 0.87500f, 0.18750f, 0.00000f, 0.93750f }, + { 0.00000f, 0.09375f, 1.00000f, 1.00000f }, + { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 0.03125f, 0.12500f, 1.00000f, 1.00000f }, + { 0.06250f, 0.12500f, 0.00000f, 1.00000f }, + { 0.00000f, 1.00000f, 0.00000f, 0.56250f }, + }, { + { 0.00000f, 0.50000f, 0.00000f, 0.71875f }, + { 0.06250f, 0.84375f, 0.00000f, 0.87500f }, + { 1.00000f, 0.50000f, 0.50000f, 0.96875f }, + { 1.00000f, 0.09375f, 0.31250f, 0.50000f }, + { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 1.00000f, 0.03125f, 0.03125f, 0.53125f }, + { 0.18750f, 0.12500f, 0.00000f, 1.00000f }, + { 0.00000f, 1.00000f, 0.03125f, 0.18750f }, + }, { + { 0.00000f, 0.59375f, 0.00000f, 0.96875f }, + { 0.06250f, 0.81250f, 0.06250f, 0.59375f }, + { 0.75000f, 0.43750f, 0.12500f, 0.96875f }, + { 0.87500f, 0.06250f, 0.18750f, 0.43750f }, + { 1.00000f, 1.00000f, 1.00000f, 1.00000f }, + { 0.15625f, 0.12500f, 1.00000f, 1.00000f }, + { 0.06250f, 0.12500f, 0.00000f, 1.00000f }, + { 0.00000f, 1.00000f, 0.03125f, 0.34375f }, + } +}; + +// structure that allow us to query and override info for training the costs +typedef struct STBIR__V_FIRST_INFO +{ + double v_cost, h_cost; + int control_v_first; // 0 = no control, 1 = force hori, 2 = force vert + int v_first; + int v_resize_classification; + int is_gather; +} STBIR__V_FIRST_INFO; + +#ifdef STBIR__V_FIRST_INFO_BUFFER +static STBIR__V_FIRST_INFO STBIR__V_FIRST_INFO_BUFFER = {0}; +#define STBIR__V_FIRST_INFO_POINTER &STBIR__V_FIRST_INFO_BUFFER +#else +#define STBIR__V_FIRST_INFO_POINTER 0 +#endif + +// Figure out whether to scale along the horizontal or vertical first. +// This only *super* important when you are scaling by a massively +// different amount in the vertical vs the horizontal (for example, if +// you are scaling by 2x in the width, and 0.5x in the height, then you +// want to do the vertical scale first, because it's around 3x faster +// in that order. +// +// In more normal circumstances, this makes a 20-40% differences, so +// it's good to get right, but not critical. The normal way that you +// decide which direction goes first is just figuring out which +// direction does more multiplies. But with modern CPUs with their +// fancy caches and SIMD and high IPC abilities, so there's just a lot +// more that goes into it. +// +// My handwavy sort of solution is to have an app that does a whole +// bunch of timing for both vertical and horizontal first modes, +// and then another app that can read lots of these timing files +// and try to search for the best weights to use. Dotimings.c +// is the app that does a bunch of timings, and vf_train.c is the +// app that solves for the best weights (and shows how well it +// does currently). + +static int stbir__should_do_vertical_first( float weights_table[STBIR_RESIZE_CLASSIFICATIONS][4], int horizontal_filter_pixel_width, float horizontal_scale, int horizontal_output_size, int vertical_filter_pixel_width, float vertical_scale, int vertical_output_size, int is_gather, STBIR__V_FIRST_INFO * info ) +{ + double v_cost, h_cost; + float * weights; + int vertical_first; + int v_classification; + + // categorize the resize into buckets + if ( ( vertical_output_size <= 4 ) || ( horizontal_output_size <= 4 ) ) + v_classification = ( vertical_output_size < horizontal_output_size ) ? 6 : 7; + else if ( vertical_scale <= 1.0f ) + v_classification = ( is_gather ) ? 1 : 0; + else if ( vertical_scale <= 2.0f) + v_classification = 2; + else if ( vertical_scale <= 3.0f) + v_classification = 3; + else if ( vertical_scale <= 4.0f) + v_classification = 5; + else + v_classification = 6; + + // use the right weights + weights = weights_table[ v_classification ]; + + // this is the costs when you don't take into account modern CPUs with high ipc and simd and caches - wish we had a better estimate + h_cost = (float)horizontal_filter_pixel_width * weights[0] + horizontal_scale * (float)vertical_filter_pixel_width * weights[1]; + v_cost = (float)vertical_filter_pixel_width * weights[2] + vertical_scale * (float)horizontal_filter_pixel_width * weights[3]; + + // use computation estimate to decide vertical first or not + vertical_first = ( v_cost <= h_cost ) ? 1 : 0; + + // save these, if requested + if ( info ) + { + info->h_cost = h_cost; + info->v_cost = v_cost; + info->v_resize_classification = v_classification; + info->v_first = vertical_first; + info->is_gather = is_gather; + } + + // and this allows us to override everything for testing (see dotiming.c) + if ( ( info ) && ( info->control_v_first ) ) + vertical_first = ( info->control_v_first == 2 ) ? 1 : 0; + + return vertical_first; +} + +// layout lookups - must match stbir_internal_pixel_layout +static unsigned char stbir__pixel_channels[] = { + 1,2,3,3,4, // 1ch, 2ch, rgb, bgr, 4ch + 4,4,4,4,2,2, // RGBA,BGRA,ARGB,ABGR,RA,AR + 4,4,4,4,2,2, // RGBA_PM,BGRA_PM,ARGB_PM,ABGR_PM,RA_PM,AR_PM +}; + +// the internal pixel layout enums are in a different order, so we can easily do range comparisons of types +// the public pixel layout is ordered in a way that if you cast num_channels (1-4) to the enum, you get something sensible +static stbir_internal_pixel_layout stbir__pixel_layout_convert_public_to_internal[] = { + STBIRI_BGR, STBIRI_1CHANNEL, STBIRI_2CHANNEL, STBIRI_RGB, STBIRI_RGBA, + STBIRI_4CHANNEL, STBIRI_BGRA, STBIRI_ARGB, STBIRI_ABGR, STBIRI_RA, STBIRI_AR, + STBIRI_RGBA_PM, STBIRI_BGRA_PM, STBIRI_ARGB_PM, STBIRI_ABGR_PM, STBIRI_RA_PM, STBIRI_AR_PM, +}; + +static stbir__info * stbir__alloc_internal_mem_and_build_samplers( stbir__sampler * horizontal, stbir__sampler * vertical, stbir__contributors * conservative, stbir_pixel_layout input_pixel_layout_public, stbir_pixel_layout output_pixel_layout_public, int splits, int new_x, int new_y, int fast_alpha, void * user_data STBIR_ONLY_PROFILE_BUILD_GET_INFO ) +{ + static char stbir_channel_count_index[8]={ 9,0,1,2, 3,9,9,4 }; + + stbir__info * info = 0; + void * alloced = 0; + int alloced_total = 0; + int vertical_first; + int decode_buffer_size, ring_buffer_length_bytes, ring_buffer_size, vertical_buffer_size, alloc_ring_buffer_num_entries; + + int alpha_weighting_type = 0; // 0=none, 1=simple, 2=fancy + int conservative_split_output_size = stbir__get_max_split( splits, vertical->scale_info.output_sub_size ); + stbir_internal_pixel_layout input_pixel_layout = stbir__pixel_layout_convert_public_to_internal[ input_pixel_layout_public ]; + stbir_internal_pixel_layout output_pixel_layout = stbir__pixel_layout_convert_public_to_internal[ output_pixel_layout_public ]; + int channels = stbir__pixel_channels[ input_pixel_layout ]; + int effective_channels = channels; + + // first figure out what type of alpha weighting to use (if any) + if ( ( horizontal->filter_enum != STBIR_FILTER_POINT_SAMPLE ) || ( vertical->filter_enum != STBIR_FILTER_POINT_SAMPLE ) ) // no alpha weighting on point sampling + { + if ( ( input_pixel_layout >= STBIRI_RGBA ) && ( input_pixel_layout <= STBIRI_AR ) && ( output_pixel_layout >= STBIRI_RGBA ) && ( output_pixel_layout <= STBIRI_AR ) ) + { + if ( fast_alpha ) + { + alpha_weighting_type = 4; + } + else + { + static int fancy_alpha_effective_cnts[6] = { 7, 7, 7, 7, 3, 3 }; + alpha_weighting_type = 2; + effective_channels = fancy_alpha_effective_cnts[ input_pixel_layout - STBIRI_RGBA ]; + } + } + else if ( ( input_pixel_layout >= STBIRI_RGBA_PM ) && ( input_pixel_layout <= STBIRI_AR_PM ) && ( output_pixel_layout >= STBIRI_RGBA ) && ( output_pixel_layout <= STBIRI_AR ) ) + { + // input premult, output non-premult + alpha_weighting_type = 3; + } + else if ( ( input_pixel_layout >= STBIRI_RGBA ) && ( input_pixel_layout <= STBIRI_AR ) && ( output_pixel_layout >= STBIRI_RGBA_PM ) && ( output_pixel_layout <= STBIRI_AR_PM ) ) + { + // input non-premult, output premult + alpha_weighting_type = 1; + } + } + + // channel in and out count must match currently + if ( channels != stbir__pixel_channels[ output_pixel_layout ] ) + return 0; + + // get vertical first + vertical_first = stbir__should_do_vertical_first( stbir__compute_weights[ (int)stbir_channel_count_index[ effective_channels ] ], horizontal->filter_pixel_width, horizontal->scale_info.scale, horizontal->scale_info.output_sub_size, vertical->filter_pixel_width, vertical->scale_info.scale, vertical->scale_info.output_sub_size, vertical->is_gather, STBIR__V_FIRST_INFO_POINTER ); + + // sometimes read one float off in some of the unrolled loops (with a weight of zero coeff, so it doesn't have an effect) + decode_buffer_size = ( conservative->n1 - conservative->n0 + 1 ) * effective_channels * sizeof(float) + sizeof(float); // extra float for padding + +#if defined( STBIR__SEPARATE_ALLOCATIONS ) && defined(STBIR_SIMD8) + if ( effective_channels == 3 ) + decode_buffer_size += sizeof(float); // avx in 3 channel mode needs one float at the start of the buffer (only with separate allocations) +#endif + + ring_buffer_length_bytes = horizontal->scale_info.output_sub_size * effective_channels * sizeof(float) + sizeof(float); // extra float for padding + + // if we do vertical first, the ring buffer holds a whole decoded line + if ( vertical_first ) + ring_buffer_length_bytes = ( decode_buffer_size + 15 ) & ~15; + + if ( ( ring_buffer_length_bytes & 4095 ) == 0 ) ring_buffer_length_bytes += 64*3; // avoid 4k alias + + // One extra entry because floating point precision problems sometimes cause an extra to be necessary. + alloc_ring_buffer_num_entries = vertical->filter_pixel_width + 1; + + // we never need more ring buffer entries than the scanlines we're outputting when in scatter mode + if ( ( !vertical->is_gather ) && ( alloc_ring_buffer_num_entries > conservative_split_output_size ) ) + alloc_ring_buffer_num_entries = conservative_split_output_size; + + ring_buffer_size = alloc_ring_buffer_num_entries * ring_buffer_length_bytes; + + // The vertical buffer is used differently, depending on whether we are scattering + // the vertical scanlines, or gathering them. + // If scattering, it's used at the temp buffer to accumulate each output. + // If gathering, it's just the output buffer. + vertical_buffer_size = horizontal->scale_info.output_sub_size * effective_channels * sizeof(float) + sizeof(float); // extra float for padding + + // we make two passes through this loop, 1st to add everything up, 2nd to allocate and init + for(;;) + { + int i; + void * advance_mem = alloced; + int copy_horizontal = 0; + stbir__sampler * possibly_use_horizontal_for_pivot = 0; + +#ifdef STBIR__SEPARATE_ALLOCATIONS + #define STBIR__NEXT_PTR( ptr, size, ntype ) if ( alloced ) { void * p = STBIR_MALLOC( size, user_data); if ( p == 0 ) { stbir__free_internal_mem( info ); return 0; } (ptr) = (ntype*)p; } +#else + #define STBIR__NEXT_PTR( ptr, size, ntype ) advance_mem = (void*) ( ( ((size_t)advance_mem) + 15 ) & ~15 ); if ( alloced ) ptr = (ntype*)advance_mem; advance_mem = ((char*)advance_mem) + (size); +#endif + + STBIR__NEXT_PTR( info, sizeof( stbir__info ), stbir__info ); + + STBIR__NEXT_PTR( info->split_info, sizeof( stbir__per_split_info ) * splits, stbir__per_split_info ); + + if ( info ) + { + static stbir__alpha_weight_func * fancy_alpha_weights[6] = { stbir__fancy_alpha_weight_4ch, stbir__fancy_alpha_weight_4ch, stbir__fancy_alpha_weight_4ch, stbir__fancy_alpha_weight_4ch, stbir__fancy_alpha_weight_2ch, stbir__fancy_alpha_weight_2ch }; + static stbir__alpha_unweight_func * fancy_alpha_unweights[6] = { stbir__fancy_alpha_unweight_4ch, stbir__fancy_alpha_unweight_4ch, stbir__fancy_alpha_unweight_4ch, stbir__fancy_alpha_unweight_4ch, stbir__fancy_alpha_unweight_2ch, stbir__fancy_alpha_unweight_2ch }; + static stbir__alpha_weight_func * simple_alpha_weights[6] = { stbir__simple_alpha_weight_4ch, stbir__simple_alpha_weight_4ch, stbir__simple_alpha_weight_4ch, stbir__simple_alpha_weight_4ch, stbir__simple_alpha_weight_2ch, stbir__simple_alpha_weight_2ch }; + static stbir__alpha_unweight_func * simple_alpha_unweights[6] = { stbir__simple_alpha_unweight_4ch, stbir__simple_alpha_unweight_4ch, stbir__simple_alpha_unweight_4ch, stbir__simple_alpha_unweight_4ch, stbir__simple_alpha_unweight_2ch, stbir__simple_alpha_unweight_2ch }; + + // initialize info fields + info->alloced_mem = alloced; + info->alloced_total = alloced_total; + + info->channels = channels; + info->effective_channels = effective_channels; + + info->offset_x = new_x; + info->offset_y = new_y; + info->alloc_ring_buffer_num_entries = alloc_ring_buffer_num_entries; + info->ring_buffer_num_entries = 0; + info->ring_buffer_length_bytes = ring_buffer_length_bytes; + info->splits = splits; + info->vertical_first = vertical_first; + + info->input_pixel_layout_internal = input_pixel_layout; + info->output_pixel_layout_internal = output_pixel_layout; + + // setup alpha weight functions + info->alpha_weight = 0; + info->alpha_unweight = 0; + + // handle alpha weighting functions and overrides + if ( alpha_weighting_type == 2 ) + { + // high quality alpha multiplying on the way in, dividing on the way out + info->alpha_weight = fancy_alpha_weights[ input_pixel_layout - STBIRI_RGBA ]; + info->alpha_unweight = fancy_alpha_unweights[ output_pixel_layout - STBIRI_RGBA ]; + } + else if ( alpha_weighting_type == 4 ) + { + // fast alpha multiplying on the way in, dividing on the way out + info->alpha_weight = simple_alpha_weights[ input_pixel_layout - STBIRI_RGBA ]; + info->alpha_unweight = simple_alpha_unweights[ output_pixel_layout - STBIRI_RGBA ]; + } + else if ( alpha_weighting_type == 1 ) + { + // fast alpha on the way in, leave in premultiplied form on way out + info->alpha_weight = simple_alpha_weights[ input_pixel_layout - STBIRI_RGBA ]; + } + else if ( alpha_weighting_type == 3 ) + { + // incoming is premultiplied, fast alpha dividing on the way out - non-premultiplied output + info->alpha_unweight = simple_alpha_unweights[ output_pixel_layout - STBIRI_RGBA ]; + } + + // handle 3-chan color flipping, using the alpha weight path + if ( ( ( input_pixel_layout == STBIRI_RGB ) && ( output_pixel_layout == STBIRI_BGR ) ) || + ( ( input_pixel_layout == STBIRI_BGR ) && ( output_pixel_layout == STBIRI_RGB ) ) ) + { + // do the flipping on the smaller of the two ends + if ( horizontal->scale_info.scale < 1.0f ) + info->alpha_unweight = stbir__simple_flip_3ch; + else + info->alpha_weight = stbir__simple_flip_3ch; + } + + } + + // get all the per-split buffers + for( i = 0 ; i < splits ; i++ ) + { + STBIR__NEXT_PTR( info->split_info[i].decode_buffer, decode_buffer_size, float ); + +#ifdef STBIR__SEPARATE_ALLOCATIONS + + #ifdef STBIR_SIMD8 + if ( ( info ) && ( effective_channels == 3 ) ) + ++info->split_info[i].decode_buffer; // avx in 3 channel mode needs one float at the start of the buffer + #endif + + STBIR__NEXT_PTR( info->split_info[i].ring_buffers, alloc_ring_buffer_num_entries * sizeof(float*), float* ); + { + int j; + for( j = 0 ; j < alloc_ring_buffer_num_entries ; j++ ) + { + STBIR__NEXT_PTR( info->split_info[i].ring_buffers[j], ring_buffer_length_bytes, float ); + #ifdef STBIR_SIMD8 + if ( ( info ) && ( effective_channels == 3 ) ) + ++info->split_info[i].ring_buffers[j]; // avx in 3 channel mode needs one float at the start of the buffer + #endif + } + } +#else + STBIR__NEXT_PTR( info->split_info[i].ring_buffer, ring_buffer_size, float ); +#endif + STBIR__NEXT_PTR( info->split_info[i].vertical_buffer, vertical_buffer_size, float ); + } + + // alloc memory for to-be-pivoted coeffs (if necessary) + if ( vertical->is_gather == 0 ) + { + int both; + int temp_mem_amt; + + // when in vertical scatter mode, we first build the coefficients in gather mode, and then pivot after, + // that means we need two buffers, so we try to use the decode buffer and ring buffer for this. if that + // is too small, we just allocate extra memory to use as this temp. + + both = vertical->gather_prescatter_contributors_size + vertical->gather_prescatter_coefficients_size; + +#ifdef STBIR__SEPARATE_ALLOCATIONS + temp_mem_amt = decode_buffer_size; +#else + temp_mem_amt = ( decode_buffer_size + ring_buffer_size + vertical_buffer_size ) * splits; +#endif + if ( temp_mem_amt >= both ) + { + if ( info ) + { + vertical->gather_prescatter_contributors = (stbir__contributors*)info->split_info[0].decode_buffer; + vertical->gather_prescatter_coefficients = (float*) ( ( (char*)info->split_info[0].decode_buffer ) + vertical->gather_prescatter_contributors_size ); + } + } + else + { + // ring+decode memory is too small, so allocate temp memory + STBIR__NEXT_PTR( vertical->gather_prescatter_contributors, vertical->gather_prescatter_contributors_size, stbir__contributors ); + STBIR__NEXT_PTR( vertical->gather_prescatter_coefficients, vertical->gather_prescatter_coefficients_size, float ); + } + } + + STBIR__NEXT_PTR( horizontal->contributors, horizontal->contributors_size, stbir__contributors ); + STBIR__NEXT_PTR( horizontal->coefficients, horizontal->coefficients_size, float ); + + // are the two filters identical?? (happens a lot with mipmap generation) + if ( ( horizontal->filter_kernel == vertical->filter_kernel ) && ( horizontal->filter_support == vertical->filter_support ) && ( horizontal->edge == vertical->edge ) && ( horizontal->scale_info.output_sub_size == vertical->scale_info.output_sub_size ) ) + { + float diff_scale = horizontal->scale_info.scale - vertical->scale_info.scale; + float diff_shift = horizontal->scale_info.pixel_shift - vertical->scale_info.pixel_shift; + if ( diff_scale < 0.0f ) diff_scale = -diff_scale; + if ( diff_shift < 0.0f ) diff_shift = -diff_shift; + if ( ( diff_scale <= stbir__small_float ) && ( diff_shift <= stbir__small_float ) ) + { + if ( horizontal->is_gather == vertical->is_gather ) + { + copy_horizontal = 1; + goto no_vert_alloc; + } + // everything matches, but vertical is scatter, horizontal is gather, use horizontal coeffs for vertical pivot coeffs + possibly_use_horizontal_for_pivot = horizontal; + } + } + + STBIR__NEXT_PTR( vertical->contributors, vertical->contributors_size, stbir__contributors ); + STBIR__NEXT_PTR( vertical->coefficients, vertical->coefficients_size, float ); + + no_vert_alloc: + + if ( info ) + { + STBIR_PROFILE_BUILD_START( horizontal ); + + stbir__calculate_filters( horizontal, 0, user_data STBIR_ONLY_PROFILE_BUILD_SET_INFO ); + + // setup the horizontal gather functions + // start with defaulting to the n_coeffs functions (specialized on channels and remnant leftover) + info->horizontal_gather_channels = stbir__horizontal_gather_n_coeffs_funcs[ effective_channels ][ horizontal->extent_info.widest & 3 ]; + // but if the number of coeffs <= 12, use another set of special cases. <=12 coeffs is any enlarging resize, or shrinking resize down to about 1/3 size + if ( horizontal->extent_info.widest <= 12 ) + info->horizontal_gather_channels = stbir__horizontal_gather_channels_funcs[ effective_channels ][ horizontal->extent_info.widest - 1 ]; + + info->scanline_extents.conservative.n0 = conservative->n0; + info->scanline_extents.conservative.n1 = conservative->n1; + + // get exact extents + stbir__get_extents( horizontal, &info->scanline_extents ); + + // pack the horizontal coeffs + horizontal->coefficient_width = stbir__pack_coefficients(horizontal->num_contributors, horizontal->contributors, horizontal->coefficients, horizontal->coefficient_width, horizontal->extent_info.widest, info->scanline_extents.conservative.n1 + 1 ); + + STBIR_MEMCPY( &info->horizontal, horizontal, sizeof( stbir__sampler ) ); + + STBIR_PROFILE_BUILD_END( horizontal ); + + if ( copy_horizontal ) + { + STBIR_MEMCPY( &info->vertical, horizontal, sizeof( stbir__sampler ) ); + } + else + { + STBIR_PROFILE_BUILD_START( vertical ); + + stbir__calculate_filters( vertical, possibly_use_horizontal_for_pivot, user_data STBIR_ONLY_PROFILE_BUILD_SET_INFO ); + STBIR_MEMCPY( &info->vertical, vertical, sizeof( stbir__sampler ) ); + + STBIR_PROFILE_BUILD_END( vertical ); + } + + // setup the vertical split ranges + stbir__get_split_info( info->split_info, info->splits, info->vertical.scale_info.output_sub_size, info->vertical.filter_pixel_margin, info->vertical.scale_info.input_full_size ); + + // now we know precisely how many entries we need + info->ring_buffer_num_entries = info->vertical.extent_info.widest; + + // we never need more ring buffer entries than the scanlines we're outputting + if ( ( !info->vertical.is_gather ) && ( info->ring_buffer_num_entries > conservative_split_output_size ) ) + info->ring_buffer_num_entries = conservative_split_output_size; + STBIR_ASSERT( info->ring_buffer_num_entries <= info->alloc_ring_buffer_num_entries ); + + // a few of the horizontal gather functions read one dword past the end (but mask it out), so put in a normal value so no snans or denormals accidentally sneak in + for( i = 0 ; i < splits ; i++ ) + { + int width, ofs; + + // find the right most span + if ( info->scanline_extents.spans[0].n1 > info->scanline_extents.spans[1].n1 ) + width = info->scanline_extents.spans[0].n1 - info->scanline_extents.spans[0].n0; + else + width = info->scanline_extents.spans[1].n1 - info->scanline_extents.spans[1].n0; + + // this calc finds the exact end of the decoded scanline for all filter modes. + // usually this is just the width * effective channels. But we have to account + // for the area to the left of the scanline for wrap filtering and alignment, this + // is stored as a negative value in info->scanline_extents.conservative.n0. Next, + // we need to skip the exact size of the right hand size filter area (again for + // wrap mode), this is in info->scanline_extents.edge_sizes[1]). + ofs = ( width + 1 - info->scanline_extents.conservative.n0 + info->scanline_extents.edge_sizes[1] ) * effective_channels; + + // place a known, but numerically valid value in the decode buffer + info->split_info[i].decode_buffer[ ofs ] = 9999.0f; + + // if vertical filtering first, place a known, but numerically valid value in the all + // of the ring buffer accumulators + if ( vertical_first ) + { + int j; + for( j = 0; j < info->ring_buffer_num_entries ; j++ ) + { + stbir__get_ring_buffer_entry( info, info->split_info + i, j )[ ofs ] = 9999.0f; + } + } + } + } + + #undef STBIR__NEXT_PTR + + + // is this the first time through loop? + if ( info == 0 ) + { + alloced_total = (int) ( 15 + (size_t)advance_mem ); + alloced = STBIR_MALLOC( alloced_total, user_data ); + if ( alloced == 0 ) + return 0; + } + else + return info; // success + } +} + +static int stbir__perform_resize( stbir__info const * info, int split_start, int split_count ) +{ + stbir__per_split_info * split_info = info->split_info + split_start; + + STBIR_PROFILE_CLEAR_EXTRAS(); + + STBIR_PROFILE_FIRST_START( looping ); + if (info->vertical.is_gather) + stbir__vertical_gather_loop( info, split_info, split_count ); + else + stbir__vertical_scatter_loop( info, split_info, split_count ); + STBIR_PROFILE_END( looping ); + + return 1; +} + +static void stbir__update_info_from_resize( stbir__info * info, STBIR_RESIZE * resize ) +{ + static stbir__decode_pixels_func * decode_simple[STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]= + { + /* 1ch-4ch */ stbir__decode_uint8_srgb, stbir__decode_uint8_srgb, 0, stbir__decode_float_linear, stbir__decode_half_float_linear, + }; + + static stbir__decode_pixels_func * decode_alphas[STBIRI_AR-STBIRI_RGBA+1][STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]= + { + { /* RGBA */ stbir__decode_uint8_srgb4_linearalpha, stbir__decode_uint8_srgb, 0, stbir__decode_float_linear, stbir__decode_half_float_linear }, + { /* BGRA */ stbir__decode_uint8_srgb4_linearalpha_BGRA, stbir__decode_uint8_srgb_BGRA, 0, stbir__decode_float_linear_BGRA, stbir__decode_half_float_linear_BGRA }, + { /* ARGB */ stbir__decode_uint8_srgb4_linearalpha_ARGB, stbir__decode_uint8_srgb_ARGB, 0, stbir__decode_float_linear_ARGB, stbir__decode_half_float_linear_ARGB }, + { /* ABGR */ stbir__decode_uint8_srgb4_linearalpha_ABGR, stbir__decode_uint8_srgb_ABGR, 0, stbir__decode_float_linear_ABGR, stbir__decode_half_float_linear_ABGR }, + { /* RA */ stbir__decode_uint8_srgb2_linearalpha, stbir__decode_uint8_srgb, 0, stbir__decode_float_linear, stbir__decode_half_float_linear }, + { /* AR */ stbir__decode_uint8_srgb2_linearalpha_AR, stbir__decode_uint8_srgb_AR, 0, stbir__decode_float_linear_AR, stbir__decode_half_float_linear_AR }, + }; + + static stbir__decode_pixels_func * decode_simple_scaled_or_not[2][2]= + { + { stbir__decode_uint8_linear_scaled, stbir__decode_uint8_linear }, { stbir__decode_uint16_linear_scaled, stbir__decode_uint16_linear }, + }; + + static stbir__decode_pixels_func * decode_alphas_scaled_or_not[STBIRI_AR-STBIRI_RGBA+1][2][2]= + { + { /* RGBA */ { stbir__decode_uint8_linear_scaled, stbir__decode_uint8_linear }, { stbir__decode_uint16_linear_scaled, stbir__decode_uint16_linear } }, + { /* BGRA */ { stbir__decode_uint8_linear_scaled_BGRA, stbir__decode_uint8_linear_BGRA }, { stbir__decode_uint16_linear_scaled_BGRA, stbir__decode_uint16_linear_BGRA } }, + { /* ARGB */ { stbir__decode_uint8_linear_scaled_ARGB, stbir__decode_uint8_linear_ARGB }, { stbir__decode_uint16_linear_scaled_ARGB, stbir__decode_uint16_linear_ARGB } }, + { /* ABGR */ { stbir__decode_uint8_linear_scaled_ABGR, stbir__decode_uint8_linear_ABGR }, { stbir__decode_uint16_linear_scaled_ABGR, stbir__decode_uint16_linear_ABGR } }, + { /* RA */ { stbir__decode_uint8_linear_scaled, stbir__decode_uint8_linear }, { stbir__decode_uint16_linear_scaled, stbir__decode_uint16_linear } }, + { /* AR */ { stbir__decode_uint8_linear_scaled_AR, stbir__decode_uint8_linear_AR }, { stbir__decode_uint16_linear_scaled_AR, stbir__decode_uint16_linear_AR } } + }; + + static stbir__encode_pixels_func * encode_simple[STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]= + { + /* 1ch-4ch */ stbir__encode_uint8_srgb, stbir__encode_uint8_srgb, 0, stbir__encode_float_linear, stbir__encode_half_float_linear, + }; + + static stbir__encode_pixels_func * encode_alphas[STBIRI_AR-STBIRI_RGBA+1][STBIR_TYPE_HALF_FLOAT-STBIR_TYPE_UINT8_SRGB+1]= + { + { /* RGBA */ stbir__encode_uint8_srgb4_linearalpha, stbir__encode_uint8_srgb, 0, stbir__encode_float_linear, stbir__encode_half_float_linear }, + { /* BGRA */ stbir__encode_uint8_srgb4_linearalpha_BGRA, stbir__encode_uint8_srgb_BGRA, 0, stbir__encode_float_linear_BGRA, stbir__encode_half_float_linear_BGRA }, + { /* ARGB */ stbir__encode_uint8_srgb4_linearalpha_ARGB, stbir__encode_uint8_srgb_ARGB, 0, stbir__encode_float_linear_ARGB, stbir__encode_half_float_linear_ARGB }, + { /* ABGR */ stbir__encode_uint8_srgb4_linearalpha_ABGR, stbir__encode_uint8_srgb_ABGR, 0, stbir__encode_float_linear_ABGR, stbir__encode_half_float_linear_ABGR }, + { /* RA */ stbir__encode_uint8_srgb2_linearalpha, stbir__encode_uint8_srgb, 0, stbir__encode_float_linear, stbir__encode_half_float_linear }, + { /* AR */ stbir__encode_uint8_srgb2_linearalpha_AR, stbir__encode_uint8_srgb_AR, 0, stbir__encode_float_linear_AR, stbir__encode_half_float_linear_AR } + }; + + static stbir__encode_pixels_func * encode_simple_scaled_or_not[2][2]= + { + { stbir__encode_uint8_linear_scaled, stbir__encode_uint8_linear }, { stbir__encode_uint16_linear_scaled, stbir__encode_uint16_linear }, + }; + + static stbir__encode_pixels_func * encode_alphas_scaled_or_not[STBIRI_AR-STBIRI_RGBA+1][2][2]= + { + { /* RGBA */ { stbir__encode_uint8_linear_scaled, stbir__encode_uint8_linear }, { stbir__encode_uint16_linear_scaled, stbir__encode_uint16_linear } }, + { /* BGRA */ { stbir__encode_uint8_linear_scaled_BGRA, stbir__encode_uint8_linear_BGRA }, { stbir__encode_uint16_linear_scaled_BGRA, stbir__encode_uint16_linear_BGRA } }, + { /* ARGB */ { stbir__encode_uint8_linear_scaled_ARGB, stbir__encode_uint8_linear_ARGB }, { stbir__encode_uint16_linear_scaled_ARGB, stbir__encode_uint16_linear_ARGB } }, + { /* ABGR */ { stbir__encode_uint8_linear_scaled_ABGR, stbir__encode_uint8_linear_ABGR }, { stbir__encode_uint16_linear_scaled_ABGR, stbir__encode_uint16_linear_ABGR } }, + { /* RA */ { stbir__encode_uint8_linear_scaled, stbir__encode_uint8_linear }, { stbir__encode_uint16_linear_scaled, stbir__encode_uint16_linear } }, + { /* AR */ { stbir__encode_uint8_linear_scaled_AR, stbir__encode_uint8_linear_AR }, { stbir__encode_uint16_linear_scaled_AR, stbir__encode_uint16_linear_AR } } + }; + + stbir__decode_pixels_func * decode_pixels = 0; + stbir__encode_pixels_func * encode_pixels = 0; + stbir_datatype input_type, output_type; + + input_type = resize->input_data_type; + output_type = resize->output_data_type; + info->input_data = resize->input_pixels; + info->input_stride_bytes = resize->input_stride_in_bytes; + info->output_stride_bytes = resize->output_stride_in_bytes; + + // if we're completely point sampling, then we can turn off SRGB + if ( ( info->horizontal.filter_enum == STBIR_FILTER_POINT_SAMPLE ) && ( info->vertical.filter_enum == STBIR_FILTER_POINT_SAMPLE ) ) + { + if ( ( ( input_type == STBIR_TYPE_UINT8_SRGB ) || ( input_type == STBIR_TYPE_UINT8_SRGB_ALPHA ) ) && + ( ( output_type == STBIR_TYPE_UINT8_SRGB ) || ( output_type == STBIR_TYPE_UINT8_SRGB_ALPHA ) ) ) + { + input_type = STBIR_TYPE_UINT8; + output_type = STBIR_TYPE_UINT8; + } + } + + // recalc the output and input strides + if ( info->input_stride_bytes == 0 ) + info->input_stride_bytes = info->channels * info->horizontal.scale_info.input_full_size * stbir__type_size[input_type]; + + if ( info->output_stride_bytes == 0 ) + info->output_stride_bytes = info->channels * info->horizontal.scale_info.output_sub_size * stbir__type_size[output_type]; + + // calc offset + info->output_data = ( (char*) resize->output_pixels ) + ( (ptrdiff_t) info->offset_y * (ptrdiff_t) resize->output_stride_in_bytes ) + ( info->offset_x * info->channels * stbir__type_size[output_type] ); + + info->in_pixels_cb = resize->input_cb; + info->user_data = resize->user_data; + info->out_pixels_cb = resize->output_cb; + + // setup the input format converters + if ( ( input_type == STBIR_TYPE_UINT8 ) || ( input_type == STBIR_TYPE_UINT16 ) ) + { + int non_scaled = 0; + + // check if we can run unscaled - 0-255.0/0-65535.0 instead of 0-1.0 (which is a tiny bit faster when doing linear 8->8 or 16->16) + if ( ( !info->alpha_weight ) && ( !info->alpha_unweight ) ) // don't short circuit when alpha weighting (get everything to 0-1.0 as usual) + if ( ( ( input_type == STBIR_TYPE_UINT8 ) && ( output_type == STBIR_TYPE_UINT8 ) ) || ( ( input_type == STBIR_TYPE_UINT16 ) && ( output_type == STBIR_TYPE_UINT16 ) ) ) + non_scaled = 1; + + if ( info->input_pixel_layout_internal <= STBIRI_4CHANNEL ) + decode_pixels = decode_simple_scaled_or_not[ input_type == STBIR_TYPE_UINT16 ][ non_scaled ]; + else + decode_pixels = decode_alphas_scaled_or_not[ ( info->input_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ input_type == STBIR_TYPE_UINT16 ][ non_scaled ]; + } + else + { + if ( info->input_pixel_layout_internal <= STBIRI_4CHANNEL ) + decode_pixels = decode_simple[ input_type - STBIR_TYPE_UINT8_SRGB ]; + else + decode_pixels = decode_alphas[ ( info->input_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ input_type - STBIR_TYPE_UINT8_SRGB ]; + } + + // setup the output format converters + if ( ( output_type == STBIR_TYPE_UINT8 ) || ( output_type == STBIR_TYPE_UINT16 ) ) + { + int non_scaled = 0; + + // check if we can run unscaled - 0-255.0/0-65535.0 instead of 0-1.0 (which is a tiny bit faster when doing linear 8->8 or 16->16) + if ( ( !info->alpha_weight ) && ( !info->alpha_unweight ) ) // don't short circuit when alpha weighting (get everything to 0-1.0 as usual) + if ( ( ( input_type == STBIR_TYPE_UINT8 ) && ( output_type == STBIR_TYPE_UINT8 ) ) || ( ( input_type == STBIR_TYPE_UINT16 ) && ( output_type == STBIR_TYPE_UINT16 ) ) ) + non_scaled = 1; + + if ( info->output_pixel_layout_internal <= STBIRI_4CHANNEL ) + encode_pixels = encode_simple_scaled_or_not[ output_type == STBIR_TYPE_UINT16 ][ non_scaled ]; + else + encode_pixels = encode_alphas_scaled_or_not[ ( info->output_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ output_type == STBIR_TYPE_UINT16 ][ non_scaled ]; + } + else + { + if ( info->output_pixel_layout_internal <= STBIRI_4CHANNEL ) + encode_pixels = encode_simple[ output_type - STBIR_TYPE_UINT8_SRGB ]; + else + encode_pixels = encode_alphas[ ( info->output_pixel_layout_internal - STBIRI_RGBA ) % ( STBIRI_AR-STBIRI_RGBA+1 ) ][ output_type - STBIR_TYPE_UINT8_SRGB ]; + } + + info->input_type = input_type; + info->output_type = output_type; + info->decode_pixels = decode_pixels; + info->encode_pixels = encode_pixels; +} + +static void stbir__clip( int * outx, int * outsubw, int outw, double * u0, double * u1 ) +{ + double per, adj; + int over; + + // do left/top edge + if ( *outx < 0 ) + { + per = ( (double)*outx ) / ( (double)*outsubw ); // is negative + adj = per * ( *u1 - *u0 ); + *u0 -= adj; // increases u0 + *outx = 0; + } + + // do right/bot edge + over = outw - ( *outx + *outsubw ); + if ( over < 0 ) + { + per = ( (double)over ) / ( (double)*outsubw ); // is negative + adj = per * ( *u1 - *u0 ); + *u1 += adj; // decrease u1 + *outsubw = outw - *outx; + } +} + +// converts a double to a rational that has less than one float bit of error (returns 0 if unable to do so) +static int stbir__double_to_rational(double f, stbir_uint32 limit, stbir_uint32 *numer, stbir_uint32 *denom, int limit_denom ) // limit_denom (1) or limit numer (0) +{ + double err; + stbir_uint64 top, bot; + stbir_uint64 numer_last = 0; + stbir_uint64 denom_last = 1; + stbir_uint64 numer_estimate = 1; + stbir_uint64 denom_estimate = 0; + + // scale to past float error range + top = (stbir_uint64)( f * (double)(1 << 25) ); + bot = 1 << 25; + + // keep refining, but usually stops in a few loops - usually 5 for bad cases + for(;;) + { + stbir_uint64 est, temp; + + // hit limit, break out and do best full range estimate + if ( ( ( limit_denom ) ? denom_estimate : numer_estimate ) >= limit ) + break; + + // is the current error less than 1 bit of a float? if so, we're done + if ( denom_estimate ) + { + err = ( (double)numer_estimate / (double)denom_estimate ) - f; + if ( err < 0.0 ) err = -err; + if ( err < ( 1.0 / (double)(1<<24) ) ) + { + // yup, found it + *numer = (stbir_uint32) numer_estimate; + *denom = (stbir_uint32) denom_estimate; + return 1; + } + } + + // no more refinement bits left? break out and do full range estimate + if ( bot == 0 ) + break; + + // gcd the estimate bits + est = top / bot; + temp = top % bot; + top = bot; + bot = temp; + + // move remainders + temp = est * denom_estimate + denom_last; + denom_last = denom_estimate; + denom_estimate = temp; + + // move remainders + temp = est * numer_estimate + numer_last; + numer_last = numer_estimate; + numer_estimate = temp; + } + + // we didn't fine anything good enough for float, use a full range estimate + if ( limit_denom ) + { + numer_estimate= (stbir_uint64)( f * (double)limit + 0.5 ); + denom_estimate = limit; + } + else + { + numer_estimate = limit; + denom_estimate = (stbir_uint64)( ( (double)limit / f ) + 0.5 ); + } + + *numer = (stbir_uint32) numer_estimate; + *denom = (stbir_uint32) denom_estimate; + + err = ( denom_estimate ) ? ( ( (double)(stbir_uint32)numer_estimate / (double)(stbir_uint32)denom_estimate ) - f ) : 1.0; + if ( err < 0.0 ) err = -err; + return ( err < ( 1.0 / (double)(1<<24) ) ) ? 1 : 0; +} + +static int stbir__calculate_region_transform( stbir__scale_info * scale_info, int output_full_range, int * output_offset, int output_sub_range, int input_full_range, double input_s0, double input_s1 ) +{ + double output_range, input_range, output_s, input_s, ratio, scale; + + input_s = input_s1 - input_s0; + + // null area + if ( ( output_full_range == 0 ) || ( input_full_range == 0 ) || + ( output_sub_range == 0 ) || ( input_s <= stbir__small_float ) ) + return 0; + + // are either of the ranges completely out of bounds? + if ( ( *output_offset >= output_full_range ) || ( ( *output_offset + output_sub_range ) <= 0 ) || ( input_s0 >= (1.0f-stbir__small_float) ) || ( input_s1 <= stbir__small_float ) ) + return 0; + + output_range = (double)output_full_range; + input_range = (double)input_full_range; + + output_s = ( (double)output_sub_range) / output_range; + + // figure out the scaling to use + ratio = output_s / input_s; + + // save scale before clipping + scale = ( output_range / input_range ) * ratio; + scale_info->scale = (float)scale; + scale_info->inv_scale = (float)( 1.0 / scale ); + + // clip output area to left/right output edges (and adjust input area) + stbir__clip( output_offset, &output_sub_range, output_full_range, &input_s0, &input_s1 ); + + // recalc input area + input_s = input_s1 - input_s0; + + // after clipping do we have zero input area? + if ( input_s <= stbir__small_float ) + return 0; + + // calculate and store the starting source offsets in output pixel space + scale_info->pixel_shift = (float) ( input_s0 * ratio * output_range ); + + scale_info->scale_is_rational = stbir__double_to_rational( scale, ( scale <= 1.0 ) ? output_full_range : input_full_range, &scale_info->scale_numerator, &scale_info->scale_denominator, ( scale >= 1.0 ) ); + + scale_info->input_full_size = input_full_range; + scale_info->output_sub_size = output_sub_range; + + return 1; +} + + +static void stbir__init_and_set_layout( STBIR_RESIZE * resize, stbir_pixel_layout pixel_layout, stbir_datatype data_type ) +{ + resize->input_cb = 0; + resize->output_cb = 0; + resize->user_data = resize; + resize->samplers = 0; + resize->needs_rebuild = 1; + resize->called_alloc = 0; + resize->horizontal_filter = STBIR_FILTER_DEFAULT; + resize->horizontal_filter_kernel = 0; resize->horizontal_filter_support = 0; + resize->vertical_filter = STBIR_FILTER_DEFAULT; + resize->vertical_filter_kernel = 0; resize->vertical_filter_support = 0; + resize->horizontal_edge = STBIR_EDGE_CLAMP; + resize->vertical_edge = STBIR_EDGE_CLAMP; + resize->input_s0 = 0; resize->input_t0 = 0; resize->input_s1 = 1; resize->input_t1 = 1; + resize->output_subx = 0; resize->output_suby = 0; resize->output_subw = resize->output_w; resize->output_subh = resize->output_h; + resize->input_data_type = data_type; + resize->output_data_type = data_type; + resize->input_pixel_layout_public = pixel_layout; + resize->output_pixel_layout_public = pixel_layout; +} + +STBIRDEF void stbir_resize_init( STBIR_RESIZE * resize, + const void *input_pixels, int input_w, int input_h, int input_stride_in_bytes, // stride can be zero + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, // stride can be zero + stbir_pixel_layout pixel_layout, stbir_datatype data_type ) +{ + resize->input_pixels = input_pixels; + resize->input_w = input_w; + resize->input_h = input_h; + resize->input_stride_in_bytes = input_stride_in_bytes; + resize->output_pixels = output_pixels; + resize->output_w = output_w; + resize->output_h = output_h; + resize->output_stride_in_bytes = output_stride_in_bytes; + resize->fast_alpha = 0; + + stbir__init_and_set_layout( resize, pixel_layout, data_type ); +} + +// You can update parameters any time after resize_init +STBIRDEF void stbir_set_datatypes( STBIR_RESIZE * resize, stbir_datatype input_type, stbir_datatype output_type ) // by default, datatype from resize_init +{ + resize->input_data_type = input_type; + resize->output_data_type = output_type; +} + +STBIRDEF void stbir_set_pixel_callbacks( STBIR_RESIZE * resize, stbir_input_callback * input_cb, stbir_output_callback * output_cb ) // no callbacks by default +{ + resize->input_cb = input_cb; + resize->output_cb = output_cb; +} + +STBIRDEF void stbir_set_user_data( STBIR_RESIZE * resize, void * user_data ) // pass back STBIR_RESIZE* by default +{ + resize->user_data = user_data; +} + +STBIRDEF void stbir_set_buffer_ptrs( STBIR_RESIZE * resize, const void * input_pixels, int input_stride_in_bytes, void * output_pixels, int output_stride_in_bytes ) +{ + resize->input_pixels = input_pixels; + resize->input_stride_in_bytes = input_stride_in_bytes; + resize->output_pixels = output_pixels; + resize->output_stride_in_bytes = output_stride_in_bytes; +} + + +STBIRDEF int stbir_set_edgemodes( STBIR_RESIZE * resize, stbir_edge horizontal_edge, stbir_edge vertical_edge ) // CLAMP by default +{ + resize->horizontal_edge = horizontal_edge; + resize->vertical_edge = vertical_edge; + resize->needs_rebuild = 1; + return 1; +} + +STBIRDEF int stbir_set_filters( STBIR_RESIZE * resize, stbir_filter horizontal_filter, stbir_filter vertical_filter ) // STBIR_DEFAULT_FILTER_UPSAMPLE/DOWNSAMPLE by default +{ + resize->horizontal_filter = horizontal_filter; + resize->vertical_filter = vertical_filter; + resize->needs_rebuild = 1; + return 1; +} + +STBIRDEF int stbir_set_filter_callbacks( STBIR_RESIZE * resize, stbir__kernel_callback * horizontal_filter, stbir__support_callback * horizontal_support, stbir__kernel_callback * vertical_filter, stbir__support_callback * vertical_support ) +{ + resize->horizontal_filter_kernel = horizontal_filter; resize->horizontal_filter_support = horizontal_support; + resize->vertical_filter_kernel = vertical_filter; resize->vertical_filter_support = vertical_support; + resize->needs_rebuild = 1; + return 1; +} + +STBIRDEF int stbir_set_pixel_layouts( STBIR_RESIZE * resize, stbir_pixel_layout input_pixel_layout, stbir_pixel_layout output_pixel_layout ) // sets new pixel layouts +{ + resize->input_pixel_layout_public = input_pixel_layout; + resize->output_pixel_layout_public = output_pixel_layout; + resize->needs_rebuild = 1; + return 1; +} + + +STBIRDEF int stbir_set_non_pm_alpha_speed_over_quality( STBIR_RESIZE * resize, int non_pma_alpha_speed_over_quality ) // sets alpha speed +{ + resize->fast_alpha = non_pma_alpha_speed_over_quality; + resize->needs_rebuild = 1; + return 1; +} + +STBIRDEF int stbir_set_input_subrect( STBIR_RESIZE * resize, double s0, double t0, double s1, double t1 ) // sets input region (full region by default) +{ + resize->input_s0 = s0; + resize->input_t0 = t0; + resize->input_s1 = s1; + resize->input_t1 = t1; + resize->needs_rebuild = 1; + + // are we inbounds? + if ( ( s1 < stbir__small_float ) || ( (s1-s0) < stbir__small_float ) || + ( t1 < stbir__small_float ) || ( (t1-t0) < stbir__small_float ) || + ( s0 > (1.0f-stbir__small_float) ) || + ( t0 > (1.0f-stbir__small_float) ) ) + return 0; + + return 1; +} + +STBIRDEF int stbir_set_output_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ) // sets input region (full region by default) +{ + resize->output_subx = subx; + resize->output_suby = suby; + resize->output_subw = subw; + resize->output_subh = subh; + resize->needs_rebuild = 1; + + // are we inbounds? + if ( ( subx >= resize->output_w ) || ( ( subx + subw ) <= 0 ) || ( suby >= resize->output_h ) || ( ( suby + subh ) <= 0 ) || ( subw == 0 ) || ( subh == 0 ) ) + return 0; + + return 1; +} + +STBIRDEF int stbir_set_pixel_subrect( STBIR_RESIZE * resize, int subx, int suby, int subw, int subh ) // sets both regions (full regions by default) +{ + double s0, t0, s1, t1; + + s0 = ( (double)subx ) / ( (double)resize->output_w ); + t0 = ( (double)suby ) / ( (double)resize->output_h ); + s1 = ( (double)(subx+subw) ) / ( (double)resize->output_w ); + t1 = ( (double)(suby+subh) ) / ( (double)resize->output_h ); + + resize->input_s0 = s0; + resize->input_t0 = t0; + resize->input_s1 = s1; + resize->input_t1 = t1; + resize->output_subx = subx; + resize->output_suby = suby; + resize->output_subw = subw; + resize->output_subh = subh; + resize->needs_rebuild = 1; + + // are we inbounds? + if ( ( subx >= resize->output_w ) || ( ( subx + subw ) <= 0 ) || ( suby >= resize->output_h ) || ( ( suby + subh ) <= 0 ) || ( subw == 0 ) || ( subh == 0 ) ) + return 0; + + return 1; +} + +static int stbir__perform_build( STBIR_RESIZE * resize, int splits ) +{ + stbir__contributors conservative = { 0, 0 }; + stbir__sampler horizontal, vertical; + int new_output_subx, new_output_suby; + stbir__info * out_info; + #ifdef STBIR_PROFILE + stbir__info profile_infod; // used to contain building profile info before everything is allocated + stbir__info * profile_info = &profile_infod; + #endif + + // have we already built the samplers? + if ( resize->samplers ) + return 0; + + #define STBIR_RETURN_ERROR_AND_ASSERT( exp ) STBIR_ASSERT( !(exp) ); if (exp) return 0; + STBIR_RETURN_ERROR_AND_ASSERT( (unsigned)resize->horizontal_filter >= STBIR_FILTER_OTHER) + STBIR_RETURN_ERROR_AND_ASSERT( (unsigned)resize->vertical_filter >= STBIR_FILTER_OTHER) + #undef STBIR_RETURN_ERROR_AND_ASSERT + + if ( splits <= 0 ) + return 0; + + STBIR_PROFILE_BUILD_FIRST_START( build ); + + new_output_subx = resize->output_subx; + new_output_suby = resize->output_suby; + + // do horizontal clip and scale calcs + if ( !stbir__calculate_region_transform( &horizontal.scale_info, resize->output_w, &new_output_subx, resize->output_subw, resize->input_w, resize->input_s0, resize->input_s1 ) ) + return 0; + + // do vertical clip and scale calcs + if ( !stbir__calculate_region_transform( &vertical.scale_info, resize->output_h, &new_output_suby, resize->output_subh, resize->input_h, resize->input_t0, resize->input_t1 ) ) + return 0; + + // if nothing to do, just return + if ( ( horizontal.scale_info.output_sub_size == 0 ) || ( vertical.scale_info.output_sub_size == 0 ) ) + return 0; + + stbir__set_sampler(&horizontal, resize->horizontal_filter, resize->horizontal_filter_kernel, resize->horizontal_filter_support, resize->horizontal_edge, &horizontal.scale_info, 1, resize->user_data ); + stbir__get_conservative_extents( &horizontal, &conservative, resize->user_data ); + stbir__set_sampler(&vertical, resize->vertical_filter, resize->horizontal_filter_kernel, resize->vertical_filter_support, resize->vertical_edge, &vertical.scale_info, 0, resize->user_data ); + + if ( ( vertical.scale_info.output_sub_size / splits ) < 4 ) // each split should be a minimum of 4 scanlines (handwavey choice) + { + splits = vertical.scale_info.output_sub_size / 4; + if ( splits == 0 ) splits = 1; + } + + STBIR_PROFILE_BUILD_START( alloc ); + out_info = stbir__alloc_internal_mem_and_build_samplers( &horizontal, &vertical, &conservative, resize->input_pixel_layout_public, resize->output_pixel_layout_public, splits, new_output_subx, new_output_suby, resize->fast_alpha, resize->user_data STBIR_ONLY_PROFILE_BUILD_SET_INFO ); + STBIR_PROFILE_BUILD_END( alloc ); + STBIR_PROFILE_BUILD_END( build ); + + if ( out_info ) + { + resize->splits = splits; + resize->samplers = out_info; + resize->needs_rebuild = 0; + #ifdef STBIR_PROFILE + STBIR_MEMCPY( &out_info->profile, &profile_infod.profile, sizeof( out_info->profile ) ); + #endif + return splits; + } + + return 0; +} + +void stbir_free_samplers( STBIR_RESIZE * resize ) +{ + if ( resize->samplers ) + { + stbir__free_internal_mem( resize->samplers ); + resize->samplers = 0; + resize->called_alloc = 0; + } +} + +STBIRDEF int stbir_build_samplers_with_splits( STBIR_RESIZE * resize, int splits ) +{ + if ( ( resize->samplers == 0 ) || ( resize->needs_rebuild ) ) + { + if ( resize->samplers ) + stbir_free_samplers( resize ); + + resize->called_alloc = 1; + return stbir__perform_build( resize, splits ); + } + + STBIR_PROFILE_BUILD_CLEAR( resize->samplers ); + + return 1; +} + +STBIRDEF int stbir_build_samplers( STBIR_RESIZE * resize ) +{ + return stbir_build_samplers_with_splits( resize, 1 ); +} + +STBIRDEF int stbir_resize_extended( STBIR_RESIZE * resize ) +{ + int result; + + if ( ( resize->samplers == 0 ) || ( resize->needs_rebuild ) ) + { + int alloc_state = resize->called_alloc; // remember allocated state + + if ( resize->samplers ) + { + stbir__free_internal_mem( resize->samplers ); + resize->samplers = 0; + } + + if ( !stbir_build_samplers( resize ) ) + return 0; + + resize->called_alloc = alloc_state; + + // if build_samplers succeeded (above), but there are no samplers set, then + // the area to stretch into was zero pixels, so don't do anything and return + // success + if ( resize->samplers == 0 ) + return 1; + } + else + { + // didn't build anything - clear it + STBIR_PROFILE_BUILD_CLEAR( resize->samplers ); + } + + + // update anything that can be changed without recalcing samplers + stbir__update_info_from_resize( resize->samplers, resize ); + + // do resize + result = stbir__perform_resize( resize->samplers, 0, resize->splits ); + + // if we alloced, then free + if ( !resize->called_alloc ) + { + stbir_free_samplers( resize ); + resize->samplers = 0; + } + + return result; +} + +STBIRDEF int stbir_resize_extended_split( STBIR_RESIZE * resize, int split_start, int split_count ) +{ + STBIR_ASSERT( resize->samplers ); + + // if we're just doing the whole thing, call full + if ( ( split_start == -1 ) || ( ( split_start == 0 ) && ( split_count == resize->splits ) ) ) + return stbir_resize_extended( resize ); + + // you **must** build samplers first when using split resize + if ( ( resize->samplers == 0 ) || ( resize->needs_rebuild ) ) + return 0; + + if ( ( split_start >= resize->splits ) || ( split_start < 0 ) || ( ( split_start + split_count ) > resize->splits ) || ( split_count <= 0 ) ) + return 0; + + // update anything that can be changed without recalcing samplers + stbir__update_info_from_resize( resize->samplers, resize ); + + // do resize + return stbir__perform_resize( resize->samplers, split_start, split_count ); +} + +static int stbir__check_output_stuff( void ** ret_ptr, int * ret_pitch, void * output_pixels, int type_size, int output_w, int output_h, int output_stride_in_bytes, stbir_internal_pixel_layout pixel_layout ) +{ + size_t size; + int pitch; + void * ptr; + + pitch = output_w * type_size * stbir__pixel_channels[ pixel_layout ]; + if ( pitch == 0 ) + return 0; + + if ( output_stride_in_bytes == 0 ) + output_stride_in_bytes = pitch; + + if ( output_stride_in_bytes < pitch ) + return 0; + + size = output_stride_in_bytes * output_h; + if ( size == 0 ) + return 0; + + *ret_ptr = 0; + *ret_pitch = output_stride_in_bytes; + + if ( output_pixels == 0 ) + { + ptr = STBIR_MALLOC( size, 0 ); + if ( ptr == 0 ) + return 0; + + *ret_ptr = ptr; + *ret_pitch = pitch; + } + + return 1; +} + + +STBIRDEF unsigned char * stbir_resize_uint8_linear( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout ) +{ + STBIR_RESIZE resize; + unsigned char * optr; + int opitch; + + if ( !stbir__check_output_stuff( (void**)&optr, &opitch, output_pixels, sizeof( unsigned char ), output_w, output_h, output_stride_in_bytes, stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ) ) + return 0; + + stbir_resize_init( &resize, + input_pixels, input_w, input_h, input_stride_in_bytes, + (optr) ? optr : output_pixels, output_w, output_h, opitch, + pixel_layout, STBIR_TYPE_UINT8 ); + + if ( !stbir_resize_extended( &resize ) ) + { + if ( optr ) + STBIR_FREE( optr, 0 ); + return 0; + } + + return (optr) ? optr : output_pixels; +} + +STBIRDEF unsigned char * stbir_resize_uint8_srgb( const unsigned char *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + unsigned char *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout ) +{ + STBIR_RESIZE resize; + unsigned char * optr; + int opitch; + + if ( !stbir__check_output_stuff( (void**)&optr, &opitch, output_pixels, sizeof( unsigned char ), output_w, output_h, output_stride_in_bytes, stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ) ) + return 0; + + stbir_resize_init( &resize, + input_pixels, input_w, input_h, input_stride_in_bytes, + (optr) ? optr : output_pixels, output_w, output_h, opitch, + pixel_layout, STBIR_TYPE_UINT8_SRGB ); + + if ( !stbir_resize_extended( &resize ) ) + { + if ( optr ) + STBIR_FREE( optr, 0 ); + return 0; + } + + return (optr) ? optr : output_pixels; +} + + +STBIRDEF float * stbir_resize_float_linear( const float *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + float *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout ) +{ + STBIR_RESIZE resize; + float * optr; + int opitch; + + if ( !stbir__check_output_stuff( (void**)&optr, &opitch, output_pixels, sizeof( float ), output_w, output_h, output_stride_in_bytes, stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ) ) + return 0; + + stbir_resize_init( &resize, + input_pixels, input_w, input_h, input_stride_in_bytes, + (optr) ? optr : output_pixels, output_w, output_h, opitch, + pixel_layout, STBIR_TYPE_FLOAT ); + + if ( !stbir_resize_extended( &resize ) ) + { + if ( optr ) + STBIR_FREE( optr, 0 ); + return 0; + } + + return (optr) ? optr : output_pixels; +} + + +STBIRDEF void * stbir_resize( const void *input_pixels , int input_w , int input_h, int input_stride_in_bytes, + void *output_pixels, int output_w, int output_h, int output_stride_in_bytes, + stbir_pixel_layout pixel_layout, stbir_datatype data_type, + stbir_edge edge, stbir_filter filter ) +{ + STBIR_RESIZE resize; + float * optr; + int opitch; + + if ( !stbir__check_output_stuff( (void**)&optr, &opitch, output_pixels, stbir__type_size[data_type], output_w, output_h, output_stride_in_bytes, stbir__pixel_layout_convert_public_to_internal[ pixel_layout ] ) ) + return 0; + + stbir_resize_init( &resize, + input_pixels, input_w, input_h, input_stride_in_bytes, + (optr) ? optr : output_pixels, output_w, output_h, output_stride_in_bytes, + pixel_layout, data_type ); + + resize.horizontal_edge = edge; + resize.vertical_edge = edge; + resize.horizontal_filter = filter; + resize.vertical_filter = filter; + + if ( !stbir_resize_extended( &resize ) ) + { + if ( optr ) + STBIR_FREE( optr, 0 ); + return 0; + } + + return (optr) ? optr : output_pixels; +} + +#ifdef STBIR_PROFILE + +STBIRDEF void stbir_resize_build_profile_info( STBIR_PROFILE_INFO * info, STBIR_RESIZE const * resize ) +{ + static char const * bdescriptions[6] = { "Building", "Allocating", "Horizontal sampler", "Vertical sampler", "Coefficient cleanup", "Coefficient piovot" } ; + stbir__info* samp = resize->samplers; + int i; + + typedef int testa[ (STBIR__ARRAY_SIZE( bdescriptions ) == (STBIR__ARRAY_SIZE( samp->profile.array )-1) )?1:-1]; + typedef int testb[ (sizeof( samp->profile.array ) == (sizeof(samp->profile.named)) )?1:-1]; + typedef int testc[ (sizeof( info->clocks ) >= (sizeof(samp->profile.named)) )?1:-1]; + + for( i = 0 ; i < STBIR__ARRAY_SIZE( bdescriptions ) ; i++) + info->clocks[i] = samp->profile.array[i+1]; + + info->total_clocks = samp->profile.named.total; + info->descriptions = bdescriptions; + info->count = STBIR__ARRAY_SIZE( bdescriptions ); +} + +STBIRDEF void stbir_resize_split_profile_info( STBIR_PROFILE_INFO * info, STBIR_RESIZE const * resize, int split_start, int split_count ) +{ + static char const * descriptions[7] = { "Looping", "Vertical sampling", "Horizontal sampling", "Scanline input", "Scanline output", "Alpha weighting", "Alpha unweighting" }; + stbir__per_split_info * split_info; + int s, i; + + typedef int testa[ (STBIR__ARRAY_SIZE( descriptions ) == (STBIR__ARRAY_SIZE( split_info->profile.array )-1) )?1:-1]; + typedef int testb[ (sizeof( split_info->profile.array ) == (sizeof(split_info->profile.named)) )?1:-1]; + typedef int testc[ (sizeof( info->clocks ) >= (sizeof(split_info->profile.named)) )?1:-1]; + + if ( split_start == -1 ) + { + split_start = 0; + split_count = resize->samplers->splits; + } + + if ( ( split_start >= resize->splits ) || ( split_start < 0 ) || ( ( split_start + split_count ) > resize->splits ) || ( split_count <= 0 ) ) + { + info->total_clocks = 0; + info->descriptions = 0; + info->count = 0; + return; + } + + split_info = resize->samplers->split_info + split_start; + + // sum up the profile from all the splits + for( i = 0 ; i < STBIR__ARRAY_SIZE( descriptions ) ; i++ ) + { + stbir_uint64 sum = 0; + for( s = 0 ; s < split_count ; s++ ) + sum += split_info[s].profile.array[i+1]; + info->clocks[i] = sum; + } + + info->total_clocks = split_info->profile.named.total; + info->descriptions = descriptions; + info->count = STBIR__ARRAY_SIZE( descriptions ); +} + +STBIRDEF void stbir_resize_extended_profile_info( STBIR_PROFILE_INFO * info, STBIR_RESIZE const * resize ) +{ + stbir_resize_split_profile_info( info, resize, -1, 0 ); +} + +#endif // STBIR_PROFILE + +#undef STBIR_BGR +#undef STBIR_1CHANNEL +#undef STBIR_2CHANNEL +#undef STBIR_RGB +#undef STBIR_RGBA +#undef STBIR_4CHANNEL +#undef STBIR_BGRA +#undef STBIR_ARGB +#undef STBIR_ABGR +#undef STBIR_RA +#undef STBIR_AR +#undef STBIR_RGBA_PM +#undef STBIR_BGRA_PM +#undef STBIR_ARGB_PM +#undef STBIR_ABGR_PM +#undef STBIR_RA_PM +#undef STBIR_AR_PM + +#endif // STB_IMAGE_RESIZE_IMPLEMENTATION + +#else // STB_IMAGE_RESIZE_HORIZONTALS&STB_IMAGE_RESIZE_DO_VERTICALS + +// we reinclude the header file to define all the horizontal functions +// specializing each function for the number of coeffs is 20-40% faster *OVERALL* + +// by including the header file again this way, we can still debug the functions + +#define STBIR_strs_join2( start, mid, end ) start##mid##end +#define STBIR_strs_join1( start, mid, end ) STBIR_strs_join2( start, mid, end ) + +#define STBIR_strs_join24( start, mid1, mid2, end ) start##mid1##mid2##end +#define STBIR_strs_join14( start, mid1, mid2, end ) STBIR_strs_join24( start, mid1, mid2, end ) + +#ifdef STB_IMAGE_RESIZE_DO_CODERS + +#ifdef stbir__decode_suffix +#define STBIR__CODER_NAME( name ) STBIR_strs_join1( name, _, stbir__decode_suffix ) +#else +#define STBIR__CODER_NAME( name ) name +#endif + +#ifdef stbir__decode_swizzle +#define stbir__decode_simdf8_flip(reg) STBIR_strs_join1( STBIR_strs_join1( STBIR_strs_join1( STBIR_strs_join1( stbir__simdf8_0123to,stbir__decode_order0,stbir__decode_order1),stbir__decode_order2,stbir__decode_order3),stbir__decode_order0,stbir__decode_order1),stbir__decode_order2,stbir__decode_order3)(reg, reg) +#define stbir__decode_simdf4_flip(reg) STBIR_strs_join1( STBIR_strs_join1( stbir__simdf_0123to,stbir__decode_order0,stbir__decode_order1),stbir__decode_order2,stbir__decode_order3)(reg, reg) +#define stbir__encode_simdf8_unflip(reg) STBIR_strs_join1( STBIR_strs_join1( STBIR_strs_join1( STBIR_strs_join1( stbir__simdf8_0123to,stbir__encode_order0,stbir__encode_order1),stbir__encode_order2,stbir__encode_order3),stbir__encode_order0,stbir__encode_order1),stbir__encode_order2,stbir__encode_order3)(reg, reg) +#define stbir__encode_simdf4_unflip(reg) STBIR_strs_join1( STBIR_strs_join1( stbir__simdf_0123to,stbir__encode_order0,stbir__encode_order1),stbir__encode_order2,stbir__encode_order3)(reg, reg) +#else +#define stbir__decode_order0 0 +#define stbir__decode_order1 1 +#define stbir__decode_order2 2 +#define stbir__decode_order3 3 +#define stbir__encode_order0 0 +#define stbir__encode_order1 1 +#define stbir__encode_order2 2 +#define stbir__encode_order3 3 +#define stbir__decode_simdf8_flip(reg) +#define stbir__decode_simdf4_flip(reg) +#define stbir__encode_simdf8_unflip(reg) +#define stbir__encode_simdf4_unflip(reg) +#endif + +#ifdef STBIR_SIMD8 +#define stbir__encode_simdfX_unflip stbir__encode_simdf8_unflip +#else +#define stbir__encode_simdfX_unflip stbir__encode_simdf4_unflip +#endif + +static void STBIR__CODER_NAME( stbir__decode_uint8_linear_scaled )( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + unsigned char const * input = (unsigned char const*)inputp; + + #ifdef STBIR_SIMD + unsigned char const * end_input_m16 = input + width_times_channels - 16; + if ( width_times_channels >= 16 ) + { + decode_end -= 16; + for(;;) + { + #ifdef STBIR_SIMD8 + stbir__simdi i; stbir__simdi8 o0,o1; + stbir__simdf8 of0, of1; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi8_expand_u8_to_u32( o0, o1, i ); + stbir__simdi8_convert_i32_to_float( of0, o0 ); + stbir__simdi8_convert_i32_to_float( of1, o1 ); + stbir__simdf8_mult( of0, of0, STBIR_max_uint8_as_float_inverted8); + stbir__simdf8_mult( of1, of1, STBIR_max_uint8_as_float_inverted8); + stbir__decode_simdf8_flip( of0 ); + stbir__decode_simdf8_flip( of1 ); + stbir__simdf8_store( decode + 0, of0 ); + stbir__simdf8_store( decode + 8, of1 ); + #else + stbir__simdi i, o0, o1, o2, o3; + stbir__simdf of0, of1, of2, of3; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi_expand_u8_to_u32( o0,o1,o2,o3,i); + stbir__simdi_convert_i32_to_float( of0, o0 ); + stbir__simdi_convert_i32_to_float( of1, o1 ); + stbir__simdi_convert_i32_to_float( of2, o2 ); + stbir__simdi_convert_i32_to_float( of3, o3 ); + stbir__simdf_mult( of0, of0, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) ); + stbir__simdf_mult( of1, of1, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) ); + stbir__simdf_mult( of2, of2, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) ); + stbir__simdf_mult( of3, of3, STBIR__CONSTF(STBIR_max_uint8_as_float_inverted) ); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__decode_simdf4_flip( of2 ); + stbir__decode_simdf4_flip( of3 ); + stbir__simdf_store( decode + 0, of0 ); + stbir__simdf_store( decode + 4, of1 ); + stbir__simdf_store( decode + 8, of2 ); + stbir__simdf_store( decode + 12, of3 ); + #endif + decode += 16; + input += 16; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 16 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m16; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = ((float)(input[stbir__decode_order0])) * stbir__max_uint8_as_float_inverted; + decode[1-4] = ((float)(input[stbir__decode_order1])) * stbir__max_uint8_as_float_inverted; + decode[2-4] = ((float)(input[stbir__decode_order2])) * stbir__max_uint8_as_float_inverted; + decode[3-4] = ((float)(input[stbir__decode_order3])) * stbir__max_uint8_as_float_inverted; + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = ((float)(input[stbir__decode_order0])) * stbir__max_uint8_as_float_inverted; + #if stbir__coder_min_num >= 2 + decode[1] = ((float)(input[stbir__decode_order1])) * stbir__max_uint8_as_float_inverted; + #endif + #if stbir__coder_min_num >= 3 + decode[2] = ((float)(input[stbir__decode_order2])) * stbir__max_uint8_as_float_inverted; + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME( stbir__encode_uint8_linear_scaled )( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char *) outputp; + unsigned char * end_output = ( (unsigned char *) output ) + width_times_channels; + + #ifdef STBIR_SIMD + if ( width_times_channels >= stbir__simdfX_float_count*2 ) + { + float const * end_encode_m8 = encode + width_times_channels - stbir__simdfX_float_count*2; + end_output -= stbir__simdfX_float_count*2; + for(;;) + { + stbir__simdfX e0, e1; + stbir__simdi i; + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdfX_madd_mem( e0, STBIR_simd_point5X, STBIR_max_uint8_as_floatX, encode ); + stbir__simdfX_madd_mem( e1, STBIR_simd_point5X, STBIR_max_uint8_as_floatX, encode+stbir__simdfX_float_count ); + stbir__encode_simdfX_unflip( e0 ); + stbir__encode_simdfX_unflip( e1 ); + #ifdef STBIR_SIMD8 + stbir__simdf8_pack_to_16bytes( i, e0, e1 ); + stbir__simdi_store( output, i ); + #else + stbir__simdf_pack_to_8bytes( i, e0, e1 ); + stbir__simdi_store2( output, i ); + #endif + encode += stbir__simdfX_float_count*2; + output += stbir__simdfX_float_count*2; + if ( output <= end_output ) + continue; + if ( output == ( end_output + stbir__simdfX_float_count*2 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + stbir__simdf e0; + stbir__simdi i0; + STBIR_NO_UNROLL(encode); + stbir__simdf_load( e0, encode ); + stbir__simdf_madd( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), e0 ); + stbir__encode_simdf4_unflip( e0 ); + stbir__simdf_pack_to_8bytes( i0, e0, e0 ); // only use first 4 + *(int*)(output-4) = stbir__simdi_to_int( i0 ); + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + stbir__simdf e0; + STBIR_NO_UNROLL(encode); + stbir__simdf_madd1_mem( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), encode+stbir__encode_order0 ); output[0] = stbir__simdf_convert_float_to_uint8( e0 ); + #if stbir__coder_min_num >= 2 + stbir__simdf_madd1_mem( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), encode+stbir__encode_order1 ); output[1] = stbir__simdf_convert_float_to_uint8( e0 ); + #endif + #if stbir__coder_min_num >= 3 + stbir__simdf_madd1_mem( e0, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint8_as_float), encode+stbir__encode_order2 ); output[2] = stbir__simdf_convert_float_to_uint8( e0 ); + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif + + #else + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + float f; + f = encode[stbir__encode_order0] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[0-4] = (unsigned char)f; + f = encode[stbir__encode_order1] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[1-4] = (unsigned char)f; + f = encode[stbir__encode_order2] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[2-4] = (unsigned char)f; + f = encode[stbir__encode_order3] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[3-4] = (unsigned char)f; + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + float f; + STBIR_NO_UNROLL(encode); + f = encode[stbir__encode_order0] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[0] = (unsigned char)f; + #if stbir__coder_min_num >= 2 + f = encode[stbir__encode_order1] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[1] = (unsigned char)f; + #endif + #if stbir__coder_min_num >= 3 + f = encode[stbir__encode_order2] * stbir__max_uint8_as_float + 0.5f; STBIR_CLAMP(f, 0, 255); output[2] = (unsigned char)f; + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif + #endif +} + +static void STBIR__CODER_NAME(stbir__decode_uint8_linear)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + unsigned char const * input = (unsigned char const*)inputp; + + #ifdef STBIR_SIMD + unsigned char const * end_input_m16 = input + width_times_channels - 16; + if ( width_times_channels >= 16 ) + { + decode_end -= 16; + for(;;) + { + #ifdef STBIR_SIMD8 + stbir__simdi i; stbir__simdi8 o0,o1; + stbir__simdf8 of0, of1; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi8_expand_u8_to_u32( o0, o1, i ); + stbir__simdi8_convert_i32_to_float( of0, o0 ); + stbir__simdi8_convert_i32_to_float( of1, o1 ); + stbir__decode_simdf8_flip( of0 ); + stbir__decode_simdf8_flip( of1 ); + stbir__simdf8_store( decode + 0, of0 ); + stbir__simdf8_store( decode + 8, of1 ); + #else + stbir__simdi i, o0, o1, o2, o3; + stbir__simdf of0, of1, of2, of3; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi_expand_u8_to_u32( o0,o1,o2,o3,i); + stbir__simdi_convert_i32_to_float( of0, o0 ); + stbir__simdi_convert_i32_to_float( of1, o1 ); + stbir__simdi_convert_i32_to_float( of2, o2 ); + stbir__simdi_convert_i32_to_float( of3, o3 ); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__decode_simdf4_flip( of2 ); + stbir__decode_simdf4_flip( of3 ); + stbir__simdf_store( decode + 0, of0 ); + stbir__simdf_store( decode + 4, of1 ); + stbir__simdf_store( decode + 8, of2 ); + stbir__simdf_store( decode + 12, of3 ); +#endif + decode += 16; + input += 16; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 16 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m16; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = ((float)(input[stbir__decode_order0])); + decode[1-4] = ((float)(input[stbir__decode_order1])); + decode[2-4] = ((float)(input[stbir__decode_order2])); + decode[3-4] = ((float)(input[stbir__decode_order3])); + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = ((float)(input[stbir__decode_order0])); + #if stbir__coder_min_num >= 2 + decode[1] = ((float)(input[stbir__decode_order1])); + #endif + #if stbir__coder_min_num >= 3 + decode[2] = ((float)(input[stbir__decode_order2])); + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME( stbir__encode_uint8_linear )( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char *) outputp; + unsigned char * end_output = ( (unsigned char *) output ) + width_times_channels; + + #ifdef STBIR_SIMD + if ( width_times_channels >= stbir__simdfX_float_count*2 ) + { + float const * end_encode_m8 = encode + width_times_channels - stbir__simdfX_float_count*2; + end_output -= stbir__simdfX_float_count*2; + for(;;) + { + stbir__simdfX e0, e1; + stbir__simdi i; + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdfX_add_mem( e0, STBIR_simd_point5X, encode ); + stbir__simdfX_add_mem( e1, STBIR_simd_point5X, encode+stbir__simdfX_float_count ); + stbir__encode_simdfX_unflip( e0 ); + stbir__encode_simdfX_unflip( e1 ); + #ifdef STBIR_SIMD8 + stbir__simdf8_pack_to_16bytes( i, e0, e1 ); + stbir__simdi_store( output, i ); + #else + stbir__simdf_pack_to_8bytes( i, e0, e1 ); + stbir__simdi_store2( output, i ); + #endif + encode += stbir__simdfX_float_count*2; + output += stbir__simdfX_float_count*2; + if ( output <= end_output ) + continue; + if ( output == ( end_output + stbir__simdfX_float_count*2 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + stbir__simdf e0; + stbir__simdi i0; + STBIR_NO_UNROLL(encode); + stbir__simdf_load( e0, encode ); + stbir__simdf_add( e0, STBIR__CONSTF(STBIR_simd_point5), e0 ); + stbir__encode_simdf4_unflip( e0 ); + stbir__simdf_pack_to_8bytes( i0, e0, e0 ); // only use first 4 + *(int*)(output-4) = stbir__simdi_to_int( i0 ); + output += 4; + encode += 4; + } + output -= 4; + #endif + + #else + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + float f; + f = encode[stbir__encode_order0] + 0.5f; STBIR_CLAMP(f, 0, 255); output[0-4] = (unsigned char)f; + f = encode[stbir__encode_order1] + 0.5f; STBIR_CLAMP(f, 0, 255); output[1-4] = (unsigned char)f; + f = encode[stbir__encode_order2] + 0.5f; STBIR_CLAMP(f, 0, 255); output[2-4] = (unsigned char)f; + f = encode[stbir__encode_order3] + 0.5f; STBIR_CLAMP(f, 0, 255); output[3-4] = (unsigned char)f; + output += 4; + encode += 4; + } + output -= 4; + #endif + + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + float f; + STBIR_NO_UNROLL(encode); + f = encode[stbir__encode_order0] + 0.5f; STBIR_CLAMP(f, 0, 255); output[0] = (unsigned char)f; + #if stbir__coder_min_num >= 2 + f = encode[stbir__encode_order1] + 0.5f; STBIR_CLAMP(f, 0, 255); output[1] = (unsigned char)f; + #endif + #if stbir__coder_min_num >= 3 + f = encode[stbir__encode_order2] + 0.5f; STBIR_CLAMP(f, 0, 255); output[2] = (unsigned char)f; + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME(stbir__decode_uint8_srgb)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float const * decode_end = (float*) decode + width_times_channels; + unsigned char const * input = (unsigned char const *)inputp; + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + decode[0-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order0 ] ]; + decode[1-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order1 ] ]; + decode[2-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order2 ] ]; + decode[3-4] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order3 ] ]; + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order0 ] ]; + #if stbir__coder_min_num >= 2 + decode[1] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order1 ] ]; + #endif + #if stbir__coder_min_num >= 3 + decode[2] = stbir__srgb_uchar_to_linear_float[ input[ stbir__decode_order2 ] ]; + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + +#define stbir__min_max_shift20( i, f ) \ + stbir__simdf_max( f, f, stbir_simdf_casti(STBIR__CONSTI( STBIR_almost_zero )) ); \ + stbir__simdf_min( f, f, stbir_simdf_casti(STBIR__CONSTI( STBIR_almost_one )) ); \ + stbir__simdi_32shr( i, stbir_simdi_castf( f ), 20 ); + +#define stbir__scale_and_convert( i, f ) \ + stbir__simdf_madd( f, STBIR__CONSTF( STBIR_simd_point5 ), STBIR__CONSTF( STBIR_max_uint8_as_float ), f ); \ + stbir__simdf_max( f, f, stbir__simdf_zeroP() ); \ + stbir__simdf_min( f, f, STBIR__CONSTF( STBIR_max_uint8_as_float ) ); \ + stbir__simdf_convert_float_to_i32( i, f ); + +#define stbir__linear_to_srgb_finish( i, f ) \ +{ \ + stbir__simdi temp; \ + stbir__simdi_32shr( temp, stbir_simdi_castf( f ), 12 ) ; \ + stbir__simdi_and( temp, temp, STBIR__CONSTI(STBIR_mastissa_mask) ); \ + stbir__simdi_or( temp, temp, STBIR__CONSTI(STBIR_topscale) ); \ + stbir__simdi_16madd( i, i, temp ); \ + stbir__simdi_32shr( i, i, 16 ); \ +} + +#define stbir__simdi_table_lookup2( v0,v1, table ) \ +{ \ + stbir__simdi_u32 temp0,temp1; \ + temp0.m128i_i128 = v0; \ + temp1.m128i_i128 = v1; \ + temp0.m128i_u32[0] = table[temp0.m128i_i32[0]]; temp0.m128i_u32[1] = table[temp0.m128i_i32[1]]; temp0.m128i_u32[2] = table[temp0.m128i_i32[2]]; temp0.m128i_u32[3] = table[temp0.m128i_i32[3]]; \ + temp1.m128i_u32[0] = table[temp1.m128i_i32[0]]; temp1.m128i_u32[1] = table[temp1.m128i_i32[1]]; temp1.m128i_u32[2] = table[temp1.m128i_i32[2]]; temp1.m128i_u32[3] = table[temp1.m128i_i32[3]]; \ + v0 = temp0.m128i_i128; \ + v1 = temp1.m128i_i128; \ +} + +#define stbir__simdi_table_lookup3( v0,v1,v2, table ) \ +{ \ + stbir__simdi_u32 temp0,temp1,temp2; \ + temp0.m128i_i128 = v0; \ + temp1.m128i_i128 = v1; \ + temp2.m128i_i128 = v2; \ + temp0.m128i_u32[0] = table[temp0.m128i_i32[0]]; temp0.m128i_u32[1] = table[temp0.m128i_i32[1]]; temp0.m128i_u32[2] = table[temp0.m128i_i32[2]]; temp0.m128i_u32[3] = table[temp0.m128i_i32[3]]; \ + temp1.m128i_u32[0] = table[temp1.m128i_i32[0]]; temp1.m128i_u32[1] = table[temp1.m128i_i32[1]]; temp1.m128i_u32[2] = table[temp1.m128i_i32[2]]; temp1.m128i_u32[3] = table[temp1.m128i_i32[3]]; \ + temp2.m128i_u32[0] = table[temp2.m128i_i32[0]]; temp2.m128i_u32[1] = table[temp2.m128i_i32[1]]; temp2.m128i_u32[2] = table[temp2.m128i_i32[2]]; temp2.m128i_u32[3] = table[temp2.m128i_i32[3]]; \ + v0 = temp0.m128i_i128; \ + v1 = temp1.m128i_i128; \ + v2 = temp2.m128i_i128; \ +} + +#define stbir__simdi_table_lookup4( v0,v1,v2,v3, table ) \ +{ \ + stbir__simdi_u32 temp0,temp1,temp2,temp3; \ + temp0.m128i_i128 = v0; \ + temp1.m128i_i128 = v1; \ + temp2.m128i_i128 = v2; \ + temp3.m128i_i128 = v3; \ + temp0.m128i_u32[0] = table[temp0.m128i_i32[0]]; temp0.m128i_u32[1] = table[temp0.m128i_i32[1]]; temp0.m128i_u32[2] = table[temp0.m128i_i32[2]]; temp0.m128i_u32[3] = table[temp0.m128i_i32[3]]; \ + temp1.m128i_u32[0] = table[temp1.m128i_i32[0]]; temp1.m128i_u32[1] = table[temp1.m128i_i32[1]]; temp1.m128i_u32[2] = table[temp1.m128i_i32[2]]; temp1.m128i_u32[3] = table[temp1.m128i_i32[3]]; \ + temp2.m128i_u32[0] = table[temp2.m128i_i32[0]]; temp2.m128i_u32[1] = table[temp2.m128i_i32[1]]; temp2.m128i_u32[2] = table[temp2.m128i_i32[2]]; temp2.m128i_u32[3] = table[temp2.m128i_i32[3]]; \ + temp3.m128i_u32[0] = table[temp3.m128i_i32[0]]; temp3.m128i_u32[1] = table[temp3.m128i_i32[1]]; temp3.m128i_u32[2] = table[temp3.m128i_i32[2]]; temp3.m128i_u32[3] = table[temp3.m128i_i32[3]]; \ + v0 = temp0.m128i_i128; \ + v1 = temp1.m128i_i128; \ + v2 = temp2.m128i_i128; \ + v3 = temp3.m128i_i128; \ +} + +static void STBIR__CODER_NAME( stbir__encode_uint8_srgb )( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char*) outputp; + unsigned char * end_output = ( (unsigned char*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + stbir_uint32 const * to_srgb = fp32_to_srgb8_tab4 - (127-13)*8; + + if ( width_times_channels >= 16 ) + { + float const * end_encode_m16 = encode + width_times_channels - 16; + end_output -= 16; + for(;;) + { + stbir__simdf f0, f1, f2, f3; + stbir__simdi i0, i1, i2, i3; + STBIR_SIMD_NO_UNROLL(encode); + + stbir__simdf_load4_transposed( f0, f1, f2, f3, encode ); + + stbir__min_max_shift20( i0, f0 ); + stbir__min_max_shift20( i1, f1 ); + stbir__min_max_shift20( i2, f2 ); + stbir__min_max_shift20( i3, f3 ); + + stbir__simdi_table_lookup4( i0, i1, i2, i3, to_srgb ); + + stbir__linear_to_srgb_finish( i0, f0 ); + stbir__linear_to_srgb_finish( i1, f1 ); + stbir__linear_to_srgb_finish( i2, f2 ); + stbir__linear_to_srgb_finish( i3, f3 ); + + stbir__interleave_pack_and_store_16_u8( output, STBIR_strs_join1(i, ,stbir__encode_order0), STBIR_strs_join1(i, ,stbir__encode_order1), STBIR_strs_join1(i, ,stbir__encode_order2), STBIR_strs_join1(i, ,stbir__encode_order3) ); + + encode += 16; + output += 16; + if ( output <= end_output ) + continue; + if ( output == ( end_output + 16 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m16; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while ( output <= end_output ) + { + STBIR_SIMD_NO_UNROLL(encode); + + output[0-4] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order0] ); + output[1-4] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order1] ); + output[2-4] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order2] ); + output[3-4] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order3] ); + + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + STBIR_NO_UNROLL(encode); + output[0] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order0] ); + #if stbir__coder_min_num >= 2 + output[1] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order1] ); + #endif + #if stbir__coder_min_num >= 3 + output[2] = stbir__linear_to_srgb_uchar( encode[stbir__encode_order2] ); + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif +} + +#if ( stbir__coder_min_num == 4 ) || ( ( stbir__coder_min_num == 1 ) && ( !defined(stbir__decode_swizzle) ) ) + +static void STBIR__CODER_NAME(stbir__decode_uint8_srgb4_linearalpha)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float const * decode_end = (float*) decode + width_times_channels; + unsigned char const * input = (unsigned char const *)inputp; + do { + decode[0] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0] ]; + decode[1] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order1] ]; + decode[2] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order2] ]; + decode[3] = ( (float) input[stbir__decode_order3] ) * stbir__max_uint8_as_float_inverted; + input += 4; + decode += 4; + } while( decode < decode_end ); +} + + +static void STBIR__CODER_NAME( stbir__encode_uint8_srgb4_linearalpha )( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char*) outputp; + unsigned char * end_output = ( (unsigned char*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + stbir_uint32 const * to_srgb = fp32_to_srgb8_tab4 - (127-13)*8; + + if ( width_times_channels >= 16 ) + { + float const * end_encode_m16 = encode + width_times_channels - 16; + end_output -= 16; + for(;;) + { + stbir__simdf f0, f1, f2, f3; + stbir__simdi i0, i1, i2, i3; + + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdf_load4_transposed( f0, f1, f2, f3, encode ); + + stbir__min_max_shift20( i0, f0 ); + stbir__min_max_shift20( i1, f1 ); + stbir__min_max_shift20( i2, f2 ); + stbir__scale_and_convert( i3, f3 ); + + stbir__simdi_table_lookup3( i0, i1, i2, to_srgb ); + + stbir__linear_to_srgb_finish( i0, f0 ); + stbir__linear_to_srgb_finish( i1, f1 ); + stbir__linear_to_srgb_finish( i2, f2 ); + + stbir__interleave_pack_and_store_16_u8( output, STBIR_strs_join1(i, ,stbir__encode_order0), STBIR_strs_join1(i, ,stbir__encode_order1), STBIR_strs_join1(i, ,stbir__encode_order2), STBIR_strs_join1(i, ,stbir__encode_order3) ); + + output += 16; + encode += 16; + + if ( output <= end_output ) + continue; + if ( output == ( end_output + 16 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m16; + } + return; + } + #endif + + do { + float f; + STBIR_SIMD_NO_UNROLL(encode); + + output[stbir__decode_order0] = stbir__linear_to_srgb_uchar( encode[0] ); + output[stbir__decode_order1] = stbir__linear_to_srgb_uchar( encode[1] ); + output[stbir__decode_order2] = stbir__linear_to_srgb_uchar( encode[2] ); + + f = encode[3] * stbir__max_uint8_as_float + 0.5f; + STBIR_CLAMP(f, 0, 255); + output[stbir__decode_order3] = (unsigned char) f; + + output += 4; + encode += 4; + } while( output < end_output ); +} + +#endif + +#if ( stbir__coder_min_num == 2 ) || ( ( stbir__coder_min_num == 1 ) && ( !defined(stbir__decode_swizzle) ) ) + +static void STBIR__CODER_NAME(stbir__decode_uint8_srgb2_linearalpha)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float const * decode_end = (float*) decode + width_times_channels; + unsigned char const * input = (unsigned char const *)inputp; + decode += 4; + while( decode <= decode_end ) + { + decode[0-4] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0] ]; + decode[1-4] = ( (float) input[stbir__decode_order1] ) * stbir__max_uint8_as_float_inverted; + decode[2-4] = stbir__srgb_uchar_to_linear_float[ input[stbir__decode_order0+2] ]; + decode[3-4] = ( (float) input[stbir__decode_order1+2] ) * stbir__max_uint8_as_float_inverted; + input += 4; + decode += 4; + } + decode -= 4; + if( decode < decode_end ) + { + decode[0] = stbir__srgb_uchar_to_linear_float[ stbir__decode_order0 ]; + decode[1] = ( (float) input[stbir__decode_order1] ) * stbir__max_uint8_as_float_inverted; + } +} + +static void STBIR__CODER_NAME( stbir__encode_uint8_srgb2_linearalpha )( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned char STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned char*) outputp; + unsigned char * end_output = ( (unsigned char*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + stbir_uint32 const * to_srgb = fp32_to_srgb8_tab4 - (127-13)*8; + + if ( width_times_channels >= 16 ) + { + float const * end_encode_m16 = encode + width_times_channels - 16; + end_output -= 16; + for(;;) + { + stbir__simdf f0, f1, f2, f3; + stbir__simdi i0, i1, i2, i3; + + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdf_load4_transposed( f0, f1, f2, f3, encode ); + + stbir__min_max_shift20( i0, f0 ); + stbir__scale_and_convert( i1, f1 ); + stbir__min_max_shift20( i2, f2 ); + stbir__scale_and_convert( i3, f3 ); + + stbir__simdi_table_lookup2( i0, i2, to_srgb ); + + stbir__linear_to_srgb_finish( i0, f0 ); + stbir__linear_to_srgb_finish( i2, f2 ); + + stbir__interleave_pack_and_store_16_u8( output, STBIR_strs_join1(i, ,stbir__encode_order0), STBIR_strs_join1(i, ,stbir__encode_order1), STBIR_strs_join1(i, ,stbir__encode_order2), STBIR_strs_join1(i, ,stbir__encode_order3) ); + + output += 16; + encode += 16; + if ( output <= end_output ) + continue; + if ( output == ( end_output + 16 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m16; + } + return; + } + #endif + + do { + float f; + STBIR_SIMD_NO_UNROLL(encode); + + output[stbir__decode_order0] = stbir__linear_to_srgb_uchar( encode[0] ); + + f = encode[1] * stbir__max_uint8_as_float + 0.5f; + STBIR_CLAMP(f, 0, 255); + output[stbir__decode_order1] = (unsigned char) f; + + output += 2; + encode += 2; + } while( output < end_output ); +} + +#endif + +static void STBIR__CODER_NAME(stbir__decode_uint16_linear_scaled)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + unsigned short const * input = (unsigned short const *)inputp; + + #ifdef STBIR_SIMD + unsigned short const * end_input_m8 = input + width_times_channels - 8; + if ( width_times_channels >= 8 ) + { + decode_end -= 8; + for(;;) + { + #ifdef STBIR_SIMD8 + stbir__simdi i; stbir__simdi8 o; + stbir__simdf8 of; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi8_expand_u16_to_u32( o, i ); + stbir__simdi8_convert_i32_to_float( of, o ); + stbir__simdf8_mult( of, of, STBIR_max_uint16_as_float_inverted8); + stbir__decode_simdf8_flip( of ); + stbir__simdf8_store( decode + 0, of ); + #else + stbir__simdi i, o0, o1; + stbir__simdf of0, of1; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi_expand_u16_to_u32( o0,o1,i ); + stbir__simdi_convert_i32_to_float( of0, o0 ); + stbir__simdi_convert_i32_to_float( of1, o1 ); + stbir__simdf_mult( of0, of0, STBIR__CONSTF(STBIR_max_uint16_as_float_inverted) ); + stbir__simdf_mult( of1, of1, STBIR__CONSTF(STBIR_max_uint16_as_float_inverted)); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__simdf_store( decode + 0, of0 ); + stbir__simdf_store( decode + 4, of1 ); + #endif + decode += 8; + input += 8; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 8 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m8; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = ((float)(input[stbir__decode_order0])) * stbir__max_uint16_as_float_inverted; + decode[1-4] = ((float)(input[stbir__decode_order1])) * stbir__max_uint16_as_float_inverted; + decode[2-4] = ((float)(input[stbir__decode_order2])) * stbir__max_uint16_as_float_inverted; + decode[3-4] = ((float)(input[stbir__decode_order3])) * stbir__max_uint16_as_float_inverted; + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = ((float)(input[stbir__decode_order0])) * stbir__max_uint16_as_float_inverted; + #if stbir__coder_min_num >= 2 + decode[1] = ((float)(input[stbir__decode_order1])) * stbir__max_uint16_as_float_inverted; + #endif + #if stbir__coder_min_num >= 3 + decode[2] = ((float)(input[stbir__decode_order2])) * stbir__max_uint16_as_float_inverted; + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + + +static void STBIR__CODER_NAME(stbir__encode_uint16_linear_scaled)( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned short STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned short*) outputp; + unsigned short * end_output = ( (unsigned short*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + { + if ( width_times_channels >= stbir__simdfX_float_count*2 ) + { + float const * end_encode_m8 = encode + width_times_channels - stbir__simdfX_float_count*2; + end_output -= stbir__simdfX_float_count*2; + for(;;) + { + stbir__simdfX e0, e1; + stbir__simdiX i; + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdfX_madd_mem( e0, STBIR_simd_point5X, STBIR_max_uint16_as_floatX, encode ); + stbir__simdfX_madd_mem( e1, STBIR_simd_point5X, STBIR_max_uint16_as_floatX, encode+stbir__simdfX_float_count ); + stbir__encode_simdfX_unflip( e0 ); + stbir__encode_simdfX_unflip( e1 ); + stbir__simdfX_pack_to_words( i, e0, e1 ); + stbir__simdiX_store( output, i ); + encode += stbir__simdfX_float_count*2; + output += stbir__simdfX_float_count*2; + if ( output <= end_output ) + continue; + if ( output == ( end_output + stbir__simdfX_float_count*2 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + } + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + stbir__simdf e; + stbir__simdi i; + STBIR_NO_UNROLL(encode); + stbir__simdf_load( e, encode ); + stbir__simdf_madd( e, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), e ); + stbir__encode_simdf4_unflip( e ); + stbir__simdf_pack_to_8words( i, e, e ); // only use first 4 + stbir__simdi_store2( output-4, i ); + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + stbir__simdf e; + STBIR_NO_UNROLL(encode); + stbir__simdf_madd1_mem( e, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), encode+stbir__encode_order0 ); output[0] = stbir__simdf_convert_float_to_short( e ); + #if stbir__coder_min_num >= 2 + stbir__simdf_madd1_mem( e, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), encode+stbir__encode_order1 ); output[1] = stbir__simdf_convert_float_to_short( e ); + #endif + #if stbir__coder_min_num >= 3 + stbir__simdf_madd1_mem( e, STBIR__CONSTF(STBIR_simd_point5), STBIR__CONSTF(STBIR_max_uint16_as_float), encode+stbir__encode_order2 ); output[2] = stbir__simdf_convert_float_to_short( e ); + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif + + #else + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + float f; + STBIR_SIMD_NO_UNROLL(encode); + f = encode[stbir__encode_order0] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[0-4] = (unsigned short)f; + f = encode[stbir__encode_order1] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[1-4] = (unsigned short)f; + f = encode[stbir__encode_order2] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[2-4] = (unsigned short)f; + f = encode[stbir__encode_order3] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[3-4] = (unsigned short)f; + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + float f; + STBIR_NO_UNROLL(encode); + f = encode[stbir__encode_order0] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[0] = (unsigned short)f; + #if stbir__coder_min_num >= 2 + f = encode[stbir__encode_order1] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[1] = (unsigned short)f; + #endif + #if stbir__coder_min_num >= 3 + f = encode[stbir__encode_order2] * stbir__max_uint16_as_float + 0.5f; STBIR_CLAMP(f, 0, 65535); output[2] = (unsigned short)f; + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif + #endif +} + +static void STBIR__CODER_NAME(stbir__decode_uint16_linear)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + unsigned short const * input = (unsigned short const *)inputp; + + #ifdef STBIR_SIMD + unsigned short const * end_input_m8 = input + width_times_channels - 8; + if ( width_times_channels >= 8 ) + { + decode_end -= 8; + for(;;) + { + #ifdef STBIR_SIMD8 + stbir__simdi i; stbir__simdi8 o; + stbir__simdf8 of; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi8_expand_u16_to_u32( o, i ); + stbir__simdi8_convert_i32_to_float( of, o ); + stbir__decode_simdf8_flip( of ); + stbir__simdf8_store( decode + 0, of ); + #else + stbir__simdi i, o0, o1; + stbir__simdf of0, of1; + STBIR_NO_UNROLL(decode); + stbir__simdi_load( i, input ); + stbir__simdi_expand_u16_to_u32( o0, o1, i ); + stbir__simdi_convert_i32_to_float( of0, o0 ); + stbir__simdi_convert_i32_to_float( of1, o1 ); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__simdf_store( decode + 0, of0 ); + stbir__simdf_store( decode + 4, of1 ); + #endif + decode += 8; + input += 8; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 8 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m8; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = ((float)(input[stbir__decode_order0])); + decode[1-4] = ((float)(input[stbir__decode_order1])); + decode[2-4] = ((float)(input[stbir__decode_order2])); + decode[3-4] = ((float)(input[stbir__decode_order3])); + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = ((float)(input[stbir__decode_order0])); + #if stbir__coder_min_num >= 2 + decode[1] = ((float)(input[stbir__decode_order1])); + #endif + #if stbir__coder_min_num >= 3 + decode[2] = ((float)(input[stbir__decode_order2])); + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME(stbir__encode_uint16_linear)( void * outputp, int width_times_channels, float const * encode ) +{ + unsigned short STBIR_SIMD_STREAMOUT_PTR( * ) output = (unsigned short*) outputp; + unsigned short * end_output = ( (unsigned short*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + { + if ( width_times_channels >= stbir__simdfX_float_count*2 ) + { + float const * end_encode_m8 = encode + width_times_channels - stbir__simdfX_float_count*2; + end_output -= stbir__simdfX_float_count*2; + for(;;) + { + stbir__simdfX e0, e1; + stbir__simdiX i; + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdfX_add_mem( e0, STBIR_simd_point5X, encode ); + stbir__simdfX_add_mem( e1, STBIR_simd_point5X, encode+stbir__simdfX_float_count ); + stbir__encode_simdfX_unflip( e0 ); + stbir__encode_simdfX_unflip( e1 ); + stbir__simdfX_pack_to_words( i, e0, e1 ); + stbir__simdiX_store( output, i ); + encode += stbir__simdfX_float_count*2; + output += stbir__simdfX_float_count*2; + if ( output <= end_output ) + continue; + if ( output == ( end_output + stbir__simdfX_float_count*2 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + } + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + stbir__simdf e; + stbir__simdi i; + STBIR_NO_UNROLL(encode); + stbir__simdf_load( e, encode ); + stbir__simdf_add( e, STBIR__CONSTF(STBIR_simd_point5), e ); + stbir__encode_simdf4_unflip( e ); + stbir__simdf_pack_to_8words( i, e, e ); // only use first 4 + stbir__simdi_store2( output-4, i ); + output += 4; + encode += 4; + } + output -= 4; + #endif + + #else + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + float f; + STBIR_SIMD_NO_UNROLL(encode); + f = encode[stbir__encode_order0] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[0-4] = (unsigned short)f; + f = encode[stbir__encode_order1] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[1-4] = (unsigned short)f; + f = encode[stbir__encode_order2] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[2-4] = (unsigned short)f; + f = encode[stbir__encode_order3] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[3-4] = (unsigned short)f; + output += 4; + encode += 4; + } + output -= 4; + #endif + + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + float f; + STBIR_NO_UNROLL(encode); + f = encode[stbir__encode_order0] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[0] = (unsigned short)f; + #if stbir__coder_min_num >= 2 + f = encode[stbir__encode_order1] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[1] = (unsigned short)f; + #endif + #if stbir__coder_min_num >= 3 + f = encode[stbir__encode_order2] + 0.5f; STBIR_CLAMP(f, 0, 65535); output[2] = (unsigned short)f; + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME(stbir__decode_half_float_linear)( float * decodep, int width_times_channels, void const * inputp ) +{ + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + stbir__FP16 const * input = (stbir__FP16 const *)inputp; + + #ifdef STBIR_SIMD + if ( width_times_channels >= 8 ) + { + stbir__FP16 const * end_input_m8 = input + width_times_channels - 8; + decode_end -= 8; + for(;;) + { + STBIR_NO_UNROLL(decode); + + stbir__half_to_float_SIMD( decode, input ); + #ifdef stbir__decode_swizzle + #ifdef STBIR_SIMD8 + { + stbir__simdf8 of; + stbir__simdf8_load( of, decode ); + stbir__decode_simdf8_flip( of ); + stbir__simdf8_store( decode, of ); + } + #else + { + stbir__simdf of0,of1; + stbir__simdf_load( of0, decode ); + stbir__simdf_load( of1, decode+4 ); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__simdf_store( decode, of0 ); + stbir__simdf_store( decode+4, of1 ); + } + #endif + #endif + decode += 8; + input += 8; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 8 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m8; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = stbir__half_to_float(input[stbir__decode_order0]); + decode[1-4] = stbir__half_to_float(input[stbir__decode_order1]); + decode[2-4] = stbir__half_to_float(input[stbir__decode_order2]); + decode[3-4] = stbir__half_to_float(input[stbir__decode_order3]); + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = stbir__half_to_float(input[stbir__decode_order0]); + #if stbir__coder_min_num >= 2 + decode[1] = stbir__half_to_float(input[stbir__decode_order1]); + #endif + #if stbir__coder_min_num >= 3 + decode[2] = stbir__half_to_float(input[stbir__decode_order2]); + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME( stbir__encode_half_float_linear )( void * outputp, int width_times_channels, float const * encode ) +{ + stbir__FP16 STBIR_SIMD_STREAMOUT_PTR( * ) output = (stbir__FP16*) outputp; + stbir__FP16 * end_output = ( (stbir__FP16*) output ) + width_times_channels; + + #ifdef STBIR_SIMD + if ( width_times_channels >= 8 ) + { + float const * end_encode_m8 = encode + width_times_channels - 8; + end_output -= 8; + for(;;) + { + STBIR_SIMD_NO_UNROLL(encode); + #ifdef stbir__decode_swizzle + #ifdef STBIR_SIMD8 + { + stbir__simdf8 of; + stbir__simdf8_load( of, encode ); + stbir__encode_simdf8_unflip( of ); + stbir__float_to_half_SIMD( output, (float*)&of ); + } + #else + { + stbir__simdf of[2]; + stbir__simdf_load( of[0], encode ); + stbir__simdf_load( of[1], encode+4 ); + stbir__encode_simdf4_unflip( of[0] ); + stbir__encode_simdf4_unflip( of[1] ); + stbir__float_to_half_SIMD( output, (float*)of ); + } + #endif + #else + stbir__float_to_half_SIMD( output, encode ); + #endif + encode += 8; + output += 8; + if ( output <= end_output ) + continue; + if ( output == ( end_output + 8 ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + STBIR_SIMD_NO_UNROLL(output); + output[0-4] = stbir__float_to_half(encode[stbir__encode_order0]); + output[1-4] = stbir__float_to_half(encode[stbir__encode_order1]); + output[2-4] = stbir__float_to_half(encode[stbir__encode_order2]); + output[3-4] = stbir__float_to_half(encode[stbir__encode_order3]); + output += 4; + encode += 4; + } + output -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + STBIR_NO_UNROLL(output); + output[0] = stbir__float_to_half(encode[stbir__encode_order0]); + #if stbir__coder_min_num >= 2 + output[1] = stbir__float_to_half(encode[stbir__encode_order1]); + #endif + #if stbir__coder_min_num >= 3 + output[2] = stbir__float_to_half(encode[stbir__encode_order2]); + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif +} + +static void STBIR__CODER_NAME(stbir__decode_float_linear)( float * decodep, int width_times_channels, void const * inputp ) +{ + #ifdef stbir__decode_swizzle + float STBIR_STREAMOUT_PTR( * ) decode = decodep; + float * decode_end = (float*) decode + width_times_channels; + float const * input = (float const *)inputp; + + #ifdef STBIR_SIMD + if ( width_times_channels >= 16 ) + { + float const * end_input_m16 = input + width_times_channels - 16; + decode_end -= 16; + for(;;) + { + STBIR_NO_UNROLL(decode); + #ifdef stbir__decode_swizzle + #ifdef STBIR_SIMD8 + { + stbir__simdf8 of0,of1; + stbir__simdf8_load( of0, input ); + stbir__simdf8_load( of1, input+8 ); + stbir__decode_simdf8_flip( of0 ); + stbir__decode_simdf8_flip( of1 ); + stbir__simdf8_store( decode, of0 ); + stbir__simdf8_store( decode+8, of1 ); + } + #else + { + stbir__simdf of0,of1,of2,of3; + stbir__simdf_load( of0, input ); + stbir__simdf_load( of1, input+4 ); + stbir__simdf_load( of2, input+8 ); + stbir__simdf_load( of3, input+12 ); + stbir__decode_simdf4_flip( of0 ); + stbir__decode_simdf4_flip( of1 ); + stbir__decode_simdf4_flip( of2 ); + stbir__decode_simdf4_flip( of3 ); + stbir__simdf_store( decode, of0 ); + stbir__simdf_store( decode+4, of1 ); + stbir__simdf_store( decode+8, of2 ); + stbir__simdf_store( decode+12, of3 ); + } + #endif + #endif + decode += 16; + input += 16; + if ( decode <= decode_end ) + continue; + if ( decode == ( decode_end + 16 ) ) + break; + decode = decode_end; // backup and do last couple + input = end_input_m16; + } + return; + } + #endif + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + decode += 4; + while( decode <= decode_end ) + { + STBIR_SIMD_NO_UNROLL(decode); + decode[0-4] = input[stbir__decode_order0]; + decode[1-4] = input[stbir__decode_order1]; + decode[2-4] = input[stbir__decode_order2]; + decode[3-4] = input[stbir__decode_order3]; + decode += 4; + input += 4; + } + decode -= 4; + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( decode < decode_end ) + { + STBIR_NO_UNROLL(decode); + decode[0] = input[stbir__decode_order0]; + #if stbir__coder_min_num >= 2 + decode[1] = input[stbir__decode_order1]; + #endif + #if stbir__coder_min_num >= 3 + decode[2] = input[stbir__decode_order2]; + #endif + decode += stbir__coder_min_num; + input += stbir__coder_min_num; + } + #endif + + #else + + if ( (void*)decodep != inputp ) + STBIR_MEMCPY( decodep, inputp, width_times_channels * sizeof( float ) ); + + #endif +} + +static void STBIR__CODER_NAME( stbir__encode_float_linear )( void * outputp, int width_times_channels, float const * encode ) +{ + #if !defined( STBIR_FLOAT_HIGH_CLAMP ) && !defined(STBIR_FLOAT_LO_CLAMP) && !defined(stbir__decode_swizzle) + + if ( (void*)outputp != (void*) encode ) + STBIR_MEMCPY( outputp, encode, width_times_channels * sizeof( float ) ); + + #else + + float STBIR_SIMD_STREAMOUT_PTR( * ) output = (float*) outputp; + float * end_output = ( (float*) output ) + width_times_channels; + + #ifdef STBIR_FLOAT_HIGH_CLAMP + #define stbir_scalar_hi_clamp( v ) if ( v > STBIR_FLOAT_HIGH_CLAMP ) v = STBIR_FLOAT_HIGH_CLAMP; + #else + #define stbir_scalar_hi_clamp( v ) + #endif + #ifdef STBIR_FLOAT_LOW_CLAMP + #define stbir_scalar_lo_clamp( v ) if ( v < STBIR_FLOAT_LOW_CLAMP ) v = STBIR_FLOAT_LOW_CLAMP; + #else + #define stbir_scalar_lo_clamp( v ) + #endif + + #ifdef STBIR_SIMD + + #ifdef STBIR_FLOAT_HIGH_CLAMP + const stbir__simdfX high_clamp = stbir__simdf_frepX(STBIR_FLOAT_HIGH_CLAMP); + #endif + #ifdef STBIR_FLOAT_LOW_CLAMP + const stbir__simdfX low_clamp = stbir__simdf_frepX(STBIR_FLOAT_LOW_CLAMP); + #endif + + if ( width_times_channels >= ( stbir__simdfX_float_count * 2 ) ) + { + float const * end_encode_m8 = encode + width_times_channels - ( stbir__simdfX_float_count * 2 ); + end_output -= ( stbir__simdfX_float_count * 2 ); + for(;;) + { + stbir__simdfX e0, e1; + STBIR_SIMD_NO_UNROLL(encode); + stbir__simdfX_load( e0, encode ); + stbir__simdfX_load( e1, encode+stbir__simdfX_float_count ); +#ifdef STBIR_FLOAT_HIGH_CLAMP + stbir__simdfX_min( e0, e0, high_clamp ); + stbir__simdfX_min( e1, e1, high_clamp ); +#endif +#ifdef STBIR_FLOAT_LOW_CLAMP + stbir__simdfX_max( e0, e0, low_clamp ); + stbir__simdfX_max( e1, e1, low_clamp ); +#endif + stbir__encode_simdfX_unflip( e0 ); + stbir__encode_simdfX_unflip( e1 ); + stbir__simdfX_store( output, e0 ); + stbir__simdfX_store( output+stbir__simdfX_float_count, e1 ); + encode += stbir__simdfX_float_count * 2; + output += stbir__simdfX_float_count * 2; + if ( output < end_output ) + continue; + if ( output == ( end_output + ( stbir__simdfX_float_count * 2 ) ) ) + break; + output = end_output; // backup and do last couple + encode = end_encode_m8; + } + return; + } + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + stbir__simdf e0; + STBIR_NO_UNROLL(encode); + stbir__simdf_load( e0, encode ); +#ifdef STBIR_FLOAT_HIGH_CLAMP + stbir__simdf_min( e0, e0, high_clamp ); +#endif +#ifdef STBIR_FLOAT_LOW_CLAMP + stbir__simdf_max( e0, e0, low_clamp ); +#endif + stbir__encode_simdf4_unflip( e0 ); + stbir__simdf_store( output-4, e0 ); + output += 4; + encode += 4; + } + output -= 4; + #endif + + #else + + // try to do blocks of 4 when you can + #if stbir__coder_min_num != 3 // doesn't divide cleanly by four + output += 4; + while( output <= end_output ) + { + float e; + STBIR_SIMD_NO_UNROLL(encode); + e = encode[ stbir__encode_order0 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[0-4] = e; + e = encode[ stbir__encode_order1 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[1-4] = e; + e = encode[ stbir__encode_order2 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[2-4] = e; + e = encode[ stbir__encode_order3 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[3-4] = e; + output += 4; + encode += 4; + } + output -= 4; + + #endif + + #endif + + // do the remnants + #if stbir__coder_min_num < 4 + while( output < end_output ) + { + float e; + STBIR_NO_UNROLL(encode); + e = encode[ stbir__encode_order0 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[0] = e; + #if stbir__coder_min_num >= 2 + e = encode[ stbir__encode_order1 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[1] = e; + #endif + #if stbir__coder_min_num >= 3 + e = encode[ stbir__encode_order2 ]; stbir_scalar_hi_clamp( e ); stbir_scalar_lo_clamp( e ); output[2] = e; + #endif + output += stbir__coder_min_num; + encode += stbir__coder_min_num; + } + #endif + + #endif +} + +#undef stbir__decode_suffix +#undef stbir__decode_simdf8_flip +#undef stbir__decode_simdf4_flip +#undef stbir__decode_order0 +#undef stbir__decode_order1 +#undef stbir__decode_order2 +#undef stbir__decode_order3 +#undef stbir__encode_order0 +#undef stbir__encode_order1 +#undef stbir__encode_order2 +#undef stbir__encode_order3 +#undef stbir__encode_simdf8_unflip +#undef stbir__encode_simdf4_unflip +#undef stbir__encode_simdfX_unflip +#undef STBIR__CODER_NAME +#undef stbir__coder_min_num +#undef stbir__decode_swizzle +#undef stbir_scalar_hi_clamp +#undef stbir_scalar_lo_clamp +#undef STB_IMAGE_RESIZE_DO_CODERS + +#elif defined( STB_IMAGE_RESIZE_DO_VERTICALS) + +#ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#define STBIR_chans( start, end ) STBIR_strs_join14(start,STBIR__vertical_channels,end,_cont) +#else +#define STBIR_chans( start, end ) STBIR_strs_join1(start,STBIR__vertical_channels,end) +#endif + +#if STBIR__vertical_channels >= 1 +#define stbIF0( code ) code +#else +#define stbIF0( code ) +#endif +#if STBIR__vertical_channels >= 2 +#define stbIF1( code ) code +#else +#define stbIF1( code ) +#endif +#if STBIR__vertical_channels >= 3 +#define stbIF2( code ) code +#else +#define stbIF2( code ) +#endif +#if STBIR__vertical_channels >= 4 +#define stbIF3( code ) code +#else +#define stbIF3( code ) +#endif +#if STBIR__vertical_channels >= 5 +#define stbIF4( code ) code +#else +#define stbIF4( code ) +#endif +#if STBIR__vertical_channels >= 6 +#define stbIF5( code ) code +#else +#define stbIF5( code ) +#endif +#if STBIR__vertical_channels >= 7 +#define stbIF6( code ) code +#else +#define stbIF6( code ) +#endif +#if STBIR__vertical_channels >= 8 +#define stbIF7( code ) code +#else +#define stbIF7( code ) +#endif + +static void STBIR_chans( stbir__vertical_scatter_with_,_coeffs)( float ** outputs, float const * vertical_coefficients, float const * input, float const * input_end ) +{ + stbIF0( float STBIR_SIMD_STREAMOUT_PTR( * ) output0 = outputs[0]; float c0s = vertical_coefficients[0]; ) + stbIF1( float STBIR_SIMD_STREAMOUT_PTR( * ) output1 = outputs[1]; float c1s = vertical_coefficients[1]; ) + stbIF2( float STBIR_SIMD_STREAMOUT_PTR( * ) output2 = outputs[2]; float c2s = vertical_coefficients[2]; ) + stbIF3( float STBIR_SIMD_STREAMOUT_PTR( * ) output3 = outputs[3]; float c3s = vertical_coefficients[3]; ) + stbIF4( float STBIR_SIMD_STREAMOUT_PTR( * ) output4 = outputs[4]; float c4s = vertical_coefficients[4]; ) + stbIF5( float STBIR_SIMD_STREAMOUT_PTR( * ) output5 = outputs[5]; float c5s = vertical_coefficients[5]; ) + stbIF6( float STBIR_SIMD_STREAMOUT_PTR( * ) output6 = outputs[6]; float c6s = vertical_coefficients[6]; ) + stbIF7( float STBIR_SIMD_STREAMOUT_PTR( * ) output7 = outputs[7]; float c7s = vertical_coefficients[7]; ) + + #ifdef STBIR_SIMD + { + stbIF0(stbir__simdfX c0 = stbir__simdf_frepX( c0s ); ) + stbIF1(stbir__simdfX c1 = stbir__simdf_frepX( c1s ); ) + stbIF2(stbir__simdfX c2 = stbir__simdf_frepX( c2s ); ) + stbIF3(stbir__simdfX c3 = stbir__simdf_frepX( c3s ); ) + stbIF4(stbir__simdfX c4 = stbir__simdf_frepX( c4s ); ) + stbIF5(stbir__simdfX c5 = stbir__simdf_frepX( c5s ); ) + stbIF6(stbir__simdfX c6 = stbir__simdf_frepX( c6s ); ) + stbIF7(stbir__simdfX c7 = stbir__simdf_frepX( c7s ); ) + while ( ( (char*)input_end - (char*) input ) >= (16*stbir__simdfX_float_count) ) + { + stbir__simdfX o0, o1, o2, o3, r0, r1, r2, r3; + STBIR_SIMD_NO_UNROLL(output0); + + stbir__simdfX_load( r0, input ); stbir__simdfX_load( r1, input+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input+(3*stbir__simdfX_float_count) ); + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( stbir__simdfX_load( o0, output0 ); stbir__simdfX_load( o1, output0+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output0+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output0+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c0 ); stbir__simdfX_madd( o1, o1, r1, c0 ); stbir__simdfX_madd( o2, o2, r2, c0 ); stbir__simdfX_madd( o3, o3, r3, c0 ); + stbir__simdfX_store( output0, o0 ); stbir__simdfX_store( output0+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output0+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output0+(3*stbir__simdfX_float_count), o3 ); ) + stbIF1( stbir__simdfX_load( o0, output1 ); stbir__simdfX_load( o1, output1+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output1+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output1+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c1 ); stbir__simdfX_madd( o1, o1, r1, c1 ); stbir__simdfX_madd( o2, o2, r2, c1 ); stbir__simdfX_madd( o3, o3, r3, c1 ); + stbir__simdfX_store( output1, o0 ); stbir__simdfX_store( output1+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output1+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output1+(3*stbir__simdfX_float_count), o3 ); ) + stbIF2( stbir__simdfX_load( o0, output2 ); stbir__simdfX_load( o1, output2+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output2+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output2+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c2 ); stbir__simdfX_madd( o1, o1, r1, c2 ); stbir__simdfX_madd( o2, o2, r2, c2 ); stbir__simdfX_madd( o3, o3, r3, c2 ); + stbir__simdfX_store( output2, o0 ); stbir__simdfX_store( output2+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output2+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output2+(3*stbir__simdfX_float_count), o3 ); ) + stbIF3( stbir__simdfX_load( o0, output3 ); stbir__simdfX_load( o1, output3+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output3+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output3+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c3 ); stbir__simdfX_madd( o1, o1, r1, c3 ); stbir__simdfX_madd( o2, o2, r2, c3 ); stbir__simdfX_madd( o3, o3, r3, c3 ); + stbir__simdfX_store( output3, o0 ); stbir__simdfX_store( output3+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output3+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output3+(3*stbir__simdfX_float_count), o3 ); ) + stbIF4( stbir__simdfX_load( o0, output4 ); stbir__simdfX_load( o1, output4+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output4+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output4+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c4 ); stbir__simdfX_madd( o1, o1, r1, c4 ); stbir__simdfX_madd( o2, o2, r2, c4 ); stbir__simdfX_madd( o3, o3, r3, c4 ); + stbir__simdfX_store( output4, o0 ); stbir__simdfX_store( output4+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output4+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output4+(3*stbir__simdfX_float_count), o3 ); ) + stbIF5( stbir__simdfX_load( o0, output5 ); stbir__simdfX_load( o1, output5+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output5+(2*stbir__simdfX_float_count)); stbir__simdfX_load( o3, output5+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c5 ); stbir__simdfX_madd( o1, o1, r1, c5 ); stbir__simdfX_madd( o2, o2, r2, c5 ); stbir__simdfX_madd( o3, o3, r3, c5 ); + stbir__simdfX_store( output5, o0 ); stbir__simdfX_store( output5+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output5+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output5+(3*stbir__simdfX_float_count), o3 ); ) + stbIF6( stbir__simdfX_load( o0, output6 ); stbir__simdfX_load( o1, output6+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output6+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output6+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c6 ); stbir__simdfX_madd( o1, o1, r1, c6 ); stbir__simdfX_madd( o2, o2, r2, c6 ); stbir__simdfX_madd( o3, o3, r3, c6 ); + stbir__simdfX_store( output6, o0 ); stbir__simdfX_store( output6+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output6+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output6+(3*stbir__simdfX_float_count), o3 ); ) + stbIF7( stbir__simdfX_load( o0, output7 ); stbir__simdfX_load( o1, output7+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output7+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output7+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c7 ); stbir__simdfX_madd( o1, o1, r1, c7 ); stbir__simdfX_madd( o2, o2, r2, c7 ); stbir__simdfX_madd( o3, o3, r3, c7 ); + stbir__simdfX_store( output7, o0 ); stbir__simdfX_store( output7+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output7+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output7+(3*stbir__simdfX_float_count), o3 ); ) + #else + stbIF0( stbir__simdfX_mult( o0, r0, c0 ); stbir__simdfX_mult( o1, r1, c0 ); stbir__simdfX_mult( o2, r2, c0 ); stbir__simdfX_mult( o3, r3, c0 ); + stbir__simdfX_store( output0, o0 ); stbir__simdfX_store( output0+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output0+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output0+(3*stbir__simdfX_float_count), o3 ); ) + stbIF1( stbir__simdfX_mult( o0, r0, c1 ); stbir__simdfX_mult( o1, r1, c1 ); stbir__simdfX_mult( o2, r2, c1 ); stbir__simdfX_mult( o3, r3, c1 ); + stbir__simdfX_store( output1, o0 ); stbir__simdfX_store( output1+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output1+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output1+(3*stbir__simdfX_float_count), o3 ); ) + stbIF2( stbir__simdfX_mult( o0, r0, c2 ); stbir__simdfX_mult( o1, r1, c2 ); stbir__simdfX_mult( o2, r2, c2 ); stbir__simdfX_mult( o3, r3, c2 ); + stbir__simdfX_store( output2, o0 ); stbir__simdfX_store( output2+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output2+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output2+(3*stbir__simdfX_float_count), o3 ); ) + stbIF3( stbir__simdfX_mult( o0, r0, c3 ); stbir__simdfX_mult( o1, r1, c3 ); stbir__simdfX_mult( o2, r2, c3 ); stbir__simdfX_mult( o3, r3, c3 ); + stbir__simdfX_store( output3, o0 ); stbir__simdfX_store( output3+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output3+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output3+(3*stbir__simdfX_float_count), o3 ); ) + stbIF4( stbir__simdfX_mult( o0, r0, c4 ); stbir__simdfX_mult( o1, r1, c4 ); stbir__simdfX_mult( o2, r2, c4 ); stbir__simdfX_mult( o3, r3, c4 ); + stbir__simdfX_store( output4, o0 ); stbir__simdfX_store( output4+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output4+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output4+(3*stbir__simdfX_float_count), o3 ); ) + stbIF5( stbir__simdfX_mult( o0, r0, c5 ); stbir__simdfX_mult( o1, r1, c5 ); stbir__simdfX_mult( o2, r2, c5 ); stbir__simdfX_mult( o3, r3, c5 ); + stbir__simdfX_store( output5, o0 ); stbir__simdfX_store( output5+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output5+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output5+(3*stbir__simdfX_float_count), o3 ); ) + stbIF6( stbir__simdfX_mult( o0, r0, c6 ); stbir__simdfX_mult( o1, r1, c6 ); stbir__simdfX_mult( o2, r2, c6 ); stbir__simdfX_mult( o3, r3, c6 ); + stbir__simdfX_store( output6, o0 ); stbir__simdfX_store( output6+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output6+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output6+(3*stbir__simdfX_float_count), o3 ); ) + stbIF7( stbir__simdfX_mult( o0, r0, c7 ); stbir__simdfX_mult( o1, r1, c7 ); stbir__simdfX_mult( o2, r2, c7 ); stbir__simdfX_mult( o3, r3, c7 ); + stbir__simdfX_store( output7, o0 ); stbir__simdfX_store( output7+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output7+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output7+(3*stbir__simdfX_float_count), o3 ); ) + #endif + + input += (4*stbir__simdfX_float_count); + stbIF0( output0 += (4*stbir__simdfX_float_count); ) stbIF1( output1 += (4*stbir__simdfX_float_count); ) stbIF2( output2 += (4*stbir__simdfX_float_count); ) stbIF3( output3 += (4*stbir__simdfX_float_count); ) stbIF4( output4 += (4*stbir__simdfX_float_count); ) stbIF5( output5 += (4*stbir__simdfX_float_count); ) stbIF6( output6 += (4*stbir__simdfX_float_count); ) stbIF7( output7 += (4*stbir__simdfX_float_count); ) + } + while ( ( (char*)input_end - (char*) input ) >= 16 ) + { + stbir__simdf o0, r0; + STBIR_SIMD_NO_UNROLL(output0); + + stbir__simdf_load( r0, input ); + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( stbir__simdf_load( o0, output0 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c0 ) ); stbir__simdf_store( output0, o0 ); ) + stbIF1( stbir__simdf_load( o0, output1 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c1 ) ); stbir__simdf_store( output1, o0 ); ) + stbIF2( stbir__simdf_load( o0, output2 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c2 ) ); stbir__simdf_store( output2, o0 ); ) + stbIF3( stbir__simdf_load( o0, output3 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c3 ) ); stbir__simdf_store( output3, o0 ); ) + stbIF4( stbir__simdf_load( o0, output4 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c4 ) ); stbir__simdf_store( output4, o0 ); ) + stbIF5( stbir__simdf_load( o0, output5 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c5 ) ); stbir__simdf_store( output5, o0 ); ) + stbIF6( stbir__simdf_load( o0, output6 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c6 ) ); stbir__simdf_store( output6, o0 ); ) + stbIF7( stbir__simdf_load( o0, output7 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c7 ) ); stbir__simdf_store( output7, o0 ); ) + #else + stbIF0( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c0 ) ); stbir__simdf_store( output0, o0 ); ) + stbIF1( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c1 ) ); stbir__simdf_store( output1, o0 ); ) + stbIF2( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c2 ) ); stbir__simdf_store( output2, o0 ); ) + stbIF3( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c3 ) ); stbir__simdf_store( output3, o0 ); ) + stbIF4( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c4 ) ); stbir__simdf_store( output4, o0 ); ) + stbIF5( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c5 ) ); stbir__simdf_store( output5, o0 ); ) + stbIF6( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c6 ) ); stbir__simdf_store( output6, o0 ); ) + stbIF7( stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c7 ) ); stbir__simdf_store( output7, o0 ); ) + #endif + + input += 4; + stbIF0( output0 += 4; ) stbIF1( output1 += 4; ) stbIF2( output2 += 4; ) stbIF3( output3 += 4; ) stbIF4( output4 += 4; ) stbIF5( output5 += 4; ) stbIF6( output6 += 4; ) stbIF7( output7 += 4; ) + } + } + #else + while ( ( (char*)input_end - (char*) input ) >= 16 ) + { + float r0, r1, r2, r3; + STBIR_NO_UNROLL(input); + + r0 = input[0], r1 = input[1], r2 = input[2], r3 = input[3]; + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( output0[0] += ( r0 * c0s ); output0[1] += ( r1 * c0s ); output0[2] += ( r2 * c0s ); output0[3] += ( r3 * c0s ); ) + stbIF1( output1[0] += ( r0 * c1s ); output1[1] += ( r1 * c1s ); output1[2] += ( r2 * c1s ); output1[3] += ( r3 * c1s ); ) + stbIF2( output2[0] += ( r0 * c2s ); output2[1] += ( r1 * c2s ); output2[2] += ( r2 * c2s ); output2[3] += ( r3 * c2s ); ) + stbIF3( output3[0] += ( r0 * c3s ); output3[1] += ( r1 * c3s ); output3[2] += ( r2 * c3s ); output3[3] += ( r3 * c3s ); ) + stbIF4( output4[0] += ( r0 * c4s ); output4[1] += ( r1 * c4s ); output4[2] += ( r2 * c4s ); output4[3] += ( r3 * c4s ); ) + stbIF5( output5[0] += ( r0 * c5s ); output5[1] += ( r1 * c5s ); output5[2] += ( r2 * c5s ); output5[3] += ( r3 * c5s ); ) + stbIF6( output6[0] += ( r0 * c6s ); output6[1] += ( r1 * c6s ); output6[2] += ( r2 * c6s ); output6[3] += ( r3 * c6s ); ) + stbIF7( output7[0] += ( r0 * c7s ); output7[1] += ( r1 * c7s ); output7[2] += ( r2 * c7s ); output7[3] += ( r3 * c7s ); ) + #else + stbIF0( output0[0] = ( r0 * c0s ); output0[1] = ( r1 * c0s ); output0[2] = ( r2 * c0s ); output0[3] = ( r3 * c0s ); ) + stbIF1( output1[0] = ( r0 * c1s ); output1[1] = ( r1 * c1s ); output1[2] = ( r2 * c1s ); output1[3] = ( r3 * c1s ); ) + stbIF2( output2[0] = ( r0 * c2s ); output2[1] = ( r1 * c2s ); output2[2] = ( r2 * c2s ); output2[3] = ( r3 * c2s ); ) + stbIF3( output3[0] = ( r0 * c3s ); output3[1] = ( r1 * c3s ); output3[2] = ( r2 * c3s ); output3[3] = ( r3 * c3s ); ) + stbIF4( output4[0] = ( r0 * c4s ); output4[1] = ( r1 * c4s ); output4[2] = ( r2 * c4s ); output4[3] = ( r3 * c4s ); ) + stbIF5( output5[0] = ( r0 * c5s ); output5[1] = ( r1 * c5s ); output5[2] = ( r2 * c5s ); output5[3] = ( r3 * c5s ); ) + stbIF6( output6[0] = ( r0 * c6s ); output6[1] = ( r1 * c6s ); output6[2] = ( r2 * c6s ); output6[3] = ( r3 * c6s ); ) + stbIF7( output7[0] = ( r0 * c7s ); output7[1] = ( r1 * c7s ); output7[2] = ( r2 * c7s ); output7[3] = ( r3 * c7s ); ) + #endif + + input += 4; + stbIF0( output0 += 4; ) stbIF1( output1 += 4; ) stbIF2( output2 += 4; ) stbIF3( output3 += 4; ) stbIF4( output4 += 4; ) stbIF5( output5 += 4; ) stbIF6( output6 += 4; ) stbIF7( output7 += 4; ) + } + #endif + while ( input < input_end ) + { + float r = input[0]; + STBIR_NO_UNROLL(output0); + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( output0[0] += ( r * c0s ); ) + stbIF1( output1[0] += ( r * c1s ); ) + stbIF2( output2[0] += ( r * c2s ); ) + stbIF3( output3[0] += ( r * c3s ); ) + stbIF4( output4[0] += ( r * c4s ); ) + stbIF5( output5[0] += ( r * c5s ); ) + stbIF6( output6[0] += ( r * c6s ); ) + stbIF7( output7[0] += ( r * c7s ); ) + #else + stbIF0( output0[0] = ( r * c0s ); ) + stbIF1( output1[0] = ( r * c1s ); ) + stbIF2( output2[0] = ( r * c2s ); ) + stbIF3( output3[0] = ( r * c3s ); ) + stbIF4( output4[0] = ( r * c4s ); ) + stbIF5( output5[0] = ( r * c5s ); ) + stbIF6( output6[0] = ( r * c6s ); ) + stbIF7( output7[0] = ( r * c7s ); ) + #endif + + ++input; + stbIF0( ++output0; ) stbIF1( ++output1; ) stbIF2( ++output2; ) stbIF3( ++output3; ) stbIF4( ++output4; ) stbIF5( ++output5; ) stbIF6( ++output6; ) stbIF7( ++output7; ) + } +} + +static void STBIR_chans( stbir__vertical_gather_with_,_coeffs)( float * outputp, float const * vertical_coefficients, float const ** inputs, float const * input0_end ) +{ + float STBIR_SIMD_STREAMOUT_PTR( * ) output = outputp; + + stbIF0( float const * input0 = inputs[0]; float c0s = vertical_coefficients[0]; ) + stbIF1( float const * input1 = inputs[1]; float c1s = vertical_coefficients[1]; ) + stbIF2( float const * input2 = inputs[2]; float c2s = vertical_coefficients[2]; ) + stbIF3( float const * input3 = inputs[3]; float c3s = vertical_coefficients[3]; ) + stbIF4( float const * input4 = inputs[4]; float c4s = vertical_coefficients[4]; ) + stbIF5( float const * input5 = inputs[5]; float c5s = vertical_coefficients[5]; ) + stbIF6( float const * input6 = inputs[6]; float c6s = vertical_coefficients[6]; ) + stbIF7( float const * input7 = inputs[7]; float c7s = vertical_coefficients[7]; ) + +#if ( STBIR__vertical_channels == 1 ) && !defined(STB_IMAGE_RESIZE_VERTICAL_CONTINUE) + // check single channel one weight + if ( ( c0s >= (1.0f-0.000001f) ) && ( c0s <= (1.0f+0.000001f) ) ) + { + STBIR_MEMCPY( output, input0, (char*)input0_end - (char*)input0 ); + return; + } +#endif + + #ifdef STBIR_SIMD + { + stbIF0(stbir__simdfX c0 = stbir__simdf_frepX( c0s ); ) + stbIF1(stbir__simdfX c1 = stbir__simdf_frepX( c1s ); ) + stbIF2(stbir__simdfX c2 = stbir__simdf_frepX( c2s ); ) + stbIF3(stbir__simdfX c3 = stbir__simdf_frepX( c3s ); ) + stbIF4(stbir__simdfX c4 = stbir__simdf_frepX( c4s ); ) + stbIF5(stbir__simdfX c5 = stbir__simdf_frepX( c5s ); ) + stbIF6(stbir__simdfX c6 = stbir__simdf_frepX( c6s ); ) + stbIF7(stbir__simdfX c7 = stbir__simdf_frepX( c7s ); ) + + while ( ( (char*)input0_end - (char*) input0 ) >= (16*stbir__simdfX_float_count) ) + { + stbir__simdfX o0, o1, o2, o3, r0, r1, r2, r3; + STBIR_SIMD_NO_UNROLL(output); + + // prefetch four loop iterations ahead (doesn't affect much for small resizes, but helps with big ones) + stbIF0( stbir__prefetch( input0 + (16*stbir__simdfX_float_count) ); ) + stbIF1( stbir__prefetch( input1 + (16*stbir__simdfX_float_count) ); ) + stbIF2( stbir__prefetch( input2 + (16*stbir__simdfX_float_count) ); ) + stbIF3( stbir__prefetch( input3 + (16*stbir__simdfX_float_count) ); ) + stbIF4( stbir__prefetch( input4 + (16*stbir__simdfX_float_count) ); ) + stbIF5( stbir__prefetch( input5 + (16*stbir__simdfX_float_count) ); ) + stbIF6( stbir__prefetch( input6 + (16*stbir__simdfX_float_count) ); ) + stbIF7( stbir__prefetch( input7 + (16*stbir__simdfX_float_count) ); ) + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( stbir__simdfX_load( o0, output ); stbir__simdfX_load( o1, output+stbir__simdfX_float_count ); stbir__simdfX_load( o2, output+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( o3, output+(3*stbir__simdfX_float_count) ); + stbir__simdfX_load( r0, input0 ); stbir__simdfX_load( r1, input0+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input0+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input0+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c0 ); stbir__simdfX_madd( o1, o1, r1, c0 ); stbir__simdfX_madd( o2, o2, r2, c0 ); stbir__simdfX_madd( o3, o3, r3, c0 ); ) + #else + stbIF0( stbir__simdfX_load( r0, input0 ); stbir__simdfX_load( r1, input0+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input0+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input0+(3*stbir__simdfX_float_count) ); + stbir__simdfX_mult( o0, r0, c0 ); stbir__simdfX_mult( o1, r1, c0 ); stbir__simdfX_mult( o2, r2, c0 ); stbir__simdfX_mult( o3, r3, c0 ); ) + #endif + + stbIF1( stbir__simdfX_load( r0, input1 ); stbir__simdfX_load( r1, input1+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input1+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input1+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c1 ); stbir__simdfX_madd( o1, o1, r1, c1 ); stbir__simdfX_madd( o2, o2, r2, c1 ); stbir__simdfX_madd( o3, o3, r3, c1 ); ) + stbIF2( stbir__simdfX_load( r0, input2 ); stbir__simdfX_load( r1, input2+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input2+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input2+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c2 ); stbir__simdfX_madd( o1, o1, r1, c2 ); stbir__simdfX_madd( o2, o2, r2, c2 ); stbir__simdfX_madd( o3, o3, r3, c2 ); ) + stbIF3( stbir__simdfX_load( r0, input3 ); stbir__simdfX_load( r1, input3+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input3+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input3+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c3 ); stbir__simdfX_madd( o1, o1, r1, c3 ); stbir__simdfX_madd( o2, o2, r2, c3 ); stbir__simdfX_madd( o3, o3, r3, c3 ); ) + stbIF4( stbir__simdfX_load( r0, input4 ); stbir__simdfX_load( r1, input4+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input4+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input4+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c4 ); stbir__simdfX_madd( o1, o1, r1, c4 ); stbir__simdfX_madd( o2, o2, r2, c4 ); stbir__simdfX_madd( o3, o3, r3, c4 ); ) + stbIF5( stbir__simdfX_load( r0, input5 ); stbir__simdfX_load( r1, input5+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input5+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input5+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c5 ); stbir__simdfX_madd( o1, o1, r1, c5 ); stbir__simdfX_madd( o2, o2, r2, c5 ); stbir__simdfX_madd( o3, o3, r3, c5 ); ) + stbIF6( stbir__simdfX_load( r0, input6 ); stbir__simdfX_load( r1, input6+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input6+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input6+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c6 ); stbir__simdfX_madd( o1, o1, r1, c6 ); stbir__simdfX_madd( o2, o2, r2, c6 ); stbir__simdfX_madd( o3, o3, r3, c6 ); ) + stbIF7( stbir__simdfX_load( r0, input7 ); stbir__simdfX_load( r1, input7+stbir__simdfX_float_count ); stbir__simdfX_load( r2, input7+(2*stbir__simdfX_float_count) ); stbir__simdfX_load( r3, input7+(3*stbir__simdfX_float_count) ); + stbir__simdfX_madd( o0, o0, r0, c7 ); stbir__simdfX_madd( o1, o1, r1, c7 ); stbir__simdfX_madd( o2, o2, r2, c7 ); stbir__simdfX_madd( o3, o3, r3, c7 ); ) + + stbir__simdfX_store( output, o0 ); stbir__simdfX_store( output+stbir__simdfX_float_count, o1 ); stbir__simdfX_store( output+(2*stbir__simdfX_float_count), o2 ); stbir__simdfX_store( output+(3*stbir__simdfX_float_count), o3 ); + output += (4*stbir__simdfX_float_count); + stbIF0( input0 += (4*stbir__simdfX_float_count); ) stbIF1( input1 += (4*stbir__simdfX_float_count); ) stbIF2( input2 += (4*stbir__simdfX_float_count); ) stbIF3( input3 += (4*stbir__simdfX_float_count); ) stbIF4( input4 += (4*stbir__simdfX_float_count); ) stbIF5( input5 += (4*stbir__simdfX_float_count); ) stbIF6( input6 += (4*stbir__simdfX_float_count); ) stbIF7( input7 += (4*stbir__simdfX_float_count); ) + } + + while ( ( (char*)input0_end - (char*) input0 ) >= 16 ) + { + stbir__simdf o0, r0; + STBIR_SIMD_NO_UNROLL(output); + + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( stbir__simdf_load( o0, output ); stbir__simdf_load( r0, input0 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c0 ) ); ) + #else + stbIF0( stbir__simdf_load( r0, input0 ); stbir__simdf_mult( o0, r0, stbir__if_simdf8_cast_to_simdf4( c0 ) ); ) + #endif + stbIF1( stbir__simdf_load( r0, input1 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c1 ) ); ) + stbIF2( stbir__simdf_load( r0, input2 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c2 ) ); ) + stbIF3( stbir__simdf_load( r0, input3 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c3 ) ); ) + stbIF4( stbir__simdf_load( r0, input4 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c4 ) ); ) + stbIF5( stbir__simdf_load( r0, input5 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c5 ) ); ) + stbIF6( stbir__simdf_load( r0, input6 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c6 ) ); ) + stbIF7( stbir__simdf_load( r0, input7 ); stbir__simdf_madd( o0, o0, r0, stbir__if_simdf8_cast_to_simdf4( c7 ) ); ) + + stbir__simdf_store( output, o0 ); + output += 4; + stbIF0( input0 += 4; ) stbIF1( input1 += 4; ) stbIF2( input2 += 4; ) stbIF3( input3 += 4; ) stbIF4( input4 += 4; ) stbIF5( input5 += 4; ) stbIF6( input6 += 4; ) stbIF7( input7 += 4; ) + } + } + #else + while ( ( (char*)input0_end - (char*) input0 ) >= 16 ) + { + float o0, o1, o2, o3; + STBIR_NO_UNROLL(output); + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( o0 = output[0] + input0[0] * c0s; o1 = output[1] + input0[1] * c0s; o2 = output[2] + input0[2] * c0s; o3 = output[3] + input0[3] * c0s; ) + #else + stbIF0( o0 = input0[0] * c0s; o1 = input0[1] * c0s; o2 = input0[2] * c0s; o3 = input0[3] * c0s; ) + #endif + stbIF1( o0 += input1[0] * c1s; o1 += input1[1] * c1s; o2 += input1[2] * c1s; o3 += input1[3] * c1s; ) + stbIF2( o0 += input2[0] * c2s; o1 += input2[1] * c2s; o2 += input2[2] * c2s; o3 += input2[3] * c2s; ) + stbIF3( o0 += input3[0] * c3s; o1 += input3[1] * c3s; o2 += input3[2] * c3s; o3 += input3[3] * c3s; ) + stbIF4( o0 += input4[0] * c4s; o1 += input4[1] * c4s; o2 += input4[2] * c4s; o3 += input4[3] * c4s; ) + stbIF5( o0 += input5[0] * c5s; o1 += input5[1] * c5s; o2 += input5[2] * c5s; o3 += input5[3] * c5s; ) + stbIF6( o0 += input6[0] * c6s; o1 += input6[1] * c6s; o2 += input6[2] * c6s; o3 += input6[3] * c6s; ) + stbIF7( o0 += input7[0] * c7s; o1 += input7[1] * c7s; o2 += input7[2] * c7s; o3 += input7[3] * c7s; ) + output[0] = o0; output[1] = o1; output[2] = o2; output[3] = o3; + output += 4; + stbIF0( input0 += 4; ) stbIF1( input1 += 4; ) stbIF2( input2 += 4; ) stbIF3( input3 += 4; ) stbIF4( input4 += 4; ) stbIF5( input5 += 4; ) stbIF6( input6 += 4; ) stbIF7( input7 += 4; ) + } + #endif + while ( input0 < input0_end ) + { + float o0; + STBIR_NO_UNROLL(output); + #ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE + stbIF0( o0 = output[0] + input0[0] * c0s; ) + #else + stbIF0( o0 = input0[0] * c0s; ) + #endif + stbIF1( o0 += input1[0] * c1s; ) + stbIF2( o0 += input2[0] * c2s; ) + stbIF3( o0 += input3[0] * c3s; ) + stbIF4( o0 += input4[0] * c4s; ) + stbIF5( o0 += input5[0] * c5s; ) + stbIF6( o0 += input6[0] * c6s; ) + stbIF7( o0 += input7[0] * c7s; ) + output[0] = o0; + ++output; + stbIF0( ++input0; ) stbIF1( ++input1; ) stbIF2( ++input2; ) stbIF3( ++input3; ) stbIF4( ++input4; ) stbIF5( ++input5; ) stbIF6( ++input6; ) stbIF7( ++input7; ) + } +} + +#undef stbIF0 +#undef stbIF1 +#undef stbIF2 +#undef stbIF3 +#undef stbIF4 +#undef stbIF5 +#undef stbIF6 +#undef stbIF7 +#undef STB_IMAGE_RESIZE_DO_VERTICALS +#undef STBIR__vertical_channels +#undef STB_IMAGE_RESIZE_DO_HORIZONTALS +#undef STBIR_strs_join24 +#undef STBIR_strs_join14 +#undef STBIR_chans +#ifdef STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#undef STB_IMAGE_RESIZE_VERTICAL_CONTINUE +#endif + +#else // !STB_IMAGE_RESIZE_DO_VERTICALS + +#define STBIR_chans( start, end ) STBIR_strs_join1(start,STBIR__horizontal_channels,end) + +#ifndef stbir__2_coeff_only +#define stbir__2_coeff_only() \ + stbir__1_coeff_only(); \ + stbir__1_coeff_remnant(1); +#endif + +#ifndef stbir__2_coeff_remnant +#define stbir__2_coeff_remnant( ofs ) \ + stbir__1_coeff_remnant(ofs); \ + stbir__1_coeff_remnant((ofs)+1); +#endif + +#ifndef stbir__3_coeff_only +#define stbir__3_coeff_only() \ + stbir__2_coeff_only(); \ + stbir__1_coeff_remnant(2); +#endif + +#ifndef stbir__3_coeff_remnant +#define stbir__3_coeff_remnant( ofs ) \ + stbir__2_coeff_remnant(ofs); \ + stbir__1_coeff_remnant((ofs)+2); +#endif + +#ifndef stbir__3_coeff_setup +#define stbir__3_coeff_setup() +#endif + +#ifndef stbir__4_coeff_start +#define stbir__4_coeff_start() \ + stbir__2_coeff_only(); \ + stbir__2_coeff_remnant(2); +#endif + +#ifndef stbir__4_coeff_continue_from_4 +#define stbir__4_coeff_continue_from_4( ofs ) \ + stbir__2_coeff_remnant(ofs); \ + stbir__2_coeff_remnant((ofs)+2); +#endif + +#ifndef stbir__store_output_tiny +#define stbir__store_output_tiny stbir__store_output +#endif + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_1_coeff)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__1_coeff_only(); + stbir__store_output_tiny(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_2_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__2_coeff_only(); + stbir__store_output_tiny(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_3_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__3_coeff_only(); + stbir__store_output_tiny(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_4_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_5_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__1_coeff_remnant(4); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_6_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__2_coeff_remnant(4); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_7_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + stbir__3_coeff_setup(); + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + + stbir__4_coeff_start(); + stbir__3_coeff_remnant(4); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_8_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__4_coeff_continue_from_4(4); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_9_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__4_coeff_continue_from_4(4); + stbir__1_coeff_remnant(8); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_10_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__4_coeff_continue_from_4(4); + stbir__2_coeff_remnant(8); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_11_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + stbir__3_coeff_setup(); + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__4_coeff_continue_from_4(4); + stbir__3_coeff_remnant(8); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_12_coeffs)( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + float const * hc = horizontal_coefficients; + stbir__4_coeff_start(); + stbir__4_coeff_continue_from_4(4); + stbir__4_coeff_continue_from_4(8); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod0 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + int n = ( ( horizontal_contributors->n1 - horizontal_contributors->n0 + 1 ) - 4 + 3 ) >> 2; + float const * hc = horizontal_coefficients; + + stbir__4_coeff_start(); + do { + hc += 4; + decode += STBIR__horizontal_channels * 4; + stbir__4_coeff_continue_from_4( 0 ); + --n; + } while ( n > 0 ); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod1 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + int n = ( ( horizontal_contributors->n1 - horizontal_contributors->n0 + 1 ) - 5 + 3 ) >> 2; + float const * hc = horizontal_coefficients; + + stbir__4_coeff_start(); + do { + hc += 4; + decode += STBIR__horizontal_channels * 4; + stbir__4_coeff_continue_from_4( 0 ); + --n; + } while ( n > 0 ); + stbir__1_coeff_remnant( 4 ); + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod2 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + int n = ( ( horizontal_contributors->n1 - horizontal_contributors->n0 + 1 ) - 6 + 3 ) >> 2; + float const * hc = horizontal_coefficients; + + stbir__4_coeff_start(); + do { + hc += 4; + decode += STBIR__horizontal_channels * 4; + stbir__4_coeff_continue_from_4( 0 ); + --n; + } while ( n > 0 ); + stbir__2_coeff_remnant( 4 ); + + stbir__store_output(); + } while ( output < output_end ); +} + +static void STBIR_chans( stbir__horizontal_gather_,_channels_with_n_coeffs_mod3 )( float * output_buffer, unsigned int output_sub_size, float const * decode_buffer, stbir__contributors const * horizontal_contributors, float const * horizontal_coefficients, int coefficient_width ) +{ + float const * output_end = output_buffer + output_sub_size * STBIR__horizontal_channels; + float STBIR_SIMD_STREAMOUT_PTR( * ) output = output_buffer; + stbir__3_coeff_setup(); + do { + float const * decode = decode_buffer + horizontal_contributors->n0 * STBIR__horizontal_channels; + int n = ( ( horizontal_contributors->n1 - horizontal_contributors->n0 + 1 ) - 7 + 3 ) >> 2; + float const * hc = horizontal_coefficients; + + stbir__4_coeff_start(); + do { + hc += 4; + decode += STBIR__horizontal_channels * 4; + stbir__4_coeff_continue_from_4( 0 ); + --n; + } while ( n > 0 ); + stbir__3_coeff_remnant( 4 ); + + stbir__store_output(); + } while ( output < output_end ); +} + +static stbir__horizontal_gather_channels_func * STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_funcs)[4]= +{ + STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod0), + STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod1), + STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod2), + STBIR_chans(stbir__horizontal_gather_,_channels_with_n_coeffs_mod3), +}; + +static stbir__horizontal_gather_channels_func * STBIR_chans(stbir__horizontal_gather_,_channels_funcs)[12]= +{ + STBIR_chans(stbir__horizontal_gather_,_channels_with_1_coeff), + STBIR_chans(stbir__horizontal_gather_,_channels_with_2_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_3_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_4_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_5_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_6_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_7_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_8_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_9_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_10_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_11_coeffs), + STBIR_chans(stbir__horizontal_gather_,_channels_with_12_coeffs), +}; + +#undef STBIR__horizontal_channels +#undef STB_IMAGE_RESIZE_DO_HORIZONTALS +#undef stbir__1_coeff_only +#undef stbir__1_coeff_remnant +#undef stbir__2_coeff_only +#undef stbir__2_coeff_remnant +#undef stbir__3_coeff_only +#undef stbir__3_coeff_remnant +#undef stbir__3_coeff_setup +#undef stbir__4_coeff_start +#undef stbir__4_coeff_continue_from_4 +#undef stbir__store_output +#undef stbir__store_output_tiny +#undef STBIR_chans + +#endif // HORIZONALS + +#undef STBIR_strs_join2 +#undef STBIR_strs_join1 + +#endif // STB_IMAGE_RESIZE_DO_HORIZONTALS/VERTICALS/CODERS + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/src/rtextures.c b/src/rtextures.c index 8624bbd48..bf1b35e40 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -213,7 +213,7 @@ #define STBIR_MALLOC(size,c) ((void)(c), RL_MALLOC(size)) #define STBIR_FREE(ptr,c) ((void)(c), RL_FREE(ptr)) #define STB_IMAGE_RESIZE_IMPLEMENTATION -#include "external/stb_image_resize.h" // Required for: stbir_resize_uint8() [ImageResize()] +#include "external/stb_image_resize2.h" // Required for: stbir_resize_uint8_linear() [ImageResize()] #if defined(SUPPORT_FILEFORMAT_SVG) #define NANOSVG_IMPLEMENTATION // Expands implementation @@ -1624,10 +1624,10 @@ void ImageResize(Image *image, int newWidth, int newHeight) switch (image->format) { - case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 1); break; - case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 2); break; - case PIXELFORMAT_UNCOMPRESSED_R8G8B8: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 3); break; - case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: stbir_resize_uint8((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, 4); break; + case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: stbir_resize_uint8_linear((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, (stbir_pixel_layout)1); break; + case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: stbir_resize_uint8_linear((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, (stbir_pixel_layout)2); break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8: stbir_resize_uint8_linear((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, (stbir_pixel_layout)3); break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: stbir_resize_uint8_linear((unsigned char *)image->data, image->width, image->height, 0, output, newWidth, newHeight, 0, (stbir_pixel_layout)4); break; default: break; } @@ -1643,7 +1643,7 @@ void ImageResize(Image *image, int newWidth, int newHeight) Color *output = (Color *)RL_MALLOC(newWidth*newHeight*sizeof(Color)); // NOTE: Color data is cast to (unsigned char *), there shouldn't been any problem... - stbir_resize_uint8((unsigned char *)pixels, image->width, image->height, 0, (unsigned char *)output, newWidth, newHeight, 0, 4); + stbir_resize_uint8_linear((unsigned char *)pixels, image->width, image->height, 0, (unsigned char *)output, newWidth, newHeight, 0, (stbir_pixel_layout)4); int format = image->format; From f3c27ec157f3a3e914f8872714173f87e7ea7751 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Fri, 13 Oct 2023 14:53:31 -0300 Subject: [PATCH 0351/1350] Fix `android`, `drm` compilation issue on `InitWindow` (#3407) * Fix drm compilation issue on InitWindow * Fix android compilation issue on InitWindow --- src/rcore_android.c | 10 +++++----- src/rcore_drm.c | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/rcore_android.c b/src/rcore_android.c index 5f6f34bab..39e4694c4 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -190,7 +190,7 @@ void InitWindow(int width, int height, const char *title) CORE.Window.screen.height = height; CORE.Window.currentFbo.width = width; CORE.Window.currentFbo.height = height; - + // Set desired windows flags before initializing anything ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER @@ -228,12 +228,12 @@ void InitWindow(int width, int height, const char *title) // Initialize base path for storage CORE.Storage.basePath = platform.app->activity->internalDataPath; - + // Set some default window flags CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false - CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED); // false - CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED); // true - CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED); // false + CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true + CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Application initialized successfully"); diff --git a/src/rcore_drm.c b/src/rcore_drm.c index c0e88c723..2644c5e73 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -228,16 +228,16 @@ void InitWindow(int width, int height, const char *title) // Set some default window flags CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false - CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED); // false - CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED); // true - CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED); // false - + CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true + CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false + // Initialize hi-res timer InitTimer(); - + // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); - + // Initialize raw input system InitEvdevInput(); // Evdev inputs initialization InitGamepad(); // Gamepad init From 36abc48cf8abe008f7f02516dcf1c1985517aee4 Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Fri, 13 Oct 2023 19:54:00 +0200 Subject: [PATCH 0352/1350] Normalize `gestureEvent.position` coordinates (#3406) Fixed the fact that coordinates were not normalized on Android, preventing detection of `GESTURE_DOUBLE_TAP` --- src/rcore_android.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/rcore_android.c b/src/rcore_android.c index 39e4694c4..3d9fab06a 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -1152,6 +1152,8 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) { gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i]; gestureEvent.position[i] = CORE.Input.Touch.position[i]; + gestureEvent.position[i].x /= (float)GetScreenWidth(); + gestureEvent.position[i].y /= (float)GetScreenHeight(); } // Gesture data is sent to gestures system for processing From 5a0d9c8d43d212892e421e1cbb532fea508d8692 Mon Sep 17 00:00:00 2001 From: Daniil Kisel <56605335+KislyjKisel@users.noreply.github.com> Date: Fri, 13 Oct 2023 20:54:43 +0300 Subject: [PATCH 0353/1350] Fix `UpdateSound` parameter name (#3405) --- src/raudio.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index c2590e302..a8d1b40e2 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -981,14 +981,14 @@ void UnloadSoundAlias(Sound alias) } // Update sound buffer with new data -void UpdateSound(Sound sound, const void *data, int sampleCount) +void UpdateSound(Sound sound, const void *data, int frameCount) { if (sound.stream.buffer != NULL) { StopAudioBuffer(sound.stream.buffer); // TODO: May want to lock/unlock this since this data buffer is read at mixing time - memcpy(sound.stream.buffer->data, data, sampleCount*ma_get_bytes_per_frame(sound.stream.buffer->converter.formatIn, sound.stream.buffer->converter.channelsIn)); + memcpy(sound.stream.buffer->data, data, frameCount*ma_get_bytes_per_frame(sound.stream.buffer->converter.formatIn, sound.stream.buffer->converter.channelsIn)); } } From 4981acb241d18afc4d0db3497ecbe37d1f31808e Mon Sep 17 00:00:00 2001 From: Purple4pur <49893724+purple4pur@users.noreply.github.com> Date: Sat, 14 Oct 2023 01:55:52 +0800 Subject: [PATCH 0354/1350] fix zig syntax errors in examples, and make it install executables correctly (#3395) --- build.zig | 2 +- examples/build.zig | 36 ++++++++++++++++++++---------------- src/build.zig | 2 +- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/build.zig b/build.zig index 12c0513f6..21638601d 100644 --- a/build.zig +++ b/build.zig @@ -1,7 +1,7 @@ const std = @import("std"); const raylib = @import("src/build.zig"); -// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) pub fn build(b: *std.Build) void { raylib.build(b); } diff --git a/examples/build.zig b/examples/build.zig index 6e13ab3da..94ecc6783 100644 --- a/examples/build.zig +++ b/examples/build.zig @@ -1,7 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); -// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) !*std.Build.Step { if (target.getOsTag() == .emscripten) { @panic("Emscripten building via Zig unsupported"); @@ -11,7 +11,7 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT const dir = try std.fs.cwd().openIterableDir(module, .{}); var iter = dir.iterate(); while (try iter.next()) |entry| { - if (entry.kind != .File) continue; + if (entry.kind != .file) continue; const extension_idx = std.mem.lastIndexOf(u8, entry.name, ".c") orelse continue; const name = entry.name[0..extension_idx]; const path = try std.fs.path.join(b.allocator, &.{ module, entry.name }); @@ -24,26 +24,26 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT .target = target, .optimize = optimize, }); - exe.addCSourceFile(path, &[_][]const u8{}); + exe.addCSourceFile(.{ .file = .{ .path = path }, .flags = &.{} }); exe.linkLibC(); exe.addObjectFile(switch (target.getOsTag()) { - .windows => "../src/zig-out/lib/raylib.lib", - .linux => "../src/zig-out/lib/libraylib.a", - .macos => "../src/zig-out/lib/libraylib.a", - .emscripten => "../src/zig-out/lib/libraylib.a", + .windows => .{ .path = "../zig-out/lib/raylib.lib" }, + .linux => .{ .path = "../zig-out/lib/libraylib.a" }, + .macos => .{ .path = "../zig-out/lib/libraylib.a" }, + .emscripten => .{ .path = "../zig-out/lib/libraylib.a" }, else => @panic("Unsupported OS"), }); - exe.addIncludePath("../src"); - exe.addIncludePath("../src/external"); - exe.addIncludePath("../src/external/glfw/include"); + exe.addIncludePath(.{ .path = "../src" }); + exe.addIncludePath(.{ .path = "../src/external" }); + exe.addIncludePath(.{ .path = "../src/external/glfw/include" }); switch (target.getOsTag()) { .windows => { exe.linkSystemLibrary("winmm"); exe.linkSystemLibrary("gdi32"); exe.linkSystemLibrary("opengl32"); - exe.addIncludePath("external/glfw/deps/mingw"); + exe.addIncludePath(.{ .path = "external/glfw/deps/mingw" }); exe.defineCMacro("PLATFORM_DESKTOP", null); }, @@ -71,11 +71,15 @@ fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossT }, } - b.installArtifact(exe); - var run = b.addRunArtifact(exe); - run.cwd = module; - b.step(name, name).dependOn(&run.step); - all.dependOn(&exe.step); + const install_cmd = b.addInstallArtifact(exe, .{}); + + const run_cmd = b.addRunArtifact(exe); + run_cmd.step.dependOn(&install_cmd.step); + + const run_step = b.step(name, name); + run_step.dependOn(&run_cmd.step); + + all.dependOn(&install_cmd.step); } return all; } diff --git a/src/build.zig b/src/build.zig index 27250f5ff..53e074245 100644 --- a/src/build.zig +++ b/src/build.zig @@ -1,6 +1,6 @@ const std = @import("std"); -// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode, options: Options) *std.Build.CompileStep { const raylib_flags = &[_][]const u8{ "-std=gnu99", From 2f08f435b91a08c57bf7e381a46381770a00791e Mon Sep 17 00:00:00 2001 From: Daniil Kisel <56605335+KislyjKisel@users.noreply.github.com> Date: Fri, 13 Oct 2023 21:54:15 +0300 Subject: [PATCH 0355/1350] Add Raylib.lean to BINDINGS.md (#3409) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index 47a1c0a27..e08f8f79e 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -81,6 +81,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | rayed-bqn | **auto** | [BQN](https://mlochbaum.github.io/BQN/) | MIT | https://github.com/Brian-ED/rayed-bqn | | rayjs | 4.6-dev | [QuickJS](https://bellard.org/quickjs/) | MIT | https://github.com/mode777/rayjs | | raylib-raku | **auto** | [Raku](https://www.raku.org/) | Artistic License 2.0 | https://github.com/vushu/raylib-raku | +| Raylib.lean | 4.5 | [Lean4](https://lean-lang.org/) | BSD-3-Clause | https://github.com/KislyjKisel/Raylib.lean | ### Utility Wrapers These are utility wrappers for specific languages, they are not required to use raylib in the language but may adapt the raylib API to be more inline with the language's pardigm. From 005ba155c0b4d8e065e6e3ffe2cdd82bb41bf200 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 14 Oct 2023 10:56:09 +0200 Subject: [PATCH 0356/1350] Minor tweaks --- src/rcore.c | 4 ++-- src/rcore_desktop.c | 2 +- src/rcore_web.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 3efa67b2c..b3e07f95a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2812,7 +2812,7 @@ static void RecordAutomationEvent(unsigned int frame) // INPUT_GAMEPAD_CONNECT /* if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) && - (CORE.Input.Gamepad.currentState[gamepad] == true)) // Check if changed to ready + (CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to ready { // TODO: Save gamepad connect event } @@ -2821,7 +2821,7 @@ static void RecordAutomationEvent(unsigned int frame) // INPUT_GAMEPAD_DISCONNECT /* if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) && - (CORE.Input.Gamepad.currentState[gamepad] == false)) // Check if changed to not-ready + (!CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to not-ready { // TODO: Save gamepad disconnect event } diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index c2e5b23f1..8fa1edd78 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -1966,7 +1966,7 @@ static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffs // GLFW3 CursorEnter Callback, when cursor enters the window static void CursorEnterCallback(GLFWwindow *window, int enter) { - if (enter == true) CORE.Input.Mouse.cursorOnScreen = true; + if (enter) CORE.Input.Mouse.cursorOnScreen = true; else CORE.Input.Mouse.cursorOnScreen = false; } diff --git a/src/rcore_web.c b/src/rcore_web.c index 261498a5b..c0b7079b0 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -1336,7 +1336,7 @@ static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffs // GLFW3 CursorEnter Callback, when cursor enters the window static void CursorEnterCallback(GLFWwindow *window, int enter) { - if (enter == true) CORE.Input.Mouse.cursorOnScreen = true; + if (enter) CORE.Input.Mouse.cursorOnScreen = true; else CORE.Input.Mouse.cursorOnScreen = false; } From 4521a142c35fe9fe1386d79ae783363c9c164827 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 14 Oct 2023 11:48:20 +0200 Subject: [PATCH 0357/1350] tweaks --- src/rcore.c | 2 +- src/rcore_web.c | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index b3e07f95a..d7aaa30b3 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2360,7 +2360,7 @@ int GetTouchPointCount(void) //---------------------------------------------------------------------------------- // NOTE: Functions with a platform-specific implementation on rcore_.c -//static bool InitGraphicsDevice(int width, int height) +//static bool InitPlatform(void) // Initialize hi-resolution timer void InitTimer(void) diff --git a/src/rcore_web.c b/src/rcore_web.c index c0b7079b0..277d1378f 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -1119,7 +1119,7 @@ static void WindowIconifyCallback(GLFWwindow *window, int iconified) // GLFW3 Window Maximize Callback, runs when window is maximized static void WindowMaximizeCallback(GLFWwindow *window, int maximized) { - + // TODO. } // GLFW3 WindowFocus Callback, runs when window get/lose focus @@ -1157,7 +1157,6 @@ static void WindowDropCallback(GLFWwindow *window, int count, const char **paths } } - // GLFW3 Keyboard Callback, runs on key pressed static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods) { @@ -1340,7 +1339,6 @@ static void CursorEnterCallback(GLFWwindow *window, int enter) else CORE.Input.Mouse.cursorOnScreen = false; } - // Register fullscreen change events static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData) { From b34c2ecbcb9da1d438b70acf8125ef5424744d11 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 14 Oct 2023 12:49:54 +0200 Subject: [PATCH 0358/1350] WARNING: REDESIGN: `InitPlatform()` to initialize all platform data #3313 `InitGraphicsDevice()` could be confusing because the function actually initialized many things: window, graphics, inputs, callbacks, timming, storage... restructured it. --- src/rcore.c | 8 +- src/rcore_android.c | 281 ++++++++++++++++++---------------- src/rcore_desktop.c | 146 ++++++++---------- src/rcore_drm.c | 354 +++++++++++++++++++++---------------------- src/rcore_template.c | 71 ++++----- src/rcore_web.c | 198 +++++++++++------------- 6 files changed, 508 insertions(+), 550 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index d7aaa30b3..e6015b33f 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2365,10 +2365,10 @@ int GetTouchPointCount(void) // Initialize hi-resolution timer void InitTimer(void) { -// Setting a higher resolution can improve the accuracy of time-out intervals in wait functions. -// However, it can also reduce overall system performance, because the thread scheduler switches tasks more often. -// High resolutions can also prevent the CPU power management system from entering power-saving modes. -// Setting a higher resolution does not improve the accuracy of the high-resolution performance counter. + // Setting a higher resolution can improve the accuracy of time-out intervals in wait functions. + // However, it can also reduce overall system performance, because the thread scheduler switches tasks more often. + // High resolutions can also prevent the CPU power management system from entering power-saving modes. + // Setting a higher resolution does not improve the accuracy of the high-resolution performance counter. #if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms) #endif diff --git a/src/rcore_android.c b/src/rcore_android.c index 3d9fab06a..3e3b8cd12 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -82,7 +82,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static bool InitGraphicsDevice(int width, int height); // Initialize graphics device +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static void ClosePlatform(void); // Close platform static void AndroidCommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event); // Process Android inputs @@ -172,88 +173,23 @@ void InitWindow(int width, int height, const char *title) TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); #endif - // NOTE: Keep internal pointer to input title string (no copy) + // Initialize window data + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + CORE.Window.eventWaiting = false; + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); + memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 CORE.Input.Keyboard.exitKey = KEY_ESCAPE; CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Window.eventWaiting = false; + CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - - // Platform specific init window - //-------------------------------------------------------------- - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - CORE.Window.currentFbo.width = width; - CORE.Window.currentFbo.height = height; - - // Set desired windows flags before initializing anything - ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER - - int orientation = AConfiguration_getOrientation(platform.app->config); - - if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait"); - else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape"); - - // TODO: Automatic orientation doesn't seem to work - if (width <= height) - { - AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_PORT); - TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait"); - } - else - { - AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_LAND); - TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape"); - } - - //AConfiguration_getDensity(platform.app->config); - //AConfiguration_getKeyboard(platform.app->config); - //AConfiguration_getScreenSize(platform.app->config); - //AConfiguration_getScreenLong(platform.app->config); - - // Initialize App command system - // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()... - platform.app->onAppCmd = AndroidCommandCallback; - - // Initialize input events system - platform.app->onInputEvent = AndroidInputCallback; - - // Initialize assets manager - InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath); - - // Initialize base path for storage - CORE.Storage.basePath = platform.app->activity->internalDataPath; - - // Set some default window flags - CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false - CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false - CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true - CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false - - TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Application initialized successfully"); - - // Android ALooper_pollAll() variables - int pollResult = 0; - int pollEvents = 0; - - // Wait for window to be initialized (display and context) - while (!CORE.Window.ready) - { - // Process events loop - while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&platform.source)) >= 0) - { - // Process this event - if (platform.source != NULL) platform.source->process(platform.app, platform.source); - - // NOTE: Never close window, native activity is controlled by the system! - //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true; - } - } + // Initialize platform + //-------------------------------------------------------------- + InitPlatform(); //-------------------------------------------------------------- } @@ -279,28 +215,9 @@ void CloseWindow(void) timeEndPeriod(1); // Restore time period #endif - // Platform specific close window - //-------------------------------------------------------------- - // Close surface, context and display - if (platform.device != EGL_NO_DISPLAY) - { - eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - - if (platform.surface != EGL_NO_SURFACE) - { - eglDestroySurface(platform.device, platform.surface); - platform.surface = EGL_NO_SURFACE; - } - - if (platform.context != EGL_NO_CONTEXT) - { - eglDestroyContext(platform.device, platform.context); - platform.context = EGL_NO_CONTEXT; - } - - eglTerminate(platform.device); - platform.device = EGL_NO_DISPLAY; - } + // De-initialize platform + //-------------------------------------------------------------- + ClosePlatform(); //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) @@ -690,25 +607,108 @@ void PollInputEvents(void) // Module Internal Functions Definition //---------------------------------------------------------------------------------- +// Initialize platform: graphics, inputs and more +static int InitPlatform(void) +{ + CORE.Window.currentFbo.width = CORE.Window.screen.width; + CORE.Window.currentFbo.height = CORE.Window.screen.width; + + // Set desired windows flags before initializing anything + ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER + + int orientation = AConfiguration_getOrientation(platform.app->config); + + if (orientation == ACONFIGURATION_ORIENTATION_PORT) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as portrait"); + else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape"); + + // TODO: Automatic orientation doesn't seem to work + if (width <= height) + { + AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_PORT); + TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait"); + } + else + { + AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_LAND); + TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to landscape"); + } + + //AConfiguration_getDensity(platform.app->config); + //AConfiguration_getKeyboard(platform.app->config); + //AConfiguration_getScreenSize(platform.app->config); + //AConfiguration_getScreenLong(platform.app->config); + + // Initialize App command system + // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()... + platform.app->onAppCmd = AndroidCommandCallback; + + // Initialize input events system + platform.app->onInputEvent = AndroidInputCallback; + + // Initialize assets manager + InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath); + + // Initialize base path for storage + CORE.Storage.basePath = platform.app->activity->internalDataPath; + + // Set some default window flags + CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false + CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true + CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false + + TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Application initialized successfully"); + + // Android ALooper_pollAll() variables + int pollResult = 0; + int pollEvents = 0; + + // Wait for window to be initialized (display and context) + while (!CORE.Window.ready) + { + // Process events loop + while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&platform.source)) >= 0) + { + // Process this event + if (platform.source != NULL) platform.source->process(platform.app, platform.source); + + // NOTE: Never close window, native activity is controlled by the system! + //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true; + } + } +} + +// Close platform +static void ClosePlatform(void) +{ + // Close surface, context and display + if (platform.device != EGL_NO_DISPLAY) + { + eglMakeCurrent(platform.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (platform.surface != EGL_NO_SURFACE) + { + eglDestroySurface(platform.device, platform.surface); + platform.surface = EGL_NO_SURFACE; + } + + if (platform.context != EGL_NO_CONTEXT) + { + eglDestroyContext(platform.device, platform.context); + platform.context = EGL_NO_CONTEXT; + } + + eglTerminate(platform.device); + platform.device = EGL_NO_DISPLAY; + } +} + // Initialize display device and framebuffer // NOTE: width and height represent the screen (framebuffer) desired size, not actual display size // If width or height are 0, default display size will be used for framebuffer size // NOTE: returns false in case graphic device could not be created -static bool InitGraphicsDevice(int width, int height) +static bool InitGraphicsDevice(void) { - CORE.Window.screen.width = width; // User desired width - CORE.Window.screen.height = height; // User desired height - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - - // Set the screen minimum and maximum default values to 0 - CORE.Window.screenMin.width = 0; - CORE.Window.screenMin.height = 0; - CORE.Window.screenMax.width = 0; - CORE.Window.screenMax.height = 0; - - // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... - // ...in top-down or left-right to match display aspect ratio (no weird scaling) - CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; @@ -748,7 +748,7 @@ static bool InitGraphicsDevice(int width, int height) if (platform.device == EGL_NO_DISPLAY) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); - return false; + return -1; } // Initialize the EGL device connection @@ -756,7 +756,7 @@ static bool InitGraphicsDevice(int width, int height) { // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred. TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); - return false; + return -1; } // Get an appropriate EGL framebuffer configuration @@ -770,7 +770,7 @@ static bool InitGraphicsDevice(int width, int height) if (platform.context == EGL_NO_CONTEXT) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); - return false; + return -1; } // Create an EGL window surface @@ -799,7 +799,7 @@ static bool InitGraphicsDevice(int width, int height) if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); - return false; + return -1; } else { @@ -819,19 +819,11 @@ static bool InitGraphicsDevice(int width, int height) // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(eglGetProcAddress); - // Initialize OpenGL context (states and resources) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - CORE.Window.ready = true; if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); - return true; + return 0; } // ANDROID: Process activity lifecycle commands @@ -874,24 +866,49 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) CORE.Window.display.height = ANativeWindow_getHeight(platform.app->window); // Initialize graphics device (display device and OpenGL context) - InitGraphicsDevice(CORE.Window.screen.width, CORE.Window.screen.height); + InitGraphicsDevice(); + + // Initialize OpenGL context (states and resources) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); // Initialize hi-res timer InitTimer(); - // Initialize random seed - srand((unsigned int)time(NULL)); - #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font // WARNING: External function: Module required: rtext LoadFontDefault(); - Rectangle rec = GetFontDefault().recs[95]; - // NOTE: We setup a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering #if defined(SUPPORT_MODULE_RSHAPES) - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); // WARNING: Module required: rshapes + // Set font white rectangle for shapes drawing, so shapes and text can be batched together + // WARNING: rshapes module is required, if not available, default internal white rectangle is used + Rectangle rec = GetFontDefault().recs[95]; + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); + } + else + { + // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); + } + #endif + #else + #if defined(SUPPORT_MODULE_RSHAPES) + // Set default texture and rectangle to be used for shapes drawing + // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 + Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes #endif #endif + + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); // TODO: GPU assets reload in case of lost focus (lost context) // NOTE: This problem has been solved just unbinding and rebinding context from display diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 8fa1edd78..81da488e3 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -111,7 +111,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static bool InitGraphicsDevice(int width, int height); // Initialize graphics device +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static void ClosePlatform(void); // Close platform // Error callback event static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error @@ -176,53 +177,31 @@ void InitWindow(int width, int height, const char *title) TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); #endif - // NOTE: Keep internal pointer to input title string (no copy) + // Initialize window data + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + CORE.Window.eventWaiting = false; + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE structure to 0 + memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 CORE.Input.Keyboard.exitKey = KEY_ESCAPE; CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Window.eventWaiting = false; - - - // Platform specific init window - //-------------------------------------------------------------- - glfwSetErrorCallback(ErrorCallback); -/* - // TODO: Setup GLFW custom allocators to match raylib ones - const GLFWallocator allocator = { - .allocate = MemAlloc, - .deallocate = MemFree, - .reallocate = MemRealloc, - .user = NULL - }; - - glfwInitAllocator(&allocator); -*/ - - // Initialize graphics device - // NOTE: returns true if window and graphic device has been initialized successfully - // WARNING: Actually, all window initialization and input callbacks initialization is - // done inside InitGraphicsDevice(), this functionality should be changed! - CORE.Window.ready = InitGraphicsDevice(width, height); - - // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } - else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); - - // Initialize hi-res timer - InitTimer(); + CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); + // Initialize platform + //-------------------------------------------------------------- + InitPlatform(); //-------------------------------------------------------------- + + // Initialize rlgl default data (buffers and shaders) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); + // Setup default viewport + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font @@ -266,6 +245,9 @@ void InitWindow(int width, int height, const char *title) CORE.Time.frameCounter = 0; #endif + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP: Application initialized successfully"); } @@ -287,14 +269,9 @@ void CloseWindow(void) rlglClose(); // De-init rlgl - // Platform specific close window - //-------------------------------------------------------------- - glfwDestroyWindow(platform.handle); - glfwTerminate(); - -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - timeEndPeriod(1); // Restore time period -#endif + // De-initialize platform + //-------------------------------------------------------------- + ClosePlatform(); //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) @@ -1379,34 +1356,28 @@ void PollInputEvents(void) // Module Internal Functions Definition //---------------------------------------------------------------------------------- -// Initialize display device and framebuffer -// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size -// If width or height are 0, default display size will be used for framebuffer size -// NOTE: returns false in case graphic device could not be created -static bool InitGraphicsDevice(int width, int height) +// Initialize platform: graphics, inputs and more +static int InitPlatform(void) { - CORE.Window.screen.width = width; // User desired width - CORE.Window.screen.height = height; // User desired height - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + glfwSetErrorCallback(ErrorCallback); +/* + // TODO: Setup GLFW custom allocators to match raylib ones + const GLFWallocator allocator = { + .allocate = MemAlloc, + .deallocate = MemFree, + .reallocate = MemRealloc, + .user = NULL + }; - // Set the screen minimum and maximum default values to 0 - CORE.Window.screenMin.width = 0; - CORE.Window.screenMin.height = 0; - CORE.Window.screenMax.width = 0; - CORE.Window.screenMax.height = 0; - - // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... - // ...in top-down or left-right to match display aspect ratio (no weird scaling) + glfwInitAllocator(&allocator); +*/ #if defined(__APPLE__) glfwInitHint(GLFW_COCOA_CHDIR_RESOURCES, GLFW_FALSE); #endif - - if (!glfwInit()) - { - TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); - return false; - } + // Initialize GLFW internal global state + int result = glfwInit(); + if (result == GLFW_FALSE) { TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); return -1; } glfwDefaultWindowHints(); // Set default windows hints //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits @@ -1528,7 +1499,7 @@ static bool InitGraphicsDevice(int width, int height) if (!monitor) { TRACELOG(LOG_WARNING, "GLFW: Failed to get primary monitor"); - return false; + return -1; } const GLFWvidmode *mode = glfwGetVideoMode(monitor); @@ -1617,7 +1588,7 @@ static bool InitGraphicsDevice(int width, int height) { glfwTerminate(); TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); - return false; + return -1; } // Set window callback events @@ -1683,22 +1654,35 @@ static bool InitGraphicsDevice(int width, int height) // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions - rlLoadExtensions(glfwGetProcAddress); - // Initialize OpenGL context (states and resources) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + // If graphic device is no properly initialized, we end program + if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } + else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); - return true; + // Initialize hi-res timer + InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + + return 0; } +// Close platform +static void ClosePlatform(void) +{ + glfwDestroyWindow(platform.handle); + glfwTerminate(); + +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) + timeEndPeriod(1); // Restore time period +#endif +} + + // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description) { diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 2644c5e73..213f2c546 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -140,21 +140,22 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static bool InitGraphicsDevice(int width, int height); // Initialize graphics device +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static void ClosePlatform(void); // Close platform -static void InitKeyboard(void); // Initialize raw keyboard system -static void RestoreKeyboard(void); // Restore keyboard system +static void InitKeyboard(void); // Initialize raw keyboard system +static void RestoreKeyboard(void); // Restore keyboard system #if defined(SUPPORT_SSH_KEYBOARD_RPI) -static void ProcessKeyboard(void); // Process keyboard events +static void ProcessKeyboard(void); // Process keyboard events #endif -static void InitEvdevInput(void); // Initialize evdev inputs -static void ConfigureEvdevDevice(char *device); // Identifies a input device and configures it for use if appropriate -static void PollKeyboardEvents(void); // Process evdev keyboard events -static void *EventThread(void *arg); // Input device events reading thread +static void InitEvdevInput(void); // Initialize evdev inputs +static void ConfigureEvdevDevice(char *device); // Identifies a input device and configures it for use if appropriate +static void PollKeyboardEvents(void); // Process evdev keyboard events +static void *EventThread(void *arg); // Input device events reading thread -static void InitGamepad(void); // Initialize raw gamepad input -static void *GamepadThread(void *arg); // Mouse reading thread +static void InitGamepad(void); // Initialize raw gamepad input +static void *GamepadThread(void *arg); // Mouse reading thread static int FindMatchingConnectorMode(const drmModeConnector *connector, const drmModeModeInfo *mode); // Search matching DRM mode in connector's mode list static int FindExactConnectorMode(const drmModeConnector *connector, uint width, uint height, uint fps, bool allowInterlaced); // Search exactly matching DRM connector mode in connector's list @@ -204,49 +205,31 @@ void InitWindow(int width, int height, const char *title) TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); #endif - // NOTE: Keep internal pointer to input title string (no copy) + // Initialize window data + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + CORE.Window.eventWaiting = false; + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); + memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 CORE.Input.Keyboard.exitKey = KEY_ESCAPE; - CORE.Input.Mouse.scale = (Vector2){1.0f, 1.0f}; + CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Window.eventWaiting = false; - - - // Platform specific init window + CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; + + // Initialize platform + //-------------------------------------------------------------- + InitPlatform(); //-------------------------------------------------------------- - // Initialize graphics device (display device and OpenGL context) - // NOTE: returns true if window and graphic device has been initialized successfully - CORE.Window.ready = InitGraphicsDevice(width, height); + + // Initialize rlgl default data (buffers and shaders) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } - else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); - - // Set some default window flags - CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false - CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false - CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true - CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false - - // Initialize hi-res timer - InitTimer(); - - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - - // Initialize raw input system - InitEvdevInput(); // Evdev inputs initialization - InitGamepad(); // Gamepad init - InitKeyboard(); // Keyboard init (stdin) - //-------------------------------------------------------------- - - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); + // Setup default viewport + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font @@ -289,7 +272,10 @@ void InitWindow(int width, int height, const char *title) events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); CORE.Time.frameCounter = 0; #endif - + + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); + TRACELOG(LOG_INFO, "PLATFORM: DRM: Application initialized successfully"); } @@ -315,93 +301,9 @@ void CloseWindow(void) timeEndPeriod(1); // Restore time period #endif - // Platform specific close window - //-------------------------------------------------------------- - if (platform.prevFB) - { - drmModeRmFB(platform.fd, platform.prevFB); - platform.prevFB = 0; - } - - if (platform.prevBO) - { - gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO); - platform.prevBO = NULL; - } - - if (platform.gbmSurface) - { - gbm_surface_destroy(platform.gbmSurface); - platform.gbmSurface = NULL; - } - - if (platform.gbmDevice) - { - gbm_device_destroy(platform.gbmDevice); - platform.gbmDevice = NULL; - } - - if (platform.crtc) - { - if (platform.connector) - { - drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, platform.crtc->buffer_id, - platform.crtc->x, platform.crtc->y, &platform.connector->connector_id, 1, &platform.crtc->mode); - drmModeFreeConnector(platform.connector); - platform.connector = NULL; - } - - drmModeFreeCrtc(platform.crtc); - platform.crtc = NULL; - } - - if (platform.fd != -1) - { - close(platform.fd); - platform.fd = -1; - } - - // Close surface, context and display - if (platform.device != EGL_NO_DISPLAY) - { - if (platform.surface != EGL_NO_SURFACE) - { - eglDestroySurface(platform.device, platform.surface); - platform.surface = EGL_NO_SURFACE; - } - - if (platform.context != EGL_NO_CONTEXT) - { - eglDestroyContext(platform.device, platform.context); - platform.context = EGL_NO_CONTEXT; - } - - eglTerminate(platform.device); - platform.device = EGL_NO_DISPLAY; - } - - // Wait for mouse and gamepad threads to finish before closing - // NOTE: Those threads should already have finished at this point - // because they are controlled by CORE.Window.shouldClose variable - - CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called - - // Close the evdev keyboard - if (platform.keyboardFd != -1) - { - close(platform.keyboardFd); - platform.keyboardFd = -1; - } - - for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i) - { - if (platform.eventWorker[i].threadId) - { - pthread_join(platform.eventWorker[i].threadId, NULL); - } - } - - if (platform.gamepadThreadId) pthread_join(platform.gamepadThreadId, NULL); + // De-initialize platform + //-------------------------------------------------------------- + ClosePlatform(); //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) @@ -808,28 +710,9 @@ void PollInputEvents(void) // Module Internal Functions Definition //---------------------------------------------------------------------------------- -// Initialize display device and framebuffer -// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size -// If width or height are 0, default display size will be used for framebuffer size -// NOTE: returns false in case graphic device could not be created -static bool InitGraphicsDevice(int width, int height) +// Initialize platform: graphics, inputs and more +static int InitPlatform(void) { - CORE.Window.screen.width = width; // User desired width - CORE.Window.screen.height = height; // User desired height - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - - // Set the window minimum and maximum default values to 0 - CORE.Window.screenMin.width = 0; - CORE.Window.screenMin.height = 0; - CORE.Window.screenMax.width = 0; - CORE.Window.screenMax.height = 0; - - // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... - // ...in top-down or left-right to match display aspect ratio (no weird scaling) - - CORE.Window.fullscreen = true; - CORE.Window.flags |= FLAG_FULLSCREEN_MODE; - platform.fd = -1; platform.connector = NULL; platform.modeIndex = -1; @@ -838,6 +721,9 @@ static bool InitGraphicsDevice(int width, int height) platform.gbmSurface = NULL; platform.prevBO = NULL; platform.prevFB = 0; + + CORE.Window.fullscreen = true; + CORE.Window.flags |= FLAG_FULLSCREEN_MODE; #if defined(DEFAULT_GRAPHIC_DEVICE_DRM) platform.fd = open(DEFAULT_GRAPHIC_DEVICE_DRM, O_RDWR); @@ -861,14 +747,14 @@ static bool InitGraphicsDevice(int width, int height) if (platform.fd == -1) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to open graphic card"); - return false; + return -1; } drmModeRes *res = drmModeGetResources(platform.fd); if (!res) { TRACELOG(LOG_WARNING, "DISPLAY: Failed get DRM resources"); - return false; + return -1; } TRACELOG(LOG_TRACE, "DISPLAY: Connectors found: %i", res->count_connectors); @@ -897,7 +783,7 @@ static bool InitGraphicsDevice(int width, int height) { TRACELOG(LOG_WARNING, "DISPLAY: No suitable DRM connector found"); drmModeFreeResources(res); - return false; + return -1; } drmModeEncoder *enc = drmModeGetEncoder(platform.fd, platform.connector->encoder_id); @@ -905,7 +791,7 @@ static bool InitGraphicsDevice(int width, int height) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode encoder"); drmModeFreeResources(res); - return false; + return -1; } platform.crtc = drmModeGetCrtc(platform.fd, enc->crtc_id); @@ -914,7 +800,7 @@ static bool InitGraphicsDevice(int width, int height) TRACELOG(LOG_WARNING, "DISPLAY: Failed to get DRM mode crtc"); drmModeFreeEncoder(enc); drmModeFreeResources(res); - return false; + return -1; } // If InitWindow should use the current mode find it in the connector's mode list @@ -929,7 +815,7 @@ static bool InitGraphicsDevice(int width, int height) TRACELOG(LOG_WARNING, "DISPLAY: No matching DRM connector mode found"); drmModeFreeEncoder(enc); drmModeFreeResources(res); - return false; + return -1; } CORE.Window.screen.width = CORE.Window.display.width; @@ -957,7 +843,7 @@ static bool InitGraphicsDevice(int width, int height) TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable DRM connector mode"); drmModeFreeEncoder(enc); drmModeFreeResources(res); - return false; + return -1; } CORE.Window.display.width = platform.connector->modes[platform.modeIndex].hdisplay; @@ -982,7 +868,7 @@ static bool InitGraphicsDevice(int width, int height) if (!platform.gbmDevice) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM device"); - return false; + return -1; } platform.gbmSurface = gbm_surface_create(platform.gbmDevice, platform.connector->modes[platform.modeIndex].hdisplay, @@ -990,7 +876,7 @@ static bool InitGraphicsDevice(int width, int height) if (!platform.gbmSurface) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create GBM surface"); - return false; + return -1; } EGLint samples = 0; @@ -1030,7 +916,7 @@ static bool InitGraphicsDevice(int width, int height) if (platform.device == EGL_NO_DISPLAY) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); - return false; + return -1; } // Initialize the EGL device connection @@ -1038,13 +924,13 @@ static bool InitGraphicsDevice(int width, int height) { // If all of the calls to eglInitialize returned EGL_FALSE then an error has occurred. TRACELOG(LOG_WARNING, "DISPLAY: Failed to initialize EGL device"); - return false; + return -1; } if (!eglChooseConfig(platform.device, NULL, NULL, 0, &numConfigs)) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get EGL config count: 0x%x", eglGetError()); - return false; + return -1; } TRACELOG(LOG_TRACE, "DISPLAY: EGL configs available: %d", numConfigs); @@ -1053,7 +939,7 @@ static bool InitGraphicsDevice(int width, int height) if (!configs) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to get memory for EGL configs"); - return false; + return -1; } EGLint matchingNumConfigs = 0; @@ -1061,7 +947,7 @@ static bool InitGraphicsDevice(int width, int height) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to choose EGL config: 0x%x", eglGetError()); free(configs); - return false; + return -1; } TRACELOG(LOG_TRACE, "DISPLAY: EGL matching configs available: %d", matchingNumConfigs); @@ -1091,7 +977,7 @@ static bool InitGraphicsDevice(int width, int height) if (!found) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to find a suitable EGL config"); - return false; + return -1; } // Set rendering API @@ -1102,7 +988,7 @@ static bool InitGraphicsDevice(int width, int height) if (platform.context == EGL_NO_CONTEXT) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); - return false; + return -1; } // Create an EGL window surface @@ -1111,7 +997,7 @@ static bool InitGraphicsDevice(int width, int height) if (EGL_NO_SURFACE == platform.surface) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL window surface: 0x%04x", eglGetError()); - return false; + return -1; } // At this point we need to manage render size vs screen size @@ -1127,7 +1013,7 @@ static bool InitGraphicsDevice(int width, int height) if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); - return false; + return -1; } else { @@ -1147,19 +1033,123 @@ static bool InitGraphicsDevice(int width, int height) // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(eglGetProcAddress); - // Initialize OpenGL context (states and resources) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + // If graphic device is no properly initialized, we end program + if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } + else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); - return true; + // Set some default window flags + CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false + CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true + CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false + + // Initialize hi-res timer + InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + + // Initialize raw input system + InitEvdevInput(); // Evdev inputs initialization + InitGamepad(); // Gamepad init + InitKeyboard(); // Keyboard init (stdin) + + return 0; } +// Close platform +static void ClosePlatform(void) +{ + if (platform.prevFB) + { + drmModeRmFB(platform.fd, platform.prevFB); + platform.prevFB = 0; + } + + if (platform.prevBO) + { + gbm_surface_release_buffer(platform.gbmSurface, platform.prevBO); + platform.prevBO = NULL; + } + + if (platform.gbmSurface) + { + gbm_surface_destroy(platform.gbmSurface); + platform.gbmSurface = NULL; + } + + if (platform.gbmDevice) + { + gbm_device_destroy(platform.gbmDevice); + platform.gbmDevice = NULL; + } + + if (platform.crtc) + { + if (platform.connector) + { + drmModeSetCrtc(platform.fd, platform.crtc->crtc_id, platform.crtc->buffer_id, + platform.crtc->x, platform.crtc->y, &platform.connector->connector_id, 1, &platform.crtc->mode); + drmModeFreeConnector(platform.connector); + platform.connector = NULL; + } + + drmModeFreeCrtc(platform.crtc); + platform.crtc = NULL; + } + + if (platform.fd != -1) + { + close(platform.fd); + platform.fd = -1; + } + + // Close surface, context and display + if (platform.device != EGL_NO_DISPLAY) + { + if (platform.surface != EGL_NO_SURFACE) + { + eglDestroySurface(platform.device, platform.surface); + platform.surface = EGL_NO_SURFACE; + } + + if (platform.context != EGL_NO_CONTEXT) + { + eglDestroyContext(platform.device, platform.context); + platform.context = EGL_NO_CONTEXT; + } + + eglTerminate(platform.device); + platform.device = EGL_NO_DISPLAY; + } + + // Wait for mouse and gamepad threads to finish before closing + // NOTE: Those threads should already have finished at this point + // because they are controlled by CORE.Window.shouldClose variable + + CORE.Window.shouldClose = true; // Added to force threads to exit when the close window is called + + // Close the evdev keyboard + if (platform.keyboardFd != -1) + { + close(platform.keyboardFd); + platform.keyboardFd = -1; + } + + for (int i = 0; i < sizeof(platform.eventWorker)/sizeof(InputEventWorker); ++i) + { + if (platform.eventWorker[i].threadId) + { + pthread_join(platform.eventWorker[i].threadId, NULL); + } + } + + if (platform.gamepadThreadId) pthread_join(platform.gamepadThreadId, NULL); +} + + // Initialize Keyboard system (using standard input) static void InitKeyboard(void) { diff --git a/src/rcore_template.c b/src/rcore_template.c index 88b3c4a7e..3929de4b3 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -73,7 +73,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static bool InitGraphicsDevice(int width, int height); // Initialize graphics device +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static bool InitGraphicsDevice(void); // Initialize graphics device //---------------------------------------------------------------------------------- // Module Functions Declaration @@ -142,19 +143,15 @@ void InitWindow(int width, int height, const char *title) // NOTE: returns true if window and graphic device has been initialized successfully CORE.Window.ready = InitGraphicsDevice(width, height); - // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } - // Initialize hi-res timer - InitTimer(); - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - //-------------------------------------------------------------- + // Initialize OpenGL context (states and resources) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font @@ -198,6 +195,9 @@ void InitWindow(int width, int height, const char *title) CORE.Time.frameCounter = 0; #endif + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); + TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Application initialized successfully"); } @@ -597,25 +597,9 @@ void PollInputEvents(void) // Module Internal Functions Definition //---------------------------------------------------------------------------------- -// Initialize display device and framebuffer -// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size -// If width or height are 0, default display size will be used for framebuffer size -// NOTE: returns false in case graphic device could not be created -static bool InitGraphicsDevice(int width, int height) +// Initialize platform: graphics, inputs and more +static int InitPlatform(void) { - CORE.Window.screen.width = width; // User desired width - CORE.Window.screen.height = height; // User desired height - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - - // Set the screen minimum and maximum default values to 0 - CORE.Window.screenMin.width = 0; - CORE.Window.screenMin.height = 0; - CORE.Window.screenMax.width = 0; - CORE.Window.screenMax.height = 0; - - // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... - // ...in top-down or left-right to match display aspect ratio (no weird scaling) - CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; @@ -677,7 +661,7 @@ static bool InitGraphicsDevice(int width, int height) if (platform.context == EGL_NO_CONTEXT) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to create EGL context"); - return false; + return -1; } // Create an EGL window surface @@ -706,7 +690,7 @@ static bool InitGraphicsDevice(int width, int height) if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); - return false; + return -1; } else { @@ -726,19 +710,24 @@ static bool InitGraphicsDevice(int width, int height) // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(eglGetProcAddress); - // Initialize OpenGL context (states and resources) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - CORE.Window.ready = true; + + // If graphic device is no properly initialized, we end program + if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } - if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + // Initialize hi-res timer + InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); - return true; + return 0; +} + +// Close platform +static void ClosePlatform(void) +{ + // TODO: De-initialize graphics, inputs and more } // EOF diff --git a/src/rcore_web.c b/src/rcore_web.c index 277d1378f..3b9373bbf 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -87,17 +87,18 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static bool InitGraphicsDevice(int width, int height); // Initialize graphics device +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static void ClosePlatform(void); // Close platform // Error callback event -static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error +static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error // Window callbacks events -static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized -static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored -static void WindowMaximizeCallback(GLFWwindow *window, int maximized); // GLFW3 Window Maximize Callback, runs when window is maximized -static void WindowFocusCallback(GLFWwindow *window, int focused); // GLFW3 WindowFocus Callback, runs when window get/lose focus -static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window +static void WindowSizeCallback(GLFWwindow *window, int width, int height); // GLFW3 WindowSize Callback, runs when window is resized +static void WindowIconifyCallback(GLFWwindow *window, int iconified); // GLFW3 WindowIconify Callback, runs when window is minimized/restored +static void WindowMaximizeCallback(GLFWwindow *window, int maximized); // GLFW3 Window Maximize Callback, runs when window is maximized +static void WindowFocusCallback(GLFWwindow *window, int focused); // GLFW3 WindowFocus Callback, runs when window get/lose focus +static void WindowDropCallback(GLFWwindow *window, int count, const char **paths); // GLFW3 Window Drop Callback, runs when drop files into window // Input callbacks events static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, int mods); // GLFW3 Keyboard Callback, runs on key pressed @@ -107,11 +108,12 @@ static void MouseCursorPosCallback(GLFWwindow *window, double x, double y); static void MouseScrollCallback(GLFWwindow *window, double xoffset, double yoffset); // GLFW3 Srolling Callback, runs on mouse wheel static void CursorEnterCallback(GLFWwindow *window, int enter); // GLFW3 Cursor Enter Callback, cursor enters client area -// Emscripten callback events +// Emscripten window callback events static EM_BOOL EmscriptenFullscreenChangeCallback(int eventType, const EmscriptenFullscreenChangeEvent *event, void *userData); static EM_BOOL EmscriptenWindowResizedCallback(int eventType, const EmscriptenUiEvent *event, void *userData); static EM_BOOL EmscriptenResizeCallback(int eventType, const EmscriptenUiEvent *event, void *userData); +// Emscripten input callback events static EM_BOOL EmscriptenMouseCallback(int eventType, const EmscriptenMouseEvent *mouseEvent, void *userData); static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent *touchEvent, void *userData); static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadEvent *gamepadEvent, void *userData); @@ -160,67 +162,32 @@ void InitWindow(int width, int height, const char *title) TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); #endif - // NOTE: Keep internal pointer to input title string (no copy) + // Initialize window data + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + CORE.Window.eventWaiting = false; + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); + memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 CORE.Input.Keyboard.exitKey = KEY_ESCAPE; - CORE.Input.Mouse.scale = (Vector2){1.0f, 1.0f}; + CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Window.eventWaiting = false; + CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - - // Platform specific init window - //-------------------------------------------------------------- - // Initialize graphics device (display device and OpenGL context) - // NOTE: returns true if window and graphic device has been initialized successfully - CORE.Window.ready = InitGraphicsDevice(width, height); - - // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return; } - else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); - - // Initialize hi-res timer - InitTimer(); - - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - - // Setup callback functions for the DOM events - emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); - - // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review - // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas) - // emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - // Check Resize event (note this is done on the window since most browsers don't support this on #canvas) - emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - - // Trigger this once to get initial window sizing - EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); - - // Support keyboard events -> Not used, GLFW.JS takes care of that - // emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); - // emscripten_set_keydown_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); - - // Support mouse events - emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback); - - // Support touch events - emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenTouchCallback); - - // Support gamepad events (not provided by GLFW3 on emscripten) - emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); - emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); + // Initialize platform + //-------------------------------------------------------------- + InitPlatform(); //-------------------------------------------------------------- + // Initialize OpenGL context (states and resources) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); + // Setup default viewport + // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); #if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) // Load default font @@ -264,6 +231,9 @@ void InitWindow(int width, int height, const char *title) CORE.Time.frameCounter = 0; #endif + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); + TRACELOG(LOG_INFO, "PLATFORM: WEB: Application initialized successfully"); } @@ -285,10 +255,9 @@ void CloseWindow(void) rlglClose(); // De-init rlgl - // Platform specific close window - //-------------------------------------------------------------- - glfwDestroyWindow(platform.handle); - glfwTerminate(); + // De-initialize platform + //-------------------------------------------------------------- + ClosePlatform(); //-------------------------------------------------------------- #if defined(SUPPORT_EVENTS_AUTOMATION) @@ -814,43 +783,14 @@ void PollInputEvents(void) // Module Internal Functions Definition //---------------------------------------------------------------------------------- -// Initialize display device and framebuffer -// NOTE: width and height represent the screen (framebuffer) desired size, not actual display size -// If width or height are 0, default display size will be used for framebuffer size -// NOTE: returns false in case graphic device could not be created -static bool InitGraphicsDevice(int width, int height) +// Initialize platform: graphics, inputs and more +static int InitPlatform(void) { - CORE.Window.screen.width = width; // User desired width - CORE.Window.screen.height = height; // User desired height - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - - // Set the screen minimum and maximum default values to 0 - CORE.Window.screenMin.width = 0; - CORE.Window.screenMin.height = 0; - CORE.Window.screenMax.width = 0; - CORE.Window.screenMax.height = 0; - - // NOTE: Framebuffer (render area - CORE.Window.render.width, CORE.Window.render.height) could include black bars... - // ...in top-down or left-right to match display aspect ratio (no weird scaling) - glfwSetErrorCallback(ErrorCallback); -/* - // TODO: Setup GLFW custom allocators to match raylib ones - const GLFWallocator allocator = { - .allocate = MemAlloc, - .deallocate = MemFree, - .reallocate = MemRealloc, - .user = NULL - }; - glfwInitAllocator(&allocator); -*/ - - if (!glfwInit()) - { - TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); - return false; - } + // Initialize GLFW internal global state + int result = glfwInit(); + if (result == GLFW_FALSE) { TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); return -1; } glfwDefaultWindowHints(); // Set default windows hints // glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits @@ -1016,7 +956,7 @@ static bool InitGraphicsDevice(int width, int height) { glfwTerminate(); TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); - return false; + return -1; } // WARNING: glfwCreateWindow() title doesn't work with emscripten @@ -1037,6 +977,12 @@ static bool InitGraphicsDevice(int width, int height) glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); glfwMakeContextCurrent(platform.handle); + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(glfwGetProcAddress); + + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need @@ -1055,22 +1001,54 @@ static bool InitGraphicsDevice(int width, int height) TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + + // If graphic device is no properly initialized, we end program + if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } + else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); - // Load OpenGL extensions - // NOTE: GL procedures address loader is required to load extensions - rlLoadExtensions(glfwGetProcAddress); + // Initialize hi-res timer + InitTimer(); + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + + // Setup callback functions for the DOM events + emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); - // Initialize OpenGL context (states and resources) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review + // Check fullscreen change events(note this is done on the window since most browsers don't support this on #canvas) + // emscripten_set_fullscreenchange_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); + // Check Resize event (note this is done on the window since most browsers don't support this on #canvas) + emscripten_set_resize_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, NULL, 1, EmscriptenResizeCallback); - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + // Trigger this once to get initial window sizing + EmscriptenResizeCallback(EMSCRIPTEN_EVENT_RESIZE, NULL, NULL); - if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + // Support keyboard events -> Not used, GLFW.JS takes care of that + // emscripten_set_keypress_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); + // emscripten_set_keydown_callback("#canvas", NULL, 1, EmscriptenKeyboardCallback); - return true; + // Support mouse events + emscripten_set_click_callback("#canvas", NULL, 1, EmscriptenMouseCallback); + + // Support touch events + emscripten_set_touchstart_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + emscripten_set_touchend_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + emscripten_set_touchmove_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + emscripten_set_touchcancel_callback("#canvas", NULL, 1, EmscriptenTouchCallback); + + // Support gamepad events (not provided by GLFW3 on emscripten) + emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); + emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); + + return 0; +} + +// Close platform +static void ClosePlatform(void) +{ + glfwDestroyWindow(platform.handle); + glfwTerminate(); } // GLFW3 Error Callback, runs on GLFW3 error From 54950f9a3d2df2e0f9907715d6c355133d011456 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 14 Oct 2023 12:55:31 +0200 Subject: [PATCH 0359/1350] Make sure CORE.Window.ready is set --- src/rcore_desktop.c | 2 ++ src/rcore_drm.c | 2 ++ src/rcore_web.c | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 81da488e3..fe5a02aac 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -1657,6 +1657,8 @@ static int InitPlatform(void) rlLoadExtensions(glfwGetProcAddress); if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + + CORE.Window.ready = true; // TODO: Proper validation on windows/context creation // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 213f2c546..8f1bfa429 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -1035,6 +1035,8 @@ static int InitPlatform(void) if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); + CORE.Window.ready = true; // TODO: Proper validation on windows/context creation + // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); diff --git a/src/rcore_web.c b/src/rcore_web.c index 3b9373bbf..15c7626dd 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -1002,6 +1002,8 @@ static int InitPlatform(void) TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + CORE.Window.ready = true; // TODO: Proper validation on windows/context creation + // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); From d31b439e04d311ea8068a1d20d573b73c812c645 Mon Sep 17 00:00:00 2001 From: BeardedBread Date: Sat, 14 Oct 2023 21:10:33 +0800 Subject: [PATCH 0360/1350] Implement SetMouseCursor for PLATFORM_WEB (#3414) --- src/rcore_web.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/rcore_web.c b/src/rcore_web.c index 15c7626dd..71e818ea8 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -659,7 +659,34 @@ void SetMousePosition(int x, int y) // Set mouse cursor void SetMouseCursor(int cursor) { - TRACELOG(LOG_INFO, "SetMouseCursor not implemented in rcore_web.c"); + const char *cursorName; + switch (cursor) + { + case MOUSE_CURSOR_IBEAM: cursorName = "text"; break; + case MOUSE_CURSOR_CROSSHAIR: cursorName = "crosshair"; break; + case MOUSE_CURSOR_POINTING_HAND: cursorName = "pointer"; break; + case MOUSE_CURSOR_RESIZE_EW: cursorName = "ew-resize"; break; + case MOUSE_CURSOR_RESIZE_NS: cursorName = "ns-resize"; break; + case MOUSE_CURSOR_RESIZE_NWSE: cursorName = "nwse-resize"; break; + case MOUSE_CURSOR_RESIZE_NESW: cursorName = "nesw-resize"; break; + case MOUSE_CURSOR_RESIZE_ALL: cursorName = "move"; break; + case MOUSE_CURSOR_NOT_ALLOWED: cursorName = "not-allowed"; break; + + case MOUSE_CURSOR_ARROW: // can't find a name specifically for arrow cursor + case MOUSE_CURSOR_DEFAULT: + { + cursorName = "default"; + } break; + + default: + { + TRACELOG(LOG_WARNING, "Cursor value out of bound (%d). Setting to default", cursor); + cursorName = "default"; + } break; + } + + // Set the cursor element on the CSS + EM_ASM({document.body.style.cursor = UTF8ToString($0);}, cursorName); } // Register all input events From 2498170b9525df447f63da4056e5c8c0e6d1d8fc Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Sat, 14 Oct 2023 15:11:56 +0200 Subject: [PATCH 0361/1350] Fix screen size check in `InitPlatform()` (#3415) --- src/rcore_android.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rcore_android.c b/src/rcore_android.c index 3e3b8cd12..b7680adbc 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -611,7 +611,7 @@ void PollInputEvents(void) static int InitPlatform(void) { CORE.Window.currentFbo.width = CORE.Window.screen.width; - CORE.Window.currentFbo.height = CORE.Window.screen.width; + CORE.Window.currentFbo.height = CORE.Window.screen.height; // Set desired windows flags before initializing anything ANativeActivity_setWindowFlags(platform.app->activity, AWINDOW_FLAG_FULLSCREEN, 0); //AWINDOW_FLAG_SCALED, AWINDOW_FLAG_DITHER @@ -622,7 +622,7 @@ static int InitPlatform(void) else if (orientation == ACONFIGURATION_ORIENTATION_LAND) TRACELOG(LOG_INFO, "ANDROID: Window orientation set as landscape"); // TODO: Automatic orientation doesn't seem to work - if (width <= height) + if (CORE.Window.screen.width <= CORE.Window.screen.height) { AConfiguration_setOrientation(platform.app->config, ACONFIGURATION_ORIENTATION_PORT); TRACELOG(LOG_WARNING, "ANDROID: Window orientation changed to portrait"); From bf639f02a8a05f678c6d2459871a9e466be34b97 Mon Sep 17 00:00:00 2001 From: Blue <69832658+bluesillybeard@users.noreply.github.com> Date: Sat, 14 Oct 2023 14:38:36 -0600 Subject: [PATCH 0362/1350] Fix raygui.c leftover from zig build (#3417) --- src/build.zig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/build.zig b/src/build.zig index 53e074245..5d7ddbf3f 100644 --- a/src/build.zig +++ b/src/build.zig @@ -53,12 +53,12 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built }, raylib_flags); } - var gen_step = std.build.Step.WriteFile.create(b); + var gen_step = b.addWriteFiles(); raylib.step.dependOn(&gen_step.step); if (options.raygui) { - _ = gen_step.add(srcdir ++ "/raygui.c", "#define RAYGUI_IMPLEMENTATION\n#include \"raygui.h\"\n"); - raylib.addCSourceFile(.{ .file = .{ .path = srcdir ++ "/raygui.c" }, .flags = raylib_flags }); + const raygui_c_path = gen_step.add("raygui.c", "#define RAYGUI_IMPLEMENTATION\n#include \"raygui.h\"\n"); + raylib.addCSourceFile(.{ .file = raygui_c_path, .flags = raylib_flags }); raylib.addIncludePath(.{ .path = srcdir }); raylib.addIncludePath(.{ .path = srcdir ++ "/../../raygui/src" }); } From b79e38109268bc23eeceb05212017d3ed1359170 Mon Sep 17 00:00:00 2001 From: BeardedBread Date: Sun, 15 Oct 2023 04:42:03 +0800 Subject: [PATCH 0363/1350] Fix SetMouseCursor implementation for PLATFORM_WEB (#3416) * Fix SetMouseCursor implementation for PLATFORM_WEB - Restrict function to only set the cursor inside the canvas * Set the CORE input mouse --- src/rcore_web.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/rcore_web.c b/src/rcore_web.c index 71e818ea8..34836b7f0 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -659,6 +659,7 @@ void SetMousePosition(int x, int y) // Set mouse cursor void SetMouseCursor(int cursor) { + CORE.Input.Mouse.cursor = cursor; const char *cursorName; switch (cursor) { @@ -682,11 +683,13 @@ void SetMouseCursor(int cursor) { TRACELOG(LOG_WARNING, "Cursor value out of bound (%d). Setting to default", cursor); cursorName = "default"; + CORE.Input.Mouse.cursor = MOUSE_CURSOR_DEFAULT; } break; } - // Set the cursor element on the CSS - EM_ASM({document.body.style.cursor = UTF8ToString($0);}, cursorName); + // Set the cursor element on the canvas CSS + // The canvas is coded to the Id "canvas" on init + EM_ASM({document.getElementById("canvas").style.cursor = UTF8ToString($0);}, cursorName); } // Register all input events From 37e3ffcaac9be9c07a9724b334d9bca3ec8b7052 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 14 Oct 2023 22:45:56 +0200 Subject: [PATCH 0364/1350] REVIEWED: `SetMouseCursor()` #3416 --- src/rcore_web.c | 50 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/src/rcore_web.c b/src/rcore_web.c index 34836b7f0..af2b6e36f 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -659,37 +659,35 @@ void SetMousePosition(int x, int y) // Set mouse cursor void SetMouseCursor(int cursor) { - CORE.Input.Mouse.cursor = cursor; - const char *cursorName; - switch (cursor) + if (CORE.Input.Mouse.cursor != cursor) { - case MOUSE_CURSOR_IBEAM: cursorName = "text"; break; - case MOUSE_CURSOR_CROSSHAIR: cursorName = "crosshair"; break; - case MOUSE_CURSOR_POINTING_HAND: cursorName = "pointer"; break; - case MOUSE_CURSOR_RESIZE_EW: cursorName = "ew-resize"; break; - case MOUSE_CURSOR_RESIZE_NS: cursorName = "ns-resize"; break; - case MOUSE_CURSOR_RESIZE_NWSE: cursorName = "nwse-resize"; break; - case MOUSE_CURSOR_RESIZE_NESW: cursorName = "nesw-resize"; break; - case MOUSE_CURSOR_RESIZE_ALL: cursorName = "move"; break; - case MOUSE_CURSOR_NOT_ALLOWED: cursorName = "not-allowed"; break; + const char *cursorName = NULL; + CORE.Input.Mouse.cursor = cursor; - case MOUSE_CURSOR_ARROW: // can't find a name specifically for arrow cursor - case MOUSE_CURSOR_DEFAULT: + switch (cursor) { - cursorName = "default"; - } break; + case MOUSE_CURSOR_IBEAM: cursorName = "text"; break; + case MOUSE_CURSOR_CROSSHAIR: cursorName = "crosshair"; break; + case MOUSE_CURSOR_POINTING_HAND: cursorName = "pointer"; break; + case MOUSE_CURSOR_RESIZE_EW: cursorName = "ew-resize"; break; + case MOUSE_CURSOR_RESIZE_NS: cursorName = "ns-resize"; break; + case MOUSE_CURSOR_RESIZE_NWSE: cursorName = "nwse-resize"; break; + case MOUSE_CURSOR_RESIZE_NESW: cursorName = "nesw-resize"; break; + case MOUSE_CURSOR_RESIZE_ALL: cursorName = "move"; break; + case MOUSE_CURSOR_NOT_ALLOWED: cursorName = "not-allowed"; break; + case MOUSE_CURSOR_ARROW: // WARNING: It does not seem t be a specific cursor for arrow + case MOUSE_CURSOR_DEFAULT: cursorName = "default"; break; + default: + { + cursorName = "default"; + CORE.Input.Mouse.cursor = MOUSE_CURSOR_DEFAULT; + } break; + } - default: - { - TRACELOG(LOG_WARNING, "Cursor value out of bound (%d). Setting to default", cursor); - cursorName = "default"; - CORE.Input.Mouse.cursor = MOUSE_CURSOR_DEFAULT; - } break; + // Set the cursor element on the canvas CSS + // The canvas is coded to the Id "canvas" on init + EM_ASM({document.getElementById("canvas").style.cursor = UTF8ToString($0);}, cursorName); } - - // Set the cursor element on the canvas CSS - // The canvas is coded to the Id "canvas" on init - EM_ASM({document.getElementById("canvas").style.cursor = UTF8ToString($0);}, cursorName); } // Register all input events From 6d7112fde7ee2f3372f7cdc95a9f474328973cdc Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Sat, 14 Oct 2023 22:46:46 +0200 Subject: [PATCH 0365/1350] Fix some omissions (#3418) Changes the return type of `InitGraphicsDevice()` from `bool` to `int`. Adds a return at the end of `InitPlatform()`. --- src/rcore_android.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rcore_android.c b/src/rcore_android.c index b7680adbc..4dc0e26c4 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -676,6 +676,8 @@ static int InitPlatform(void) //if (platform.app->destroyRequested != 0) CORE.Window.shouldClose = true; } } + + return 0; } // Close platform @@ -707,7 +709,7 @@ static void ClosePlatform(void) // NOTE: width and height represent the screen (framebuffer) desired size, not actual display size // If width or height are 0, default display size will be used for framebuffer size // NOTE: returns false in case graphic device could not be created -static bool InitGraphicsDevice(void) +static int InitGraphicsDevice(void) { CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; From 781f7175308f0b708393b1135657aa191ca2b508 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sat, 14 Oct 2023 17:47:35 -0300 Subject: [PATCH 0366/1350] Remove the rcore.h include from drm, web, template (#3420) --- src/rcore_drm.c | 22 ++++++++++------------ src/rcore_template.c | 12 +++++------- src/rcore_web.c | 18 ++++++++---------- 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 8f1bfa429..3f03bfa12 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -47,8 +47,6 @@ * **********************************************************************************************/ -#include "rcore.h" - #include // POSIX file control definitions - open(), creat(), fcntl() #include // POSIX standard function definitions - read(), close(), STDIN_FILENO #include // POSIX terminal control definitions - tcgetattr(), tcsetattr() @@ -218,12 +216,12 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - + // Initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- InitPlatform(); //-------------------------------------------------------------- - + // Initialize rlgl default data (buffers and shaders) // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); @@ -272,10 +270,10 @@ void InitWindow(int width, int height, const char *title) events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); CORE.Time.frameCounter = 0; #endif - + // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); - + TRACELOG(LOG_INFO, "PLATFORM: DRM: Application initialized successfully"); } @@ -302,7 +300,7 @@ void CloseWindow(void) #endif // De-initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- ClosePlatform(); //-------------------------------------------------------------- @@ -721,7 +719,7 @@ static int InitPlatform(void) platform.gbmSurface = NULL; platform.prevBO = NULL; platform.prevFB = 0; - + CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; @@ -1034,9 +1032,9 @@ static int InitPlatform(void) rlLoadExtensions(eglGetProcAddress); if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); - + CORE.Window.ready = true; // TODO: Proper validation on windows/context creation - + // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); @@ -1057,7 +1055,7 @@ static int InitPlatform(void) InitEvdevInput(); // Evdev inputs initialization InitGamepad(); // Gamepad init InitKeyboard(); // Keyboard init (stdin) - + return 0; } diff --git a/src/rcore_template.c b/src/rcore_template.c index 3929de4b3..89be8ac00 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -46,8 +46,6 @@ * **********************************************************************************************/ -#include "rcore.h" - // TODO: Include the platform specific libraries //---------------------------------------------------------------------------------- @@ -130,8 +128,8 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN CORE.Window.eventWaiting = false; - - + + // TODO: Platform specific init window //-------------------------------------------------------------- CORE.Window.screen.width = width; @@ -144,7 +142,7 @@ void InitWindow(int width, int height, const char *title) CORE.Window.ready = InitGraphicsDevice(width, height); - + // Initialize OpenGL context (states and resources) // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); @@ -711,13 +709,13 @@ static int InitPlatform(void) rlLoadExtensions(eglGetProcAddress); CORE.Window.ready = true; - + // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } // Initialize hi-res timer InitTimer(); - + // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); diff --git a/src/rcore_web.c b/src/rcore_web.c index af2b6e36f..9e4ce5b97 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -45,8 +45,6 @@ * **********************************************************************************************/ -#include "rcore.h" - #define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) // #define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?) #include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management @@ -177,7 +175,7 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; // Initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- InitPlatform(); //-------------------------------------------------------------- @@ -256,7 +254,7 @@ void CloseWindow(void) rlglClose(); // De-init rlgl // De-initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- ClosePlatform(); //-------------------------------------------------------------- @@ -1005,7 +1003,7 @@ static int InitPlatform(void) glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); glfwMakeContextCurrent(platform.handle); - + // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(glfwGetProcAddress); @@ -1029,19 +1027,19 @@ static int InitPlatform(void) TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); - + CORE.Window.ready = true; // TODO: Proper validation on windows/context creation - + // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); // Initialize hi-res timer InitTimer(); - + // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); - + // Setup callback functions for the DOM events emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); @@ -1070,7 +1068,7 @@ static int InitPlatform(void) // Support gamepad events (not provided by GLFW3 on emscripten) emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); - + return 0; } From 18bedbd0952c27b0eb8bc5df0df4acf589cef181 Mon Sep 17 00:00:00 2001 From: MichaelFiber <42419558+michaelfiber@users.noreply.github.com> Date: Sat, 14 Oct 2023 16:51:35 -0400 Subject: [PATCH 0367/1350] [core] Change axisCount to be an array (#3421) * Update `PLATFORM_DRM` implementation of `GetGamepadAxisCount` * Update * Update `PLATFORM_DRM` implementation of `GetGamepadName` * Add example to test gamepad info functions Fix typo * Update new gamepad info example * Move axis count update out of GamepadThread - race condition * Remove pointless if statement * Start integrating stuff from the mikesinput lib * Add more logging * Add semicolon * Add forgotten static * More fixes * Update axisCount to be array * More debugging * Add forgotten index to ready check * Add path logging * Missing parenthesis * Add missing slash * Fix axis count being reset to 0 * Fix missing paren * Test polling joystick button events * Major updates * Fix missing array index * Fix another missing array index * Update example * dumb logging * Wrong constant for ev.code handling * More dumb logging * Remove some logging * Add FPS to gamepad info example and try for max FPS * tweak * Revert example * Add fps back * Clean up after merge * Switch axisCount to be an array --- examples/core/core_input_gamepad_info.c | 20 +++++++++++++++++--- src/rcore.c | 2 +- src/rcore.h | 2 +- src/rcore_android.c | 2 +- src/rcore_desktop.c | 4 ++-- src/rcore_drm.c | 4 ++-- src/rcore_template.c | 2 +- src/rcore_web.c | 4 ++-- 8 files changed, 27 insertions(+), 13 deletions(-) diff --git a/examples/core/core_input_gamepad_info.c b/examples/core/core_input_gamepad_info.c index 84a687cd8..55f0354b5 100644 --- a/examples/core/core_input_gamepad_info.c +++ b/examples/core/core_input_gamepad_info.c @@ -41,15 +41,29 @@ int main(void) ClearBackground(RAYWHITE); - for (int i = 0; i < 4; i++) // by default rcore.h has a MAX_GAMEPADS of 4 so mimmic that here. + for (int i = 0; i < 4; i++) // by default rcore.h has a MAX_GAMEPADS of 4 so mimmic that here. { if (IsGamepadAvailable(i)) { - DrawText(TextFormat("Gamepad:\n\tName: %s\n\tAxes: %d", GetGamepadName(i), GetGamepadAxisCount(i)), 10, y, 20, BLACK); - y += 40; + DrawText(TextFormat("Gamepad name: %s", GetGamepadName(i)), 10, y, 20, BLACK); + y += 30; + DrawText(TextFormat("\tAxis count: %d", GetGamepadAxisCount(i)), 10, y, 20, BLACK); + y += 30; + for (int axis = 0; axis < GetGamepadAxisCount(i); axis++) + { + DrawText(TextFormat("\tAxis %d = %f", axis, GetGamepadAxisMovement(i, axis)), 10, y, 20, BLACK); + y += 30; + } + for (int button = 0; button < 32; button++) + { + DrawText(TextFormat("\tButton %d = %d", button, IsGamepadButtonDown(i, button)), 10, y, 20, BLACK); + y += 30; + } } } + DrawFPS(GetScreenWidth() - 100, 100); + EndDrawing(); } diff --git a/src/rcore.c b/src/rcore.c index e6015b33f..f94731f90 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2168,7 +2168,7 @@ int GetGamepadButtonPressed(void) // Get gamepad axis count int GetGamepadAxisCount(int gamepad) { - return CORE.Input.Gamepad.axisCount; + return CORE.Input.Gamepad.axisCount[gamepad]; } // Get axis movement vector for a gamepad diff --git a/src/rcore.h b/src/rcore.h index dbff6ab13..1127585a0 100644 --- a/src/rcore.h +++ b/src/rcore.h @@ -179,7 +179,7 @@ typedef struct CoreData { } Touch; struct { int lastButtonPressed; // Register last gamepad button pressed - int axisCount; // Register number of available gamepad axis + int axisCount[MAX_GAMEPADS]; // Register number of available gamepad axis bool ready[MAX_GAMEPADS]; // Flag to know if gamepad is ready char name[MAX_GAMEPADS][64]; // Gamepad name holder char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state diff --git a/src/rcore_android.c b/src/rcore_android.c index 4dc0e26c4..98ce64a6b 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -566,7 +566,7 @@ void PollInputEvents(void) // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Input.Gamepad.axisCount = 0; + //CORE.Input.Gamepad.axisCount = 0; // Register previous touch states for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index fe5a02aac..4039bbd84 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -1234,7 +1234,7 @@ void PollInputEvents(void) // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Input.Gamepad.axisCount = 0; + //CORE.Input.Gamepad.axisCount = 0; // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback) // Register previous keys states @@ -1341,7 +1341,7 @@ void PollInputEvents(void) CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_LEFT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_LEFT_TRIGGER] > 0.1f); CORE.Input.Gamepad.currentButtonState[i][GAMEPAD_BUTTON_RIGHT_TRIGGER_2] = (char)(CORE.Input.Gamepad.axisState[i][GAMEPAD_AXIS_RIGHT_TRIGGER] > 0.1f); - CORE.Input.Gamepad.axisCount = GLFW_GAMEPAD_AXIS_LAST + 1; + CORE.Input.Gamepad.axisCount[i] = GLFW_GAMEPAD_AXIS_LAST + 1; } } diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 3f03bfa12..1fb83c8fe 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -655,7 +655,7 @@ void PollInputEvents(void) // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Input.Gamepad.axisCount = 0; + //CORE.Input.Gamepad.axisCount = 0; // Register previous keys states for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) @@ -1847,7 +1847,7 @@ static void InitGamepad(void) } ioctl(platform.gamepadStreamFd[i], JSIOCGNAME(64), &CORE.Input.Gamepad.name[i]); - ioctl(platform.gamepadStreamFd[i], JSIOCGAXES, &CORE.Input.Gamepad.axisCount); + ioctl(platform.gamepadStreamFd[i], JSIOCGAXES, &CORE.Input.Gamepad.axisCount[i]); } } } diff --git a/src/rcore_template.c b/src/rcore_template.c index 89be8ac00..4d2db9d30 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -569,7 +569,7 @@ void PollInputEvents(void) // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Input.Gamepad.axisCount = 0; + //CORE.Input.Gamepad.axisCount = 0; // Register previous touch states for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; diff --git a/src/rcore_web.c b/src/rcore_web.c index 9e4ce5b97..f8e1e5b2d 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -705,7 +705,7 @@ void PollInputEvents(void) // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Input.Gamepad.axisCount = 0; + //CORE.Input.Gamepad.axisCount = 0; // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback) // Register previous keys states @@ -799,7 +799,7 @@ void PollInputEvents(void) CORE.Input.Gamepad.axisState[i][j] = gamepadState.axis[j]; } - CORE.Input.Gamepad.axisCount = gamepadState.numAxes; + CORE.Input.Gamepad.axisCount[i] = gamepadState.numAxes; } } } From a75251f0a9732f42cfeb8d018210a358f46c910d Mon Sep 17 00:00:00 2001 From: Johnathan Corkery Date: Sun, 15 Oct 2023 18:25:39 -0400 Subject: [PATCH 0368/1350] Inclusion of Matte to BINDINGS.md (#3427) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index e08f8f79e..400bc3846 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -41,6 +41,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | KaylibKit | **4.5**| [Kotlin/native](https://kotlinlang.org) | Zlib | https://codeberg.org/Kenta/KaylibKit | | raylib-lua | **4.5** | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | | raylua | 4.0 | [Lua](http://www.lua.org/) | MIT | https://github.com/Rabios/raylua | +| raylib-matte | 4.6-dev | [Matte](https://github.com/jcorks/matte/) | MIT | https://github.com/jcorks/raylib-matte | | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | | Raylib.nelua | **4.5** | [nelua](https://nelua.io/) | Zlib | https://github.com/Its-Kenta/Raylib-Nelua | | NimraylibNow! | 4.2 | [Nim](https://nim-lang.org/) | MIT | https://github.com/greenfork/nimraylib_now | From 84818c96f236cc4ff172dda2013aba231f68ff13 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 16 Oct 2023 00:51:44 +0200 Subject: [PATCH 0369/1350] ADDED: NEW PLATFORM: SDL (DESKTOP) `rcore_desktop_sdl` #3313 --- src/rcore.c | 22 +- src/rcore_desktop_sdl.c | 755 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 769 insertions(+), 8 deletions(-) create mode 100644 src/rcore_desktop_sdl.c diff --git a/src/rcore.c b/src/rcore.c index f94731f90..b813b0197 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3,14 +3,18 @@ * rcore - Window/display management, Graphic device/context management and input management * * PLATFORMS SUPPORTED: -* - PLATFORM_DESKTOP: Windows (Win32, Win64) -* - PLATFORM_DESKTOP: Linux (X11 desktop mode) -* - PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) -* - PLATFORM_DESKTOP: OSX/macOS -* - PLATFORM_WEB: HTML5 (WebAssembly) -* - PLATFORM_DRM: Raspberry Pi 0-5 -* - PLATFORM_DRM: Linux native mode (KMS driver) -* - PLATFORM_ANDROID: Android (ARM, ARM64) +* - PLATFORM_DESKTOP: +* > Windows (Win32, Win64) +* > Linux (X11/Wayland desktop mode) +* > macOS/OSX (x64, arm64) +* > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +* - PLATFORM_WEB: +* > HTML5 (WebAssembly) +* - PLATFORM_DRM: +* > Raspberry Pi 0-5 +* > Linux native mode (KMS driver) +* - PLATFORM_ANDROID: +* > Android (ARM, ARM64) * * CONFIGURATION: * #define SUPPORT_DEFAULT_FONT (default) @@ -303,6 +307,8 @@ const char *TextFormat(const char *text, ...); // Formatting of text with // Include platform-specific submodules #if defined(PLATFORM_DESKTOP) #include "rcore_desktop.c" +#elif defined(PLATFORM_DESKTOP_SDL) + #include "rcore_desktop_sdl.c" #elif defined(PLATFORM_WEB) #include "rcore_web.c" #elif defined(PLATFORM_DRM) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c new file mode 100644 index 000000000..e85d18965 --- /dev/null +++ b/src/rcore_desktop_sdl.c @@ -0,0 +1,755 @@ +/********************************************************************************************** +* +* rcore_desktop_sdl - Functions to manage window, graphics device and inputs +* +* PLATFORM: DESKTOP: SDL +* - Windows (Win32, Win64) +* - Linux (X11/Wayland desktop mode) +* - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +* - OSX/macOS (x64, arm64) +* +* LIMITATIONS: +* - Limitation 01 +* - Limitation 02 +* +* POSSIBLE IMPROVEMENTS: +* - Improvement 01 +* - Improvement 02 +* +* ADDITIONAL NOTES: +* - TRACELOG() function is located in raylib [utils] module +* +* CONFIGURATION: +* #define RCORE_PLATFORM_CUSTOM_FLAG +* Custom flag for rcore on target platform -not used- +* +* DEPENDENCIES: +* - SDL 2 (main library) +* - Dependency 02 +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) and contributors +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#include "rcore.h" + +#include "SDL.h" // SDL base library (window/rendered, input, timming... functionality) +#include "SDL_opengl.h" // SDL OpenGL functionality (if required, instead of internal renderer) + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef struct { + SDL_Window *window; + SDL_GLContext glContext; + + SDL_Joystick *gamepad; +} PlatformData; + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +extern CoreData CORE; // Global CORE state context + +static PlatformData platform = { 0 }; // Platform specific data + +//---------------------------------------------------------------------------------- +// Module Internal Functions Declaration +//---------------------------------------------------------------------------------- +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static void ClosePlatform(void); // Close platform + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +// NOTE: Functions declaration is provided by raylib.h + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Window and Graphics Device +//---------------------------------------------------------------------------------- + +// Initialize window and OpenGL context +// NOTE: data parameter could be used to pass any kind of required data to the initialization +void InitWindow(int width, int height, const char *title) +{ + TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); + + TRACELOG(LOG_INFO, "Supported raylib modules:"); + TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); + TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); +#if defined(SUPPORT_MODULE_RSHAPES) + TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXTURES) + TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXT) + TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RMODELS) + TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RAUDIO) + TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); +#endif + + // Initialize window data + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + CORE.Window.eventWaiting = false; + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; + + // Initialize global input state + memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 + CORE.Input.Keyboard.exitKey = KEY_ESCAPE; + CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; + CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; + CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; + + // Initialize platform + //-------------------------------------------------------------- + InitPlatform(); + //-------------------------------------------------------------- + + // Initialize rlgl default data (buffers and shaders) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + // Setup default viewport + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + // Load default font + // WARNING: External function: Module required: rtext + LoadFontDefault(); + #if defined(SUPPORT_MODULE_RSHAPES) + // Set font white rectangle for shapes drawing, so shapes and text can be batched together + // WARNING: rshapes module is required, if not available, default internal white rectangle is used + Rectangle rec = GetFontDefault().recs[95]; + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); + } + else + { + // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); + } + #endif +#else + #if defined(SUPPORT_MODULE_RSHAPES) + // Set default texture and rectangle to be used for shapes drawing + // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 + Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes + #endif +#endif +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // Set default font texture filter for HighDPI (blurry) + // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); + } +#endif + + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); + +#if defined(SUPPORT_EVENTS_AUTOMATION) + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + CORE.Time.frameCounter = 0; +#endif + + TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Application initialized successfully"); +} + +// Close window and unload OpenGL context +void CloseWindow(void) +{ +#if defined(SUPPORT_GIF_RECORDING) + if (gifRecording) + { + MsfGifResult result = msf_gif_end(&gifState); + msf_gif_free(result); + gifRecording = false; + } +#endif + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + UnloadFontDefault(); // WARNING: Module required: rtext +#endif + + rlglClose(); // De-init rlgl + + // De-initialize platform + //-------------------------------------------------------------- + ClosePlatform(); + //-------------------------------------------------------------- + +#if defined(SUPPORT_EVENTS_AUTOMATION) + RL_FREE(events); +#endif + + CORE.Window.ready = false; + TRACELOG(LOG_INFO, "Window closed successfully"); +} + +// Check if application should close +bool WindowShouldClose(void) +{ + if (CORE.Window.ready) return CORE.Window.shouldClose; + else return true; +} + +// Toggle fullscreen mode +void ToggleFullscreen(void) +{ + //SDL_SetWindowFullscreen +} + +// Toggle borderless windowed mode +void ToggleBorderlessWindowed(void) +{ + //SDL_SetWindowFullscreen +} + +// Set window state: maximized, if resizable +void MaximizeWindow(void) +{ + SDL_MaximizeWindow(platform.window); + CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; +} + +// Set window state: minimized +void MinimizeWindow(void) +{ + SDL_MinimizeWindow(platform.window); + CORE.Window.flags |= FLAG_WINDOW_MINIMIZED; +} + +// Set window state: not minimized/maximized +void RestoreWindow(void) +{ + SDL_ShowWindow(platform.window); +} + +// Set window configuration state using flags +void SetWindowState(unsigned int flags) +{ + //SDL_HideWindow(platform.window); +} + +// Clear window configuration state flags +void ClearWindowState(unsigned int flags) +{ + TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform"); +} + +// Set icon for window +void SetWindowIcon(Image image) +{ + TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform"); +} + +// Set icon for window +void SetWindowIcons(Image *images, int count) +{ + TRACELOG(LOG_WARNING, "SetWindowIcons() not available on target platform"); +} + +// Set title for window +void SetWindowTitle(const char *title) +{ + CORE.Window.title = title; +} + +// Set window position on screen (windowed mode) +void SetWindowPosition(int x, int y) +{ + TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform"); +} + +// Set monitor for the current window +void SetWindowMonitor(int monitor) +{ + TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform"); +} + +// Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMinSize(int width, int height) +{ + CORE.Window.screenMin.width = width; + CORE.Window.screenMin.height = height; +} + +// Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) +void SetWindowMaxSize(int width, int height) +{ + CORE.Window.screenMax.width = width; + CORE.Window.screenMax.height = height; +} + +// Set window dimensions +void SetWindowSize(int width, int height) +{ + TRACELOG(LOG_WARNING, "SetWindowSize() not available on target platform"); +} + +// Set window opacity, value opacity is between 0.0 and 1.0 +void SetWindowOpacity(float opacity) +{ + TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform"); +} + +// Set window focused +void SetWindowFocused(void) +{ + TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform"); +} + +// Get native window handle +void *GetWindowHandle(void) +{ + TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform"); + return NULL; +} + +// Get number of monitors +int GetMonitorCount(void) +{ + TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform"); + return 1; +} + +// Get number of monitors +int GetCurrentMonitor(void) +{ + TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform"); + return 0; +} + +// Get selected monitor position +Vector2 GetMonitorPosition(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform"); + return (Vector2){ 0, 0 }; +} + +// Get selected monitor width (currently used by monitor) +int GetMonitorWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on target platform"); + return 0; +} + +// Get selected monitor height (currently used by monitor) +int GetMonitorHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on target platform"); + return 0; +} + +// Get selected monitor physical width in millimetres +int GetMonitorPhysicalWidth(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform"); + return 0; +} + +// Get selected monitor physical height in millimetres +int GetMonitorPhysicalHeight(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform"); + return 0; +} + +// Get selected monitor refresh rate +int GetMonitorRefreshRate(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform"); + return 0; +} + +// Get the human-readable, UTF-8 encoded name of the selected monitor +const char *GetMonitorName(int monitor) +{ + TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on target platform"); + return ""; +} + +// Get window position XY on monitor +Vector2 GetWindowPosition(void) +{ + TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on target platform"); + return (Vector2){ 0, 0 }; +} + +// Get window scale DPI factor for current monitor +Vector2 GetWindowScaleDPI(void) +{ + TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform"); + return (Vector2){ 1.0f, 1.0f }; +} + +// Set clipboard text content +void SetClipboardText(const char *text) +{ + TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on target platform"); +} + +// Get clipboard text content +// NOTE: returned string is allocated and freed by GLFW +const char *GetClipboardText(void) +{ + TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform"); + return NULL; +} + +// Show mouse cursor +void ShowCursor(void) +{ + CORE.Input.Mouse.cursorHidden = false; +} + +// Hides mouse cursor +void HideCursor(void) +{ + CORE.Input.Mouse.cursorHidden = true; +} + +// Enables cursor (unlock cursor) +void EnableCursor(void) +{ + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + + CORE.Input.Mouse.cursorHidden = false; +} + +// Disables cursor (lock cursor) +void DisableCursor(void) +{ + // Set cursor position in the middle + SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + + CORE.Input.Mouse.cursorHidden = true; +} + +// Swap back buffer with front buffer (screen drawing) +void SwapScreenBuffer(void) +{ + SDL_GL_SwapWindow(platform.window); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Misc +//---------------------------------------------------------------------------------- + +// Get elapsed time measure in seconds +double GetTime(void) +{ + unsigned int ms = SDL_GetTicks(); // Elapsed time in milliseconds since SDL_Init() + double time = (double)ms/1000; + return time; +} + +// Open URL with default system browser (if available) +void OpenURL(const char *url) +{ + SDL_OpenURL(url); +} + +//---------------------------------------------------------------------------------- +// Module Functions Definition: Inputs +//---------------------------------------------------------------------------------- + +// Set internal gamepad mappings +int SetGamepadMappings(const char *mappings) +{ + TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform"); + return 0; +} + +// Set mouse position XY +void SetMousePosition(int x, int y) +{ + CORE.Input.Mouse.currentPosition = (Vector2){ (float)x, (float)y }; + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; +} + +// Set mouse cursor +void SetMouseCursor(int cursor) +{ + TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); +} + +// Register all input events +void PollInputEvents(void) +{ +#if defined(SUPPORT_GESTURES_SYSTEM) + // NOTE: Gestures update must be called every frame to reset gestures correctly + // because ProcessGestureEvent() is just called on an event, not every frame + UpdateGestures(); +#endif + + // Reset keys/chars pressed registered + CORE.Input.Keyboard.keyPressedQueueCount = 0; + CORE.Input.Keyboard.charPressedQueueCount = 0; + + // Reset key repeats + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + + // Reset last gamepad button/axis registered state + CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; + for (int i = 0; i < MAX_GAMEPADS; i++) CORE.Input.Gamepad.axisCount[i] = 0; + + // Register previous touch states + for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.previousTouchState[i] = CORE.Input.Touch.currentTouchState[i]; + + // Reset touch positions + // TODO: It resets on target platform the mouse position and not filled again until a move-event, + // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! + //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; + + // Register previous keys states + // NOTE: Android supports up to 260 keys + for (int i = 0; i < 260; i++) + { + CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } + + // Poll input events for current plaform + //----------------------------------------------------------------------------- + /* + // WARNING: Indexes into this array are obtained by using SDL_Scancode values, not SDL_Keycode values + const Uint8 *keys = SDL_GetKeyboardState(NULL); + for (int i = 0; i < 256; ++i) + { + CORE.Input.Keyboard.currentKeyState[i] = keys[i]; + //if (keys[i]) TRACELOG(LOG_WARNING, "Pressed key: %i", i); + } + */ + + SDL_Event event = { 0 }; + while (SDL_PollEvent(&event) != 0) + { + // All input events can be processed after polling + switch (event.type) + { + case SDL_QUIT: CORE.Window.shouldClose = true; break; + + // Window events are also polled (Minimized, maximized, close...) + case SDL_WINDOWEVENT: + { + switch (event.window.event) + { + case SDL_WINDOWEVENT_LEAVE: + case SDL_WINDOWEVENT_HIDDEN: + case SDL_WINDOWEVENT_MINIMIZED: + case SDL_WINDOWEVENT_FOCUS_LOST: + case SDL_WINDOWEVENT_ENTER: + case SDL_WINDOWEVENT_SHOWN: + case SDL_WINDOWEVENT_FOCUS_GAINED: + case SDL_WINDOWEVENT_MAXIMIZED: + case SDL_WINDOWEVENT_RESTORED: + default: break; + } + } break; + + // Keyboard events + case SDL_KEYDOWN: + { + CORE.Input.Keyboard.currentKeyState[event.key.keysym.sym] = 1; + + if (event.key.keysym.sym == SDLK_ESCAPE) + { + CORE.Window.shouldClose = true; + } + } break; + case SDL_KEYUP: break; + + // Check mouse events + case SDL_MOUSEBUTTONDOWN: + { + CORE.Input.Mouse.currentButtonState[event.button.button - 1] = 1; + } break; + case SDL_MOUSEBUTTONUP: break; + case SDL_MOUSEWHEEL: + { + CORE.Input.Mouse.currentWheelMove.x = (float)event.wheel.x; + CORE.Input.Mouse.currentWheelMove.y = (float)event.wheel.y; + } break; + case SDL_MOUSEMOTION: + { + CORE.Input.Mouse.currentPosition.x = (float)event.motion.x; + CORE.Input.Mouse.currentPosition.y = (float)event.motion.y; + } break; + + // Check gamepad events + case SDL_JOYAXISMOTION: + { + // Motion on gamepad 0 + if (event.jaxis.which == 0) + { + // X axis motion + if (event.jaxis.axis == 0) + { + //... + } + // Y axis motion + else if (event.jaxis.axis == 1) + { + //... + } + } + } break; + default: break; + } + } + //----------------------------------------------------------------------------- +} + + +//---------------------------------------------------------------------------------- +// Module Internal Functions Definition +//---------------------------------------------------------------------------------- + +// Initialize platform: graphics, inputs and more +static int InitPlatform(void) +{ + // Initialize SDL internal global state + int result = SDL_Init(SDL_INIT_EVERYTHING); + if (result < 0) { TRACELOG(LOG_WARNING, "SDL: Failed to initialize SDL"); return -1; } + + unsigned int flags = 0; + flags |= SDL_WINDOW_SHOWN; + flags |= SDL_WINDOW_OPENGL; + flags |= SDL_WINDOW_INPUT_FOCUS; + flags |= SDL_WINDOW_MOUSE_FOCUS; + flags |= SDL_WINDOW_MOUSE_CAPTURE; // Window has mouse captured + + // Check window creation flags + if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) + { + CORE.Window.fullscreen = true; + flags |= SDL_WINDOW_FULLSCREEN; + } + + //if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) == 0) flags |= SDL_WINDOW_HIDDEN; + if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) flags |= SDL_WINDOW_BORDERLESS; + if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) flags |= SDL_WINDOW_RESIZABLE; + if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) flags |= SDL_WINDOW_MINIMIZED; + if ((CORE.Window.flags & FLAG_WINDOW_MAXIMIZED) > 0) flags |= SDL_WINDOW_MAXIMIZED; + + if ((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) + { + flags &= ~SDL_WINDOW_INPUT_FOCUS; + flags &= ~SDL_WINDOW_MOUSE_FOCUS; + } + + if ((CORE.Window.flags & FLAG_WINDOW_TOPMOST) > 0) flags |= SDL_WINDOW_ALWAYS_ON_TOP; + if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) flags &= ~SDL_WINDOW_MOUSE_CAPTURE; + + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) flags |= SDL_WINDOW_ALLOW_HIGHDPI; + + //if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) flags |= SDL_WINDOW_TRANSPARENT; // Alternative: SDL_GL_ALPHA_SIZE = 8 + + //if ((CORE.Window.flags & FLAG_FULLSCREEN_DESKTOP) > 0) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; + + // NOTE: Some OpenGL context attributes must be set before window creation + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); + //SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG | SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); + } + + // Init window + platform.window = SDL_CreateWindow(CORE.Window.title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, CORE.Window.screen.width, CORE.Window.screen.height, flags); + + // Init OpenGL context + platform.glContext = SDL_GL_CreateContext(platform.window); + + // Check window and glContext have been initialized succesfully + if ((platform.window != NULL) && (platform.glContext != NULL)) + { + CORE.Window.ready = true; + + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + } + else { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(SDL_GL_GetProcAddress); + + + // Init input gamepad + if (SDL_NumJoysticks() >= 1) + { + SDL_Joystick *gamepad = SDL_JoystickOpen(0); + //if (SDL_Joystick *gamepad == NULL) SDL_Log("WARNING: Unable to open game controller! SDL Error: %s\n", SDL_GetError()); + } + + // Initialize hi-res timer + //InitTimer(); + CORE.Time.previous = GetTime(); // Get time as double + + // Initialize base path for storage + CORE.Storage.basePath = GetWorkingDirectory(); + + return 0; +} + +static void ClosePlatform(void) +{ + SDL_GL_DeleteContext(platform.glContext); // Deinitialize OpenGL context + SDL_DestroyWindow(platform.window); + SDL_Quit(); // Deinitialize SDL internal global state +} +// EOF From 73363f829b709dddedf21a5e6b696f37e44bce19 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Mon, 16 Oct 2023 04:43:20 -0300 Subject: [PATCH 0370/1350] [core] Fix some mouse issues on `SDL` (#3428) * Fix mouse wheel getting stucked scrolling up or down * Fix mouse movement on 3D * Fix mouse button presses --- src/rcore_desktop_sdl.c | 57 +++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 22 deletions(-) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index e85d18965..4ce09cf80 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -60,7 +60,7 @@ typedef struct { SDL_Window *window; SDL_GLContext glContext; - + SDL_Joystick *gamepad; } PlatformData; @@ -134,12 +134,12 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - + // Initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- InitPlatform(); //-------------------------------------------------------------- - + // Initialize rlgl default data (buffers and shaders) // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); @@ -214,7 +214,7 @@ void CloseWindow(void) rlglClose(); // De-init rlgl // De-initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- ClosePlatform(); //-------------------------------------------------------------- @@ -481,7 +481,7 @@ void SwapScreenBuffer(void) double GetTime(void) { unsigned int ms = SDL_GetTicks(); // Elapsed time in milliseconds since SDL_Init() - double time = (double)ms/1000; + double time = (double)ms/1000; return time; } @@ -531,6 +531,13 @@ void PollInputEvents(void) // Reset key repeats for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + // Reset mouse wheel + CORE.Input.Mouse.currentWheelMove.x = 0; + CORE.Input.Mouse.currentWheelMove.y = 0; + + // Register previous mouse position + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; for (int i = 0; i < MAX_GAMEPADS; i++) CORE.Input.Gamepad.axisCount[i] = 0; @@ -551,6 +558,9 @@ void PollInputEvents(void) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; } + // Register previous mouse states + for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; + // Poll input events for current plaform //----------------------------------------------------------------------------- /* @@ -602,12 +612,15 @@ void PollInputEvents(void) case SDL_KEYUP: break; // Check mouse events - case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONDOWN: { CORE.Input.Mouse.currentButtonState[event.button.button - 1] = 1; } break; - case SDL_MOUSEBUTTONUP: break; - case SDL_MOUSEWHEEL: + case SDL_MOUSEBUTTONUP: + { + CORE.Input.Mouse.currentButtonState[event.button.button - 1] = 0; + } break; + case SDL_MOUSEWHEEL: { CORE.Input.Mouse.currentWheelMove.x = (float)event.wheel.x; CORE.Input.Mouse.currentWheelMove.y = (float)event.wheel.y; @@ -653,7 +666,7 @@ static int InitPlatform(void) // Initialize SDL internal global state int result = SDL_Init(SDL_INIT_EVERYTHING); if (result < 0) { TRACELOG(LOG_WARNING, "SDL: Failed to initialize SDL"); return -1; } - + unsigned int flags = 0; flags |= SDL_WINDOW_SHOWN; flags |= SDL_WINDOW_OPENGL; @@ -662,12 +675,12 @@ static int InitPlatform(void) flags |= SDL_WINDOW_MOUSE_CAPTURE; // Window has mouse captured // Check window creation flags - if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) + if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) { CORE.Window.fullscreen = true; flags |= SDL_WINDOW_FULLSCREEN; } - + //if ((CORE.Window.flags & FLAG_WINDOW_HIDDEN) == 0) flags |= SDL_WINDOW_HIDDEN; if ((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) flags |= SDL_WINDOW_BORDERLESS; if ((CORE.Window.flags & FLAG_WINDOW_RESIZABLE) > 0) flags |= SDL_WINDOW_RESIZABLE; @@ -684,11 +697,11 @@ static int InitPlatform(void) if ((CORE.Window.flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) > 0) flags &= ~SDL_WINDOW_MOUSE_CAPTURE; if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) flags |= SDL_WINDOW_ALLOW_HIGHDPI; - + //if ((CORE.Window.flags & FLAG_WINDOW_TRANSPARENT) > 0) flags |= SDL_WINDOW_TRANSPARENT; // Alternative: SDL_GL_ALPHA_SIZE = 8 - + //if ((CORE.Window.flags & FLAG_FULLSCREEN_DESKTOP) > 0) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; - + // NOTE: Some OpenGL context attributes must be set before window creation SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); @@ -702,12 +715,12 @@ static int InitPlatform(void) // Init window platform.window = SDL_CreateWindow(CORE.Window.title, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, CORE.Window.screen.width, CORE.Window.screen.height, flags); - + // Init OpenGL context platform.glContext = SDL_GL_CreateContext(platform.window); - + // Check window and glContext have been initialized succesfully - if ((platform.window != NULL) && (platform.glContext != NULL)) + if ((platform.window != NULL) && (platform.glContext != NULL)) { CORE.Window.ready = true; @@ -728,21 +741,21 @@ static int InitPlatform(void) // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(SDL_GL_GetProcAddress); - + // Init input gamepad if (SDL_NumJoysticks() >= 1) { SDL_Joystick *gamepad = SDL_JoystickOpen(0); //if (SDL_Joystick *gamepad == NULL) SDL_Log("WARNING: Unable to open game controller! SDL Error: %s\n", SDL_GetError()); } - + // Initialize hi-res timer //InitTimer(); CORE.Time.previous = GetTime(); // Get time as double - + // Initialize base path for storage CORE.Storage.basePath = GetWorkingDirectory(); - + return 0; } From c4296b166af5f1859707234dddae73d4dce84628 Mon Sep 17 00:00:00 2001 From: neyrox Date: Mon, 16 Oct 2023 15:06:12 +0300 Subject: [PATCH 0371/1350] Fix GenMeshPlane when resX != resZ (#3425) Co-authored-by: Stanislav Yablonskiy --- src/rmodels.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index a11ecf799..f9018eaaf 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2249,7 +2249,7 @@ Mesh GenMeshPlane(float width, float length, int resX, int resZ) for (int face = 0; face < numFaces; face++) { // Retrieve lower left corner from face ind - int i = face % (resX - 1) + (face/(resZ - 1)*resX); + int i = face + face / (resX - 1); triangles[t++] = i + resX; triangles[t++] = i + 1; From 859c67792a839021b98b7abf25a664b7109cff3f Mon Sep 17 00:00:00 2001 From: Peter0x44 Date: Mon, 16 Oct 2023 13:08:55 +0100 Subject: [PATCH 0372/1350] Make sure rcore.o gets compiled in more situations (#3423) Currently doing the following: ``` make touch rcore_desktop.c make ``` Will not result in rcore.o getting compiled again, despite that rcore_desktop.c has changed This commit resolves that --- src/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Makefile b/src/Makefile index e75ae5368..56b6d0632 100644 --- a/src/Makefile +++ b/src/Makefile @@ -632,6 +632,9 @@ endif # Compile all modules with their prerequisites +# Prerequisites of core module +rcore.o : rcore_android.c rcore_desktop.c rcore_drm.c rcore_template.c rcore_web.c + # Compile core module rcore.o : rcore.c raylib.h rlgl.h utils.h raymath.h rcamera.h rgestures.h $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) From fab99b8309b483f81c8a7b2524da0e0e0079560f Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Mon, 16 Oct 2023 09:59:08 -0300 Subject: [PATCH 0373/1350] Remove rcore.h include from android (#3429) --- src/rcore_android.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/rcore_android.c b/src/rcore_android.c index 98ce64a6b..cfc8edf61 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -46,8 +46,6 @@ * **********************************************************************************************/ -#include "rcore.h" - #include // Required for: android_app struct and activity management #include // Required for: AWINDOW_FLAG_FULLSCREEN definition and others //#include // Required for: Android sensors functions (accelerometer, gyroscope, light...) @@ -188,7 +186,7 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; // Initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- InitPlatform(); //-------------------------------------------------------------- } @@ -216,7 +214,7 @@ void CloseWindow(void) #endif // De-initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- ClosePlatform(); //-------------------------------------------------------------- @@ -869,7 +867,7 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) // Initialize graphics device (display device and OpenGL context) InitGraphicsDevice(); - + // Initialize OpenGL context (states and resources) // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); @@ -908,7 +906,7 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes #endif #endif - + // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); From af83764f4fe83d9f036fab94a6ab886ffdf82587 Mon Sep 17 00:00:00 2001 From: Dor Shapira <107134807+sDos280@users.noreply.github.com> Date: Tue, 17 Oct 2023 10:53:53 +0300 Subject: [PATCH 0374/1350] Implement GetCurrentMonitor in rcore_desktop_sdl (#3431) * Implemented GetCurrentMonitor * remove traceloog in GetCurrentMonitor --- src/rcore_desktop_sdl.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index 4ce09cf80..7f593abcb 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -356,8 +356,7 @@ int GetMonitorCount(void) // Get number of monitors int GetCurrentMonitor(void) { - TRACELOG(LOG_WARNING, "GetCurrentMonitor() not implemented on target platform"); - return 0; + return SDL_GetWindowDisplayIndex(platform.window); } // Get selected monitor position From 7290ea9bfbccd62ab5e4870ca84868c2aecf5382 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 17 Oct 2023 10:59:25 +0200 Subject: [PATCH 0375/1350] Update models_mesh_generation.c --- examples/models/models_mesh_generation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/models/models_mesh_generation.c b/examples/models/models_mesh_generation.c index d17a20a12..94e0a4c48 100644 --- a/examples/models/models_mesh_generation.c +++ b/examples/models/models_mesh_generation.c @@ -36,7 +36,7 @@ int main(void) Model models[NUM_MODELS] = { 0 }; - models[0] = LoadModelFromMesh(GenMeshPlane(2, 2, 5, 5)); + models[0] = LoadModelFromMesh(GenMeshPlane(2, 2, 4, 3)); models[1] = LoadModelFromMesh(GenMeshCube(2.0f, 1.0f, 2.0f)); models[2] = LoadModelFromMesh(GenMeshSphere(2, 32, 32)); models[3] = LoadModelFromMesh(GenMeshHemiSphere(2, 16, 16)); From 99ede0f747f6d8a0b0f7d0d9371b9d46af16cb80 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 17 Oct 2023 11:09:56 +0200 Subject: [PATCH 0376/1350] Added some notes for alternative implementations #3362 --- src/rtext.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/rtext.c b/src/rtext.c index 2d72bbe6b..5b43bfb92 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1344,10 +1344,12 @@ Rectangle GetGlyphAtlasRec(Font font, int codepoint) // Get text length in bytes, check for \0 character unsigned int TextLength(const char *text) { - unsigned int length = 0; //strlen(text) + unsigned int length = 0; if (text != NULL) { + // NOTE: Alternative: use strlen(text) + while (*text++) length++; } @@ -1415,6 +1417,8 @@ int TextCopy(char *dst, const char *src) if ((src != NULL) && (dst != NULL)) { + // NOTE: Alternative: use strcpy(dst, src) + while (*src != '\0') { *dst = *src; @@ -1459,6 +1463,8 @@ const char *TextSubtext(const char *text, int position, int length) } if (length >= textLength) length = textLength; + + // NOTE: Alternative: memcpy(buffer, text + position, length) for (int c = 0 ; c < length ; c++) { From f353cd1c3a884d6b80af311d40586ad414e09efd Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Tue, 17 Oct 2023 07:01:01 -0300 Subject: [PATCH 0377/1350] [core] Add some missing implementations to `SDL` (#3432) * Add missing implementations * Add missing implementations 2 * Add missing implementations 3 * Add missing implementations 4 * Add missing implementations 5 --- src/rcore_desktop_sdl.c | 87 ++++++++++++++++++++++++++++++++++------- 1 file changed, 73 insertions(+), 14 deletions(-) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index 7f593abcb..0910343db 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -292,13 +292,18 @@ void SetWindowIcons(Image *images, int count) // Set title for window void SetWindowTitle(const char *title) { + SDL_SetWindowTitle(platform.window, title); + CORE.Window.title = title; } // Set window position on screen (windowed mode) void SetWindowPosition(int x, int y) { - TRACELOG(LOG_WARNING, "SetWindowPosition() not available on target platform"); + SDL_SetWindowPosition(platform.window, x, y); + + CORE.Window.position.x = x; + CORE.Window.position.y = y; } // Set monitor for the current window @@ -324,13 +329,19 @@ void SetWindowMaxSize(int width, int height) // Set window dimensions void SetWindowSize(int width, int height) { - TRACELOG(LOG_WARNING, "SetWindowSize() not available on target platform"); + SDL_SetWindowSize(platform.window, width, height); + + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; } // Set window opacity, value opacity is between 0.0 and 1.0 void SetWindowOpacity(float opacity) { - TRACELOG(LOG_WARNING, "SetWindowOpacity() not available on target platform"); + if (opacity >= 1.0f) opacity = 1.0f; + else if (opacity <= 0.0f) opacity = 0.0f; + + SDL_SetWindowOpacity(platform.window, opacity); } // Set window focused @@ -349,8 +360,11 @@ void *GetWindowHandle(void) // Get number of monitors int GetMonitorCount(void) { - TRACELOG(LOG_WARNING, "GetMonitorCount() not implemented on target platform"); - return 1; + int monitorCount = 0; + + monitorCount = SDL_GetNumVideoDisplays(); + + return monitorCount; } // Get number of monitors @@ -369,15 +383,39 @@ Vector2 GetMonitorPosition(int monitor) // Get selected monitor width (currently used by monitor) int GetMonitorWidth(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorWidth() not implemented on target platform"); - return 0; + int width = 0; + + int monitorCount = 0; + monitorCount = SDL_GetNumVideoDisplays(); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + SDL_DisplayMode mode; + SDL_GetCurrentDisplayMode(monitor, &mode); + width = mode.w; + } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); + + return width; } // Get selected monitor height (currently used by monitor) int GetMonitorHeight(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorHeight() not implemented on target platform"); - return 0; + int height = 0; + + int monitorCount = 0; + monitorCount = SDL_GetNumVideoDisplays(); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + SDL_DisplayMode mode; + SDL_GetCurrentDisplayMode(monitor, &mode); + height = mode.h; + } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); + + return height; } // Get selected monitor physical width in millimetres @@ -397,22 +435,43 @@ int GetMonitorPhysicalHeight(int monitor) // Get selected monitor refresh rate int GetMonitorRefreshRate(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorRefreshRate() not implemented on target platform"); - return 0; + int refresh = 0; + + int monitorCount = 0; + monitorCount = SDL_GetNumVideoDisplays(); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + SDL_DisplayMode mode; + SDL_GetCurrentDisplayMode(monitor, &mode); + refresh = mode.refresh_rate; + } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); + + return refresh; } // Get the human-readable, UTF-8 encoded name of the selected monitor const char *GetMonitorName(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorName() not implemented on target platform"); + int monitorCount = 0; + monitorCount = SDL_GetNumVideoDisplays(); + + if ((monitor >= 0) && (monitor < monitorCount)) return SDL_GetDisplayName(monitor); + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); + return ""; } // Get window position XY on monitor Vector2 GetWindowPosition(void) { - TRACELOG(LOG_WARNING, "GetWindowPosition() not implemented on target platform"); - return (Vector2){ 0, 0 }; + int x = 0; + int y = 0; + + SDL_GetWindowPosition(platform.window, &x, &y); + + return (Vector2){ (float)x, (float)y }; } // Get window scale DPI factor for current monitor From 80432fde62a31ff5e7ad61f8a0352de9642cd97f Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Tue, 17 Oct 2023 23:29:28 +0200 Subject: [PATCH 0378/1350] Fix SDL keyboard issue (#3435) * Fix SDL keyboard issue We have added a mapping table between raylib keys and SDL scancodes. * Change `ScancodeToKey` array type --- src/rcore_desktop_sdl.c | 134 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 129 insertions(+), 5 deletions(-) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index 0910343db..b66167c89 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -71,11 +71,119 @@ extern CoreData CORE; // Global CORE state context static PlatformData platform = { 0 }; // Platform specific data +//---------------------------------------------------------------------------------- +// Local Variables Definition +//---------------------------------------------------------------------------------- +#define SCANCODE_MAPPED_NUM 100 +static const KeyboardKey ScancodeToKey[SCANCODE_MAPPED_NUM] = { + KEY_NULL, // SDL_SCANCODE_UNKNOWN + 0, + 0, + 0, + KEY_A, // SDL_SCANCODE_A + KEY_B, // SDL_SCANCODE_B + KEY_C, // SDL_SCANCODE_C + KEY_D, // SDL_SCANCODE_D + KEY_E, // SDL_SCANCODE_E + KEY_F, // SDL_SCANCODE_F + KEY_G, // SDL_SCANCODE_G + KEY_H, // SDL_SCANCODE_H + KEY_I, // SDL_SCANCODE_I + KEY_J, // SDL_SCANCODE_J + KEY_K, // SDL_SCANCODE_K + KEY_L, // SDL_SCANCODE_L + KEY_M, // SDL_SCANCODE_M + KEY_N, // SDL_SCANCODE_N + KEY_O, // SDL_SCANCODE_O + KEY_P, // SDL_SCANCODE_P + KEY_Q, // SDL_SCANCODE_Q + KEY_R, // SDL_SCANCODE_R + KEY_S, // SDL_SCANCODE_S + KEY_T, // SDL_SCANCODE_T + KEY_U, // SDL_SCANCODE_U + KEY_V, // SDL_SCANCODE_V + KEY_W, // SDL_SCANCODE_W + KEY_X, // SDL_SCANCODE_X + KEY_Y, // SDL_SCANCODE_Y + KEY_Z, // SDL_SCANCODE_Z + KEY_ONE, // SDL_SCANCODE_1 + KEY_TWO, // SDL_SCANCODE_2 + KEY_THREE, // SDL_SCANCODE_3 + KEY_FOUR, // SDL_SCANCODE_4 + KEY_FIVE, // SDL_SCANCODE_5 + KEY_SIX, // SDL_SCANCODE_6 + KEY_SEVEN, // SDL_SCANCODE_7 + KEY_EIGHT, // SDL_SCANCODE_8 + KEY_NINE, // SDL_SCANCODE_9 + KEY_ZERO, // SDL_SCANCODE_0 + KEY_ENTER, // SDL_SCANCODE_RETURN + KEY_ESCAPE, // SDL_SCANCODE_ESCAPE + KEY_BACKSPACE, // SDL_SCANCODE_BACKSPACE + KEY_TAB, // SDL_SCANCODE_TAB + KEY_SPACE, // SDL_SCANCODE_SPACE + KEY_MINUS, // SDL_SCANCODE_MINUS + KEY_EQUAL, // SDL_SCANCODE_EQUALS + KEY_LEFT_BRACKET, // SDL_SCANCODE_LEFTBRACKET + KEY_RIGHT_BRACKET, // SDL_SCANCODE_RIGHTBRACKET + KEY_BACKSLASH, // SDL_SCANCODE_BACKSLASH + 0, // SDL_SCANCODE_NONUSHASH + KEY_SEMICOLON, // SDL_SCANCODE_SEMICOLON + KEY_APOSTROPHE, // SDL_SCANCODE_APOSTROPHE + KEY_GRAVE, // SDL_SCANCODE_GRAVE + KEY_COMMA, // SDL_SCANCODE_COMMA + KEY_PERIOD, // SDL_SCANCODE_PERIOD + KEY_SLASH, // SDL_SCANCODE_SLASH + KEY_CAPS_LOCK, // SDL_SCANCODE_CAPSLOCK + KEY_F1, // SDL_SCANCODE_F1 + KEY_F2, // SDL_SCANCODE_F2 + KEY_F3, // SDL_SCANCODE_F3 + KEY_F4, // SDL_SCANCODE_F4 + KEY_F5, // SDL_SCANCODE_F5 + KEY_F6, // SDL_SCANCODE_F6 + KEY_F7, // SDL_SCANCODE_F7 + KEY_F8, // SDL_SCANCODE_F8 + KEY_F9, // SDL_SCANCODE_F9 + KEY_F10, // SDL_SCANCODE_F10 + KEY_F11, // SDL_SCANCODE_F11 + KEY_F12, // SDL_SCANCODE_F12 + KEY_PRINT_SCREEN, // SDL_SCANCODE_PRINTSCREEN + KEY_SCROLL_LOCK, // SDL_SCANCODE_SCROLLLOCK + KEY_PAUSE, // SDL_SCANCODE_PAUSE + KEY_INSERT, // SDL_SCANCODE_INSERT + KEY_HOME, // SDL_SCANCODE_HOME + KEY_PAGE_UP, // SDL_SCANCODE_PAGEUP + KEY_DELETE, // SDL_SCANCODE_DELETE + KEY_END, // SDL_SCANCODE_END + KEY_PAGE_DOWN, // SDL_SCANCODE_PAGEDOWN + KEY_RIGHT, // SDL_SCANCODE_RIGHT + KEY_LEFT, // SDL_SCANCODE_LEFT + KEY_DOWN, // SDL_SCANCODE_DOWN + KEY_UP, // SDL_SCANCODE_UP + KEY_NUM_LOCK, // SDL_SCANCODE_NUMLOCKCLEAR + KEY_KP_DIVIDE, // SDL_SCANCODE_KP_DIVIDE + KEY_KP_MULTIPLY, // SDL_SCANCODE_KP_MULTIPLY + KEY_KP_SUBTRACT, // SDL_SCANCODE_KP_MINUS + KEY_KP_ADD, // SDL_SCANCODE_KP_PLUS + KEY_KP_ENTER, // SDL_SCANCODE_KP_ENTER + KEY_KP_1, // SDL_SCANCODE_KP_1 + KEY_KP_2, // SDL_SCANCODE_KP_2 + KEY_KP_3, // SDL_SCANCODE_KP_3 + KEY_KP_4, // SDL_SCANCODE_KP_4 + KEY_KP_5, // SDL_SCANCODE_KP_5 + KEY_KP_6, // SDL_SCANCODE_KP_6 + KEY_KP_7, // SDL_SCANCODE_KP_7 + KEY_KP_8, // SDL_SCANCODE_KP_8 + KEY_KP_9, // SDL_SCANCODE_KP_9 + KEY_KP_0, // SDL_SCANCODE_KP_0 + KEY_KP_DECIMAL // SDL_SCANCODE_KP_PERIOD +}; + //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static void ClosePlatform(void); // Close platform +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static void ClosePlatform(void); // Close platform +static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode); // Help convert SDL scancodes to raylib key //---------------------------------------------------------------------------------- // Module Functions Declaration @@ -660,14 +768,21 @@ void PollInputEvents(void) // Keyboard events case SDL_KEYDOWN: { - CORE.Input.Keyboard.currentKeyState[event.key.keysym.sym] = 1; + KeyboardKey key = ConvertScancodeToKey(event.key.keysym.scancode); + if (key != KEY_NULL) CORE.Input.Keyboard.currentKeyState[key] = 1; - if (event.key.keysym.sym == SDLK_ESCAPE) + // TODO: Put exitKey verification outside the switch? + if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey]) { CORE.Window.shouldClose = true; } } break; - case SDL_KEYUP: break; + + case SDL_KEYUP: + { + KeyboardKey key = ConvertScancodeToKey(event.key.keysym.scancode); + if (key != KEY_NULL) CORE.Input.Keyboard.currentKeyState[key] = 0; + } break; // Check mouse events case SDL_MOUSEBUTTONDOWN: @@ -823,4 +938,13 @@ static void ClosePlatform(void) SDL_DestroyWindow(platform.window); SDL_Quit(); // Deinitialize SDL internal global state } + +static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode) +{ + if (sdlScancode >= 0 && sdlScancode < SCANCODE_MAPPED_NUM) + { + return ScancodeToKey[sdlScancode]; + } + return KEY_NULL; // No equivalent key in Raylib +} // EOF From d7d04a07a28a80569f440459f2a1ca4ea2fe2497 Mon Sep 17 00:00:00 2001 From: Alexey Kutepov Date: Wed, 18 Oct 2023 04:35:38 +0700 Subject: [PATCH 0379/1350] [raudio] Implement GetMasterVolume() (#3434) It feels a little unfinished when you can SetMasterVolume but can't really Get it. So to finish the symmetry here is the GetMasterVolume implementation. --- src/raudio.c | 8 ++++++++ src/raylib.h | 1 + 2 files changed, 9 insertions(+) diff --git a/src/raudio.c b/src/raudio.c index a8d1b40e2..6a1096767 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -536,6 +536,14 @@ void SetMasterVolume(float volume) ma_device_set_master_volume(&AUDIO.System.device, volume); } +// Get master volume (listener) +float GetMasterVolume(void) +{ + float volume = 0.0f; + ma_device_get_master_volume(&AUDIO.System.device, &volume); + return volume; +} + //---------------------------------------------------------------------------------- // Module Functions Definition - Audio Buffer management //---------------------------------------------------------------------------------- diff --git a/src/raylib.h b/src/raylib.h index c03e0a576..331bf5253 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1538,6 +1538,7 @@ RLAPI void InitAudioDevice(void); // Initial RLAPI void CloseAudioDevice(void); // Close the audio device and context RLAPI bool IsAudioDeviceReady(void); // Check if audio device has been initialized successfully RLAPI void SetMasterVolume(float volume); // Set master volume (listener) +RLAPI float GetMasterVolume(void); // Get master volume (listener) // Wave/Sound loading/unloading functions RLAPI Wave LoadWave(const char *fileName); // Load wave data from file From 9534f48425bb87e205406fe950b606a2186ccaac Mon Sep 17 00:00:00 2001 From: Michael Scherbakow Date: Tue, 17 Oct 2023 23:36:42 +0200 Subject: [PATCH 0380/1350] fix build.zig (#3433) for zig master (2023-10-17) --- src/build.zig | 83 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 55 insertions(+), 28 deletions(-) diff --git a/src/build.zig b/src/build.zig index 5d7ddbf3f..5e2b5916e 100644 --- a/src/build.zig +++ b/src/build.zig @@ -20,37 +20,55 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built raylib.addIncludePath(.{ .path = srcdir ++ "/external/glfw/include" }); } - raylib.addCSourceFiles(&.{ - srcdir ++ "/rcore.c", - srcdir ++ "/utils.c", - }, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{ + srcdir ++ "/rcore.c", + srcdir ++ "/utils.c", + }, + .flags = raylib_flags, + }); if (options.raudio) { - raylib.addCSourceFiles(&.{ - srcdir ++ "/raudio.c", - }, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{ + srcdir ++ "/raudio.c", + }, + .flags = raylib_flags, + }); } if (options.rmodels) { - raylib.addCSourceFiles(&.{ - srcdir ++ "/rmodels.c", - }, &[_][]const u8{ - "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 - } ++ raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{ + srcdir ++ "/rmodels.c", + }, + .flags = &[_][]const u8{ + "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 + } ++ raylib_flags, + }); } if (options.rshapes) { - raylib.addCSourceFiles(&.{ - srcdir ++ "/rshapes.c", - }, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{ + srcdir ++ "/rshapes.c", + }, + .flags = raylib_flags, + }); } if (options.rtext) { - raylib.addCSourceFiles(&.{ - srcdir ++ "/rtext.c", - }, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{ + srcdir ++ "/rtext.c", + }, + .flags = raylib_flags, + }); } if (options.rtextures) { - raylib.addCSourceFiles(&.{ - srcdir ++ "/rtextures.c", - }, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{ + srcdir ++ "/rtextures.c", + }, + .flags = raylib_flags, + }); } var gen_step = b.addWriteFiles(); @@ -65,7 +83,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built switch (target.getOsTag()) { .windows => { - raylib.addCSourceFiles(&.{srcdir ++ "/rglfw.c"}, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{srcdir ++ "/rglfw.c"}, + .flags = raylib_flags, + }); raylib.linkSystemLibrary("winmm"); raylib.linkSystemLibrary("gdi32"); raylib.linkSystemLibrary("opengl32"); @@ -75,7 +96,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built }, .linux => { if (!options.platform_drm) { - raylib.addCSourceFiles(&.{srcdir ++ "/rglfw.c"}, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{srcdir ++ "/rglfw.c"}, + .flags = raylib_flags, + }); raylib.linkSystemLibrary("GL"); raylib.linkSystemLibrary("rt"); raylib.linkSystemLibrary("dl"); @@ -103,7 +127,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built } }, .freebsd, .openbsd, .netbsd, .dragonfly => { - raylib.addCSourceFiles(&.{srcdir ++ "/rglfw.c"}, raylib_flags); + raylib.addCSourceFiles(.{ + .files = &.{srcdir ++ "/rglfw.c"}, + .flags = raylib_flags, + }); raylib.linkSystemLibrary("GL"); raylib.linkSystemLibrary("rt"); raylib.linkSystemLibrary("dl"); @@ -122,10 +149,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built const raylib_flags_extra_macos = &[_][]const u8{ "-ObjC", }; - raylib.addCSourceFiles( - &.{srcdir ++ "/rglfw.c"}, - raylib_flags ++ raylib_flags_extra_macos, - ); + raylib.addCSourceFiles(.{ + .files = &.{srcdir ++ "/rglfw.c"}, + .flags = raylib_flags ++ raylib_flags_extra_macos, + }); raylib.linkFramework("Foundation"); raylib.linkFramework("CoreServices"); raylib.linkFramework("CoreGraphics"); From 53cd60bb29c97951cd0379aa230d43f794f055ce Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 18 Oct 2023 00:03:47 +0200 Subject: [PATCH 0381/1350] REVIEWED: Move `InitWindow()`/`CloseWindow()` to `rcore.c` #3313 --- src/rcore.c | 143 ++++++++++++++++++++++++++++++++++ src/rcore_android.c | 90 --------------------- src/rcore_desktop.c | 161 +++----------------------------------- src/rcore_desktop_sdl.c | 140 --------------------------------- src/rcore_drm.c | 144 ---------------------------------- src/rcore_template.c | 169 +--------------------------------------- src/rcore_web.c | 141 --------------------------------- 7 files changed, 153 insertions(+), 835 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index b813b0197..c6770a5bb 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -281,6 +281,9 @@ extern void LoadFontDefault(void); // [Module: text] Loads default font on extern void UnloadFontDefault(void); // [Module: text] Unloads default font from GPU memory #endif +static int InitPlatform(void); // Initialize platform (graphics, inputs and more) +static void ClosePlatform(void); // Close platform + static void InitTimer(void); // Initialize timer (hi-resolution if available) static void SetupFramebuffer(int width, int height); // Setup main framebuffer static void SetupViewport(int width, int height); // Set viewport for a provided width and height @@ -369,6 +372,146 @@ const char *TextFormat(const char *text, ...); // Formatting of text with //void EnableCursor(void) //void DisableCursor(void) +// Initialize window and OpenGL context +// NOTE: data parameter could be used to pass any kind of required data to the initialization +void InitWindow(int width, int height, const char *title) +{ + TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); + + TRACELOG(LOG_INFO, "Supported raylib modules:"); + TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); + TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); +#if defined(SUPPORT_MODULE_RSHAPES) + TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXTURES) + TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RTEXT) + TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RMODELS) + TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); +#endif +#if defined(SUPPORT_MODULE_RAUDIO) + TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); +#else + TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); +#endif + + // Initialize window data + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + CORE.Window.eventWaiting = false; + CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default + if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; + + // Initialize global input state + memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 + CORE.Input.Keyboard.exitKey = KEY_ESCAPE; + CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; + CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; + CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; + + // Initialize platform + //-------------------------------------------------------------- + InitPlatform(); + //-------------------------------------------------------------- + + // Initialize rlgl default data (buffers and shaders) + // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl + rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + + // Setup default viewport + SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + // Load default font + // WARNING: External function: Module required: rtext + LoadFontDefault(); + #if defined(SUPPORT_MODULE_RSHAPES) + // Set font white rectangle for shapes drawing, so shapes and text can be batched together + // WARNING: rshapes module is required, if not available, default internal white rectangle is used + Rectangle rec = GetFontDefault().recs[95]; + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) + { + // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); + } + else + { + // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding + SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); + } + #endif +#else + #if defined(SUPPORT_MODULE_RSHAPES) + // Set default texture and rectangle to be used for shapes drawing + // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 + Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes + #endif +#endif +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // Set default font texture filter for HighDPI (blurry) + // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); + rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); + } +#endif + +#if defined(SUPPORT_EVENTS_AUTOMATION) + events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + CORE.Time.frameCounter = 0; +#endif + + // Initialize random seed + SetRandomSeed((unsigned int)time(NULL)); + + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP: Application initialized successfully"); +} + +// Close window and unload OpenGL context +void CloseWindow(void) +{ +#if defined(SUPPORT_GIF_RECORDING) + if (gifRecording) + { + MsfGifResult result = msf_gif_end(&gifState); + msf_gif_free(result); + gifRecording = false; + } +#endif + +#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) + UnloadFontDefault(); // WARNING: Module required: rtext +#endif + + rlglClose(); // De-init rlgl + + // De-initialize platform + //-------------------------------------------------------------- + ClosePlatform(); + //-------------------------------------------------------------- + +#if defined(SUPPORT_EVENTS_AUTOMATION) + RL_FREE(events); +#endif + + CORE.Window.ready = false; + TRACELOG(LOG_INFO, "Window closed successfully"); +} + // Check if window has been initialized successfully bool IsWindowReady(void) { diff --git a/src/rcore_android.c b/src/rcore_android.c index cfc8edf61..bf55ece27 100644 --- a/src/rcore_android.c +++ b/src/rcore_android.c @@ -136,96 +136,6 @@ struct android_app *GetAndroidApp(void) // Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- -// Initialize window and OpenGL context -// NOTE: data parameter could be used to pass any kind of required data to the initialization -void InitWindow(int width, int height, const char *title) -{ - TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); - - TRACELOG(LOG_INFO, "Supported raylib modules:"); - TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); - TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); -#if defined(SUPPORT_MODULE_RSHAPES) - TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXTURES) - TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXT) - TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RMODELS) - TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RAUDIO) - TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); -#endif - - // Initialize window data - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - CORE.Window.eventWaiting = false; - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; - - // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 - CORE.Input.Keyboard.exitKey = KEY_ESCAPE; - CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; - CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - - // Initialize platform - //-------------------------------------------------------------- - InitPlatform(); - //-------------------------------------------------------------- -} - -// Close window and unload OpenGL context -void CloseWindow(void) -{ -#if defined(SUPPORT_GIF_RECORDING) - if (gifRecording) - { - MsfGifResult result = msf_gif_end(&gifState); - msf_gif_free(result); - gifRecording = false; - } -#endif - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - UnloadFontDefault(); // WARNING: Module required: rtext -#endif - - rlglClose(); // De-init rlgl - -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - timeEndPeriod(1); // Restore time period -#endif - - // De-initialize platform - //-------------------------------------------------------------- - ClosePlatform(); - //-------------------------------------------------------------- - -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - - CORE.Window.ready = false; - TRACELOG(LOG_INFO, "Window closed successfully"); -} - // Check if application should close bool WindowShouldClose(void) { diff --git a/src/rcore_desktop.c b/src/rcore_desktop.c index 4039bbd84..29969d4ca 100644 --- a/src/rcore_desktop.c +++ b/src/rcore_desktop.c @@ -142,162 +142,11 @@ static void JoystickCallback(int jid, int event); // Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- -// Initialize window and OpenGL context -// NOTE: data parameter could be used to pass any kind of required data to the initialization -void InitWindow(int width, int height, const char *title) -{ - TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); - - TRACELOG(LOG_INFO, "Supported raylib modules:"); - TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); - TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); -#if defined(SUPPORT_MODULE_RSHAPES) - TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXTURES) - TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXT) - TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RMODELS) - TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RAUDIO) - TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); -#endif - - // Initialize window data - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - CORE.Window.eventWaiting = false; - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; - - // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 - CORE.Input.Keyboard.exitKey = KEY_ESCAPE; - CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; - CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - - // Initialize platform - //-------------------------------------------------------------- - InitPlatform(); - //-------------------------------------------------------------- - - // Initialize rlgl default data (buffers and shaders) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Setup default viewport - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - // Load default font - // WARNING: External function: Module required: rtext - LoadFontDefault(); - #if defined(SUPPORT_MODULE_RSHAPES) - // Set font white rectangle for shapes drawing, so shapes and text can be batched together - // WARNING: rshapes module is required, if not available, default internal white rectangle is used - Rectangle rec = GetFontDefault().recs[95]; - if (CORE.Window.flags & FLAG_MSAA_4X_HINT) - { - // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); - } - else - { - // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); - } - #endif -#else - #if defined(SUPPORT_MODULE_RSHAPES) - // Set default texture and rectangle to be used for shapes drawing - // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 - Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes - #endif -#endif -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // Set default font texture filter for HighDPI (blurry) - // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); - } -#endif - -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; -#endif - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); - - TRACELOG(LOG_INFO, "PLATFORM: DESKTOP: Application initialized successfully"); -} - -// Close window and unload OpenGL context -void CloseWindow(void) -{ -#if defined(SUPPORT_GIF_RECORDING) - if (gifRecording) - { - MsfGifResult result = msf_gif_end(&gifState); - msf_gif_free(result); - gifRecording = false; - } -#endif - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - UnloadFontDefault(); // WARNING: Module required: rtext -#endif - - rlglClose(); // De-init rlgl - - // De-initialize platform - //-------------------------------------------------------------- - ClosePlatform(); - //-------------------------------------------------------------- - -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - - CORE.Window.ready = false; - TRACELOG(LOG_INFO, "Window closed successfully"); -} - // Check if application should close // NOTE: By default, if KEY_ESCAPE pressed or window close icon clicked bool WindowShouldClose(void) { - if (CORE.Window.ready) - { - // While window minimized, stop loop execution - while (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)) glfwWaitEvents(); - - CORE.Window.shouldClose = glfwWindowShouldClose(platform.handle); - - // Reset close status for next frame - glfwSetWindowShouldClose(platform.handle, GLFW_FALSE); - - return CORE.Window.shouldClose; - } + if (CORE.Window.ready) return CORE.Window.shouldClose; else return true; } @@ -1349,6 +1198,14 @@ void PollInputEvents(void) if (CORE.Window.eventWaiting) glfwWaitEvents(); // Wait for in input events before continue (drawing is paused) else glfwPollEvents(); // Poll input events: keyboard/mouse/window events (callbacks) + + // While window minimized, stop loop execution + while (IsWindowState(FLAG_WINDOW_MINIMIZED) && !IsWindowState(FLAG_WINDOW_ALWAYS_RUN)) glfwWaitEvents(); + + CORE.Window.shouldClose = glfwWindowShouldClose(platform.handle); + + // Reset close status for next frame + glfwSetWindowShouldClose(platform.handle, GLFW_FALSE); } diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index b66167c89..15c79cb87 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -194,146 +194,6 @@ static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode); // Help conv // Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- -// Initialize window and OpenGL context -// NOTE: data parameter could be used to pass any kind of required data to the initialization -void InitWindow(int width, int height, const char *title) -{ - TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); - - TRACELOG(LOG_INFO, "Supported raylib modules:"); - TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); - TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); -#if defined(SUPPORT_MODULE_RSHAPES) - TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXTURES) - TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXT) - TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RMODELS) - TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RAUDIO) - TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); -#endif - - // Initialize window data - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - CORE.Window.eventWaiting = false; - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; - - // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 - CORE.Input.Keyboard.exitKey = KEY_ESCAPE; - CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; - CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - - // Initialize platform - //-------------------------------------------------------------- - InitPlatform(); - //-------------------------------------------------------------- - - // Initialize rlgl default data (buffers and shaders) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Setup default viewport - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - // Load default font - // WARNING: External function: Module required: rtext - LoadFontDefault(); - #if defined(SUPPORT_MODULE_RSHAPES) - // Set font white rectangle for shapes drawing, so shapes and text can be batched together - // WARNING: rshapes module is required, if not available, default internal white rectangle is used - Rectangle rec = GetFontDefault().recs[95]; - if (CORE.Window.flags & FLAG_MSAA_4X_HINT) - { - // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); - } - else - { - // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); - } - #endif -#else - #if defined(SUPPORT_MODULE_RSHAPES) - // Set default texture and rectangle to be used for shapes drawing - // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 - Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes - #endif -#endif -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // Set default font texture filter for HighDPI (blurry) - // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); - } -#endif - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); - -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; -#endif - - TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Application initialized successfully"); -} - -// Close window and unload OpenGL context -void CloseWindow(void) -{ -#if defined(SUPPORT_GIF_RECORDING) - if (gifRecording) - { - MsfGifResult result = msf_gif_end(&gifState); - msf_gif_free(result); - gifRecording = false; - } -#endif - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - UnloadFontDefault(); // WARNING: Module required: rtext -#endif - - rlglClose(); // De-init rlgl - - // De-initialize platform - //-------------------------------------------------------------- - ClosePlatform(); - //-------------------------------------------------------------- - -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - - CORE.Window.ready = false; - TRACELOG(LOG_INFO, "Window closed successfully"); -} - // Check if application should close bool WindowShouldClose(void) { diff --git a/src/rcore_drm.c b/src/rcore_drm.c index 1fb83c8fe..3302ec362 100644 --- a/src/rcore_drm.c +++ b/src/rcore_drm.c @@ -168,150 +168,6 @@ static int FindNearestConnectorMode(const drmModeConnector *connector, uint widt // Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- -// Initialize window and OpenGL context -// NOTE: data parameter could be used to pass any kind of required data to the initialization -void InitWindow(int width, int height, const char *title) -{ - TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); - - TRACELOG(LOG_INFO, "Supported raylib modules:"); - TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); - TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); -#if defined(SUPPORT_MODULE_RSHAPES) - TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXTURES) - TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXT) - TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RMODELS) - TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RAUDIO) - TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); -#endif - - // Initialize window data - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - CORE.Window.eventWaiting = false; - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; - - // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 - CORE.Input.Keyboard.exitKey = KEY_ESCAPE; - CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; - CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - - // Initialize platform - //-------------------------------------------------------------- - InitPlatform(); - //-------------------------------------------------------------- - - // Initialize rlgl default data (buffers and shaders) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Setup default viewport - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - // Load default font - // WARNING: External function: Module required: rtext - LoadFontDefault(); -#if defined(SUPPORT_MODULE_RSHAPES) - // Set font white rectangle for shapes drawing, so shapes and text can be batched together - // WARNING: rshapes module is required, if not available, default internal white rectangle is used - Rectangle rec = GetFontDefault().recs[95]; - if (CORE.Window.flags & FLAG_MSAA_4X_HINT) - { - // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering - SetShapesTexture(GetFontDefault().texture, (Rectangle){rec.x + 2, rec.y + 2, 1, 1}); - } - else - { - // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding - SetShapesTexture(GetFontDefault().texture, (Rectangle){rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2}); - } -#endif -#else -#if defined(SUPPORT_MODULE_RSHAPES) - // Set default texture and rectangle to be used for shapes drawing - // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 - Texture2D texture = {rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8}; - SetShapesTexture(texture, (Rectangle){0.0f, 0.0f, 1.0f, 1.0f}); // WARNING: Module required: rshapes -#endif -#endif -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // Set default font texture filter for HighDPI (blurry) - // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); - } -#endif - -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; -#endif - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); - - TRACELOG(LOG_INFO, "PLATFORM: DRM: Application initialized successfully"); -} - -// Close window and unload OpenGL context -void CloseWindow(void) -{ -#if defined(SUPPORT_GIF_RECORDING) - if (gifRecording) - { - MsfGifResult result = msf_gif_end(&gifState); - msf_gif_free(result); - gifRecording = false; - } -#endif - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - UnloadFontDefault(); // WARNING: Module required: rtext -#endif - - rlglClose(); // De-init rlgl - -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) - timeEndPeriod(1); // Restore time period -#endif - - // De-initialize platform - //-------------------------------------------------------------- - ClosePlatform(); - //-------------------------------------------------------------- - -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - - CORE.Window.ready = false; - TRACELOG(LOG_INFO, "Window closed successfully"); -} - // Check if application should close // NOTE: By default, if KEY_ESCAPE pressed bool WindowShouldClose(void) diff --git a/src/rcore_template.c b/src/rcore_template.c index 4d2db9d30..f94cca8c1 100644 --- a/src/rcore_template.c +++ b/src/rcore_template.c @@ -83,153 +83,6 @@ static bool InitGraphicsDevice(void); // Initialize graphics device // Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- -// Initialize window and OpenGL context -// NOTE: data parameter could be used to pass any kind of required data to the initialization -void InitWindow(int width, int height, const char *title) -{ - TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); - - TRACELOG(LOG_INFO, "Supported raylib modules:"); - TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); - TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); -#if defined(SUPPORT_MODULE_RSHAPES) - TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXTURES) - TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXT) - TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RMODELS) - TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RAUDIO) - TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); -#endif - - // NOTE: Keep internal pointer to input title string (no copy) - if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; - - // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); - CORE.Input.Keyboard.exitKey = KEY_ESCAPE; - CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; - CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = 0; // GAMEPAD_BUTTON_UNKNOWN - CORE.Window.eventWaiting = false; - - - // TODO: Platform specific init window - //-------------------------------------------------------------- - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - CORE.Window.currentFbo.width = width; - CORE.Window.currentFbo.height = height; - - // Initialize graphics device - // NOTE: returns true if window and graphic device has been initialized successfully - CORE.Window.ready = InitGraphicsDevice(width, height); - - - - // Initialize OpenGL context (states and resources) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - // Load default font - // WARNING: External function: Module required: rtext - LoadFontDefault(); - #if defined(SUPPORT_MODULE_RSHAPES) - // Set font white rectangle for shapes drawing, so shapes and text can be batched together - // WARNING: rshapes module is required, if not available, default internal white rectangle is used - Rectangle rec = GetFontDefault().recs[95]; - if (CORE.Window.flags & FLAG_MSAA_4X_HINT) - { - // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 2, rec.y + 2, 1, 1 }); - } - else - { - // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding - SetShapesTexture(GetFontDefault().texture, (Rectangle){ rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2 }); - } - #endif -#else - #if defined(SUPPORT_MODULE_RSHAPES) - // Set default texture and rectangle to be used for shapes drawing - // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 - Texture2D texture = { rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - SetShapesTexture(texture, (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }); // WARNING: Module required: rshapes - #endif -#endif -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // Set default font texture filter for HighDPI (blurry) - // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); - } -#endif - -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; -#endif - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); - - TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Application initialized successfully"); -} - -// Close window and unload OpenGL context -void CloseWindow(void) -{ -#if defined(SUPPORT_GIF_RECORDING) - if (gifRecording) - { - MsfGifResult result = msf_gif_end(&gifState); - msf_gif_free(result); - gifRecording = false; - } -#endif - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - UnloadFontDefault(); // WARNING: Module required: rtext -#endif - - rlglClose(); // De-init rlgl - - // Platform specific close window - //-------------------------------------------------------------- - // TODO. - //-------------------------------------------------------------- - -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - - CORE.Window.ready = false; - TRACELOG(LOG_INFO, "Window closed successfully"); -} - // Check if application should close bool WindowShouldClose(void) { @@ -503,27 +356,7 @@ void OpenURL(const char *url) if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); else { - JNIEnv *env = NULL; - JavaVM *vm = platform.app->activity->vm; - (*vm)->AttachCurrentThread(vm, &env, NULL); - - jstring urlString = (*env)->NewStringUTF(env, url); - jclass uriClass = (*env)->FindClass(env, "android/net/Uri"); - jmethodID uriParse = (*env)->GetStaticMethodID(env, uriClass, "parse", "(Ljava/lang/String;)Landroid/net/Uri;"); - jobject uri = (*env)->CallStaticObjectMethod(env, uriClass, uriParse, urlString); - - jclass intentClass = (*env)->FindClass(env, "android/content/Intent"); - jfieldID actionViewId = (*env)->GetStaticFieldID(env, intentClass, "ACTION_VIEW", "Ljava/lang/String;"); - jobject actionView = (*env)->GetStaticObjectField(env, intentClass, actionViewId); - jmethodID newIntent = (*env)->GetMethodID(env, intentClass, "", "(Ljava/lang/String;Landroid/net/Uri;)V"); - jobject intent = (*env)->AllocObject(env, intentClass); - - (*env)->CallVoidMethod(env, intent, newIntent, actionView, uri); - jclass activityClass = (*env)->FindClass(env, "android/app/Activity"); - jmethodID startActivity = (*env)->GetMethodID(env, activityClass, "startActivity", "(Landroid/content/Intent;)V"); - (*env)->CallVoidMethod(env, platform.app->activity->clazz, startActivity, intent); - - (*vm)->DetachCurrentThread(vm); + // TODO: } } diff --git a/src/rcore_web.c b/src/rcore_web.c index f8e1e5b2d..465bb8957 100644 --- a/src/rcore_web.c +++ b/src/rcore_web.c @@ -125,147 +125,6 @@ static EM_BOOL EmscriptenGamepadCallback(int eventType, const EmscriptenGamepadE // Module Functions Definition: Window and Graphics Device //---------------------------------------------------------------------------------- -// Initialize window and OpenGL context -// NOTE: data parameter could be used to pass any kind of required data to the initialization -void InitWindow(int width, int height, const char *title) -{ - TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); - - TRACELOG(LOG_INFO, "Supported raylib modules:"); - TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); - TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); -#if defined(SUPPORT_MODULE_RSHAPES) - TRACELOG(LOG_INFO, " > rshapes:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rshapes:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXTURES) - TRACELOG(LOG_INFO, " > rtextures:. loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtextures:. not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RTEXT) - TRACELOG(LOG_INFO, " > rtext:..... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rtext:..... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RMODELS) - TRACELOG(LOG_INFO, " > rmodels:... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > rmodels:... not loaded (optional)"); -#endif -#if defined(SUPPORT_MODULE_RAUDIO) - TRACELOG(LOG_INFO, " > raudio:.... loaded (optional)"); -#else - TRACELOG(LOG_INFO, " > raudio:.... not loaded (optional)"); -#endif - - // Initialize window data - CORE.Window.screen.width = width; - CORE.Window.screen.height = height; - CORE.Window.eventWaiting = false; - CORE.Window.screenScale = MatrixIdentity(); // No draw scaling required by default - if ((title != NULL) && (title[0] != 0)) CORE.Window.title = title; - - // Initialize global input state - memset(&CORE.Input, 0, sizeof(CORE.Input)); // Reset CORE.Input structure to 0 - CORE.Input.Keyboard.exitKey = KEY_ESCAPE; - CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; - CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; - CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - - // Initialize platform - //-------------------------------------------------------------- - InitPlatform(); - //-------------------------------------------------------------- - - // Initialize OpenGL context (states and resources) - // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl - rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - - // Setup default viewport - // NOTE: It updated CORE.Window.render.width and CORE.Window.render.height - SetupViewport(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - // Load default font - // WARNING: External function: Module required: rtext - LoadFontDefault(); -#if defined(SUPPORT_MODULE_RSHAPES) - // Set font white rectangle for shapes drawing, so shapes and text can be batched together - // WARNING: rshapes module is required, if not available, default internal white rectangle is used - Rectangle rec = GetFontDefault().recs[95]; - if (CORE.Window.flags & FLAG_MSAA_4X_HINT) - { - // NOTE: We try to maxime rec padding to avoid pixel bleeding on MSAA filtering - SetShapesTexture(GetFontDefault().texture, (Rectangle){rec.x + 2, rec.y + 2, 1, 1}); - } - else - { - // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding - SetShapesTexture(GetFontDefault().texture, (Rectangle){rec.x + 1, rec.y + 1, rec.width - 2, rec.height - 2}); - } -#endif -#else -#if defined(SUPPORT_MODULE_RSHAPES) - // Set default texture and rectangle to be used for shapes drawing - // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 - Texture2D texture = {rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8}; - SetShapesTexture(texture, (Rectangle){0.0f, 0.0f, 1.0f, 1.0f}); // WARNING: Module required: rshapes -#endif -#endif -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // Set default font texture filter for HighDPI (blurry) - // RL_TEXTURE_FILTER_LINEAR - tex filter: BILINEAR, no mipmaps - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MIN_FILTER, RL_TEXTURE_FILTER_LINEAR); - rlTextureParameters(GetFontDefault().texture.id, RL_TEXTURE_MAG_FILTER, RL_TEXTURE_FILTER_LINEAR); - } -#endif - -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; -#endif - - // Initialize random seed - SetRandomSeed((unsigned int)time(NULL)); - - TRACELOG(LOG_INFO, "PLATFORM: WEB: Application initialized successfully"); -} - -// Close window and unload OpenGL context -void CloseWindow(void) -{ -#if defined(SUPPORT_GIF_RECORDING) - if (gifRecording) - { - MsfGifResult result = msf_gif_end(&gifState); - msf_gif_free(result); - gifRecording = false; - } -#endif - -#if defined(SUPPORT_MODULE_RTEXT) && defined(SUPPORT_DEFAULT_FONT) - UnloadFontDefault(); // WARNING: Module required: rtext -#endif - - rlglClose(); // De-init rlgl - - // De-initialize platform - //-------------------------------------------------------------- - ClosePlatform(); - //-------------------------------------------------------------- - -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - - CORE.Window.ready = false; - TRACELOG(LOG_INFO, "Window closed successfully"); -} - // Check if application should close bool WindowShouldClose(void) { From fc6152613f4bb97708935a9f8096d2ca4d9fbb76 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 18 Oct 2023 00:33:05 +0200 Subject: [PATCH 0382/1350] REVIEWED: `raylib 5.0-dev` version for a future release --- examples/Makefile | 2 +- examples/Makefile.Web | 2 +- src/Makefile | 4 ++-- src/raylib.dll.rc | 8 ++++---- src/raylib.dll.rc.data | Bin 11246 -> 11246 bytes src/raylib.h | 6 +++--- src/raylib.rc | 8 ++++---- src/raylib.rc.data | Bin 11182 -> 11182 bytes 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 6031f05e9..74b3b874d 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -30,7 +30,7 @@ PLATFORM ?= PLATFORM_DESKTOP # Define required raylib variables PROJECT_NAME ?= raylib_examples -RAYLIB_VERSION ?= 4.5.0 +RAYLIB_VERSION ?= 5.0.0 RAYLIB_PATH ?= .. # Locations of raylib.h and libraylib.a/libraylib.so diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 267e02396..ac8d5af89 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -30,7 +30,7 @@ PLATFORM ?= PLATFORM_WEB # Define required raylib variables PROJECT_NAME ?= raylib_examples -RAYLIB_VERSION ?= 4.5.0 +RAYLIB_VERSION ?= 5.0.0 RAYLIB_PATH ?= .. # Locations of raylib.h and libraylib.a/libraylib.so diff --git a/src/Makefile b/src/Makefile index 56b6d0632..e6c5106cc 100644 --- a/src/Makefile +++ b/src/Makefile @@ -44,8 +44,8 @@ PLATFORM ?= PLATFORM_DESKTOP # Define required raylib variables -RAYLIB_VERSION = 4.5.0 -RAYLIB_API_VERSION = 450 +RAYLIB_VERSION = 5.0.0 +RAYLIB_API_VERSION = 500 # Define raylib source code path RAYLIB_SRC_PATH ?= ../src diff --git a/src/raylib.dll.rc b/src/raylib.dll.rc index c2a42dca2..e1455af7a 100644 --- a/src/raylib.dll.rc +++ b/src/raylib.dll.rc @@ -1,8 +1,8 @@ GLFW_ICON ICON "raylib.ico" 1 VERSIONINFO -FILEVERSION 4,5,0,0 -PRODUCTVERSION 4,5,0,0 +FILEVERSION 5,0,0,0 +PRODUCTVERSION 5,0,0,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -11,12 +11,12 @@ BEGIN BEGIN //VALUE "CompanyName", "raylib technologies" VALUE "FileDescription", "raylib dynamic library (www.raylib.com)" - VALUE "FileVersion", "4.5.0" + VALUE "FileVersion", "5.0.0" VALUE "InternalName", "raylib.dll" VALUE "LegalCopyright", "(c) 2023 Ramon Santamaria (@raysan5)" VALUE "OriginalFilename", "raylib.dll" VALUE "ProductName", "raylib" - VALUE "ProductVersion", "4.5.0" + VALUE "ProductVersion", "5.0.0" END END BLOCK "VarFileInfo" diff --git a/src/raylib.dll.rc.data b/src/raylib.dll.rc.data index 7403bd020b0658ed1c8ee569850e2eff9a0b3a50..e93edcffbcb1e7363078ec6d16a9c83b79e50b3c 100644 GIT binary patch delta 51 zcmaDC{w{pO9Sv><237_LV4eI?Lwu4Z$7D9mYpkXWdJG1eA8I-?LYR{^wT}V-aI6ha delta 51 zcmaDC{w{pO9Sv?)1{MYo0Me5`YKTwL // Required for: va_list - Only used by TraceLogCallback -#define RAYLIB_VERSION_MAJOR 4 -#define RAYLIB_VERSION_MINOR 6 +#define RAYLIB_VERSION_MAJOR 5 +#define RAYLIB_VERSION_MINOR 0 #define RAYLIB_VERSION_PATCH 0 -#define RAYLIB_VERSION "4.6-dev" +#define RAYLIB_VERSION "5.0-dev" // Function specifiers in case library is build/used as a shared library (Windows) // NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll diff --git a/src/raylib.rc b/src/raylib.rc index 6a9546537..8612a9561 100644 --- a/src/raylib.rc +++ b/src/raylib.rc @@ -1,8 +1,8 @@ GLFW_ICON ICON "raylib.ico" 1 VERSIONINFO -FILEVERSION 4,5,0,0 -PRODUCTVERSION 4,5,0,0 +FILEVERSION 5,0,0,0 +PRODUCTVERSION 5,0,0,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -11,12 +11,12 @@ BEGIN BEGIN //VALUE "CompanyName", "raylib technologies" VALUE "FileDescription", "raylib application (www.raylib.com)" - VALUE "FileVersion", "4.5.0" + VALUE "FileVersion", "5.0.0" VALUE "InternalName", "raylib app" VALUE "LegalCopyright", "(c) 2023 Ramon Santamaria (@raysan5)" //VALUE "OriginalFilename", "raylib_app.exe" VALUE "ProductName", "raylib app" - VALUE "ProductVersion", "4.5.0" + VALUE "ProductVersion", "5.0.0" END END BLOCK "VarFileInfo" diff --git a/src/raylib.rc.data b/src/raylib.rc.data index 1485d3aa4d9470bc98c786e0699c49dd0c9c3899..1476a1cbaeb46b6b4ddc19647cb7fe87993a5e97 100644 GIT binary patch delta 51 zcmZ1%zAk*j9Sv><237_LV4eI?Lwu4Z$7D9mbF8KedJG1eZ)$QgLYR|ZY8?dtRw@lf delta 51 zcmZ1%zAk*j9Sv?)1{MYo0Me5`YKTwL Date: Wed, 18 Oct 2023 03:05:35 -0300 Subject: [PATCH 0383/1350] [core] Add more missing implementations to `SDL` (#3436) * Add more missing implementations 1 * Add more missing implementations 2 * Add more missing implementations 3 * Add more missing implementations 4 * Add more missing implementations 5 * Add more missing implementations 6 --- src/rcore_desktop_sdl.c | 87 ++++++++++++++++++++++++++++++++--------- 1 file changed, 69 insertions(+), 18 deletions(-) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index 15c79cb87..cddee7448 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -62,6 +62,7 @@ typedef struct { SDL_GLContext glContext; SDL_Joystick *gamepad; + SDL_Cursor *cursor; } PlatformData; //---------------------------------------------------------------------------------- @@ -178,6 +179,22 @@ static const KeyboardKey ScancodeToKey[SCANCODE_MAPPED_NUM] = { KEY_KP_DECIMAL // SDL_SCANCODE_KP_PERIOD }; +static const int CursorsLUT[] = { + SDL_SYSTEM_CURSOR_ARROW, // 0 MOUSE_CURSOR_DEFAULT + SDL_SYSTEM_CURSOR_ARROW, // 1 MOUSE_CURSOR_ARROW + SDL_SYSTEM_CURSOR_IBEAM, // 2 MOUSE_CURSOR_IBEAM + SDL_SYSTEM_CURSOR_CROSSHAIR, // 3 MOUSE_CURSOR_CROSSHAIR + SDL_SYSTEM_CURSOR_HAND, // 4 MOUSE_CURSOR_POINTING_HAND + SDL_SYSTEM_CURSOR_SIZEWE, // 5 MOUSE_CURSOR_RESIZE_EW + SDL_SYSTEM_CURSOR_SIZENS, // 6 MOUSE_CURSOR_RESIZE_NS + SDL_SYSTEM_CURSOR_SIZENWSE, // 7 MOUSE_CURSOR_RESIZE_NWSE + SDL_SYSTEM_CURSOR_SIZENESW, // 8 MOUSE_CURSOR_RESIZE_NESW + SDL_SYSTEM_CURSOR_SIZEALL, // 9 MOUSE_CURSOR_RESIZE_ALL + SDL_SYSTEM_CURSOR_NO // 10 MOUSE_CURSOR_NOT_ALLOWED + //SDL_SYSTEM_CURSOR_WAIT, // No equivalent implemented on MouseCursor enum on raylib.h + //SDL_SYSTEM_CURSOR_WAITARROW, // No equivalent implemented on MouseCursor enum on raylib.h +}; + //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- @@ -315,14 +332,13 @@ void SetWindowOpacity(float opacity) // Set window focused void SetWindowFocused(void) { - TRACELOG(LOG_WARNING, "SetWindowFocused() not available on target platform"); + SDL_RaiseWindow(platform.window); } // Get native window handle void *GetWindowHandle(void) { - TRACELOG(LOG_WARNING, "GetWindowHandle() not implemented on target platform"); - return NULL; + return (void *)platform.window; } // Get number of monitors @@ -389,15 +405,45 @@ int GetMonitorHeight(int monitor) // Get selected monitor physical width in millimetres int GetMonitorPhysicalWidth(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPhysicalWidth() not implemented on target platform"); - return 0; + int width = 0; + + int monitorCount = 0; + monitorCount = SDL_GetNumVideoDisplays(); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + float vdpi = 0.0f; + SDL_GetDisplayDPI(monitor, NULL, NULL, &vdpi); + SDL_DisplayMode mode; + SDL_GetCurrentDisplayMode(monitor, &mode); + // Calculate size on inches, then convert to millimeter + if (vdpi > 0.0f) width = (mode.w/vdpi)*25.4f; + } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); + + return width; } // Get selected monitor physical height in millimetres int GetMonitorPhysicalHeight(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPhysicalHeight() not implemented on target platform"); - return 0; + int height = 0; + + int monitorCount = 0; + monitorCount = SDL_GetNumVideoDisplays(); + + if ((monitor >= 0) && (monitor < monitorCount)) + { + float vdpi = 0.0f; + SDL_GetDisplayDPI(monitor, NULL, NULL, &vdpi); + SDL_DisplayMode mode; + SDL_GetCurrentDisplayMode(monitor, &mode); + // Calculate size on inches, then convert to millimeter + if (vdpi > 0.0f) height = (mode.h/vdpi)*25.4f; + } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); + + return height; } // Get selected monitor refresh rate @@ -452,34 +498,37 @@ Vector2 GetWindowScaleDPI(void) // Set clipboard text content void SetClipboardText(const char *text) { - TRACELOG(LOG_WARNING, "SetClipboardText() not implemented on target platform"); + SDL_SetClipboardText(text); } // Get clipboard text content -// NOTE: returned string is allocated and freed by GLFW +// NOTE: returned string must be freed with SDL_free() const char *GetClipboardText(void) { - TRACELOG(LOG_WARNING, "GetClipboardText() not implemented on target platform"); - return NULL; + return SDL_GetClipboardText(); } // Show mouse cursor void ShowCursor(void) { + SDL_ShowCursor(SDL_ENABLE); + CORE.Input.Mouse.cursorHidden = false; } // Hides mouse cursor void HideCursor(void) { + SDL_ShowCursor(SDL_DISABLE); + CORE.Input.Mouse.cursorHidden = true; } // Enables cursor (unlock cursor) void EnableCursor(void) { - // Set cursor position in the middle - SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + SDL_SetRelativeMouseMode(SDL_FALSE); + SDL_ShowCursor(SDL_ENABLE); CORE.Input.Mouse.cursorHidden = false; } @@ -487,8 +536,7 @@ void EnableCursor(void) // Disables cursor (lock cursor) void DisableCursor(void) { - // Set cursor position in the middle - SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + SDL_SetRelativeMouseMode(SDL_TRUE); CORE.Input.Mouse.cursorHidden = true; } @@ -524,8 +572,7 @@ void OpenURL(const char *url) // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { - TRACELOG(LOG_WARNING, "SetGamepadMappings() not implemented on target platform"); - return 0; + SDL_GameControllerAddMapping(mappings); } // Set mouse position XY @@ -538,7 +585,10 @@ void SetMousePosition(int x, int y) // Set mouse cursor void SetMouseCursor(int cursor) { - TRACELOG(LOG_WARNING, "SetMouseCursor() not implemented on target platform"); + platform.cursor = SDL_CreateSystemCursor(CursorsLUT[cursor]); + SDL_SetCursor(platform.cursor); + + CORE.Input.Mouse.cursor = cursor; } // Register all input events @@ -794,6 +844,7 @@ static int InitPlatform(void) static void ClosePlatform(void) { + SDL_FreeCursor(platform.cursor); // Free cursor SDL_GL_DeleteContext(platform.glContext); // Deinitialize OpenGL context SDL_DestroyWindow(platform.window); SDL_Quit(); // Deinitialize SDL internal global state From d7a098ebd31c3235711a473b0894bfa3e6ec0fc9 Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Thu, 19 Oct 2023 00:09:00 +0200 Subject: [PATCH 0384/1350] [core] Add more missing implementations to SDL (#3439) * [core] Add more missing implementations to SDL Add functions: `SetWindowState`, `ClearWindowState`, `SetWindowIcon` * Completing `SetWIndowState` and `ClearWindowState` * Add VSync support for SDL * Fix `CORE.Window.display` size issue * Fix getting monitor size We now get the size of the monitor where the window is located * Add `ToggleBorderlessWindowed` * Add `ToggleFullscreen` * Add `GetMonitorPosition` * Add `SetWindowMonitor` NOTE: The function is implemented but incomplete * Replace `TraceLog` by `TRACELOG` * Fixed mouse delta issue in relative mode Fixed a delta retrieval issue with `GetMouseDelta` when the mouse is in relative mode. Solution by @ubkp * Fix `IsKeyPressed` issue An issue caused `IsKeyPressed` to continuously return true for most keys when pressed * Fix `SetGamepadMappings` returning --- src/rcore_desktop_sdl.c | 383 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 360 insertions(+), 23 deletions(-) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index cddee7448..fedebeee4 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -63,6 +63,7 @@ typedef struct { SDL_Joystick *gamepad; SDL_Cursor *cursor; + bool cursorRelative; } PlatformData; //---------------------------------------------------------------------------------- @@ -221,13 +222,72 @@ bool WindowShouldClose(void) // Toggle fullscreen mode void ToggleFullscreen(void) { - //SDL_SetWindowFullscreen + if (!IsWindowState(FLAG_FULLSCREEN_MODE)) + { + SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN); + CORE.Window.flags |= FLAG_FULLSCREEN_MODE; + } + else + { + SDL_SetWindowFullscreen(platform.window, 0); + CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; + } } // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - //SDL_SetWindowFullscreen + // Leave fullscreen before attempting to set borderless windowed mode and get screen position from it + bool wasOnFullscreen = false; + if (CORE.Window.fullscreen) + { + CORE.Window.previousPosition = CORE.Window.position; + ToggleFullscreen(); + wasOnFullscreen = true; + } + + if (!IsWindowState(FLAG_BORDERLESS_WINDOWED_MODE)) + { + // Store the window's current position and size + SDL_GetWindowPosition(platform.window, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y); + CORE.Window.previousScreen = CORE.Window.screen; + + // Set screen position and size inside valid bounds + SDL_Rect displayBounds; + if (SDL_GetDisplayBounds(GetCurrentMonitor(), &displayBounds) == 0) + { + SDL_SetWindowPosition(platform.window, displayBounds.x, displayBounds.y); + SDL_SetWindowSize(platform.window, displayBounds.w, displayBounds.h); + } + + // Set borderless mode and flag + SDL_SetWindowBordered(platform.window, SDL_FALSE); + CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; + + // Set topmost modes and flag + SDL_SetWindowAlwaysOnTop(platform.window, SDL_TRUE); + CORE.Window.flags |= FLAG_WINDOW_TOPMOST; + + // Set borderless windowed flag + CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE; + } + else + { + // Remove borderless mode and flag + SDL_SetWindowBordered(platform.window, SDL_TRUE); + CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; + + // Remove topmost modes and flag + SDL_SetWindowAlwaysOnTop(platform.window, SDL_FALSE); + CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; + + // Restore the window's previous size and position + SDL_SetWindowSize(platform.window, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); + SDL_SetWindowPosition(platform.window, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y); + + // Remove borderless windowed flag + CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; + } } // Set window state: maximized, if resizable @@ -253,19 +313,247 @@ void RestoreWindow(void) // Set window configuration state using flags void SetWindowState(unsigned int flags) { - //SDL_HideWindow(platform.window); + CORE.Window.flags |= flags; + + if (flags & FLAG_VSYNC_HINT) + { + SDL_GL_SetSwapInterval(1); + } + if (flags & FLAG_FULLSCREEN_MODE) + { + SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN); + } + if (flags & FLAG_WINDOW_RESIZABLE) + { + SDL_SetWindowResizable(platform.window, SDL_TRUE); + } + if (flags & FLAG_WINDOW_UNDECORATED) + { + SDL_SetWindowBordered(platform.window, SDL_FALSE); + } + if (flags & FLAG_WINDOW_HIDDEN) + { + SDL_HideWindow(platform.window); + } + if (flags & FLAG_WINDOW_MINIMIZED) + { + SDL_MinimizeWindow(platform.window); + } + if (flags & FLAG_WINDOW_MAXIMIZED) + { + SDL_MaximizeWindow(platform.window); + } + if (flags & FLAG_WINDOW_UNFOCUSED) + { + // NOTE: To be able to implement this part it seems that we should + // do it ourselves, via `Windows.h`, `X11/Xlib.h` or even `Cocoa.h` + TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_SDL"); + } + if (flags & FLAG_WINDOW_TOPMOST) + { + SDL_SetWindowAlwaysOnTop(platform.window, SDL_FALSE); + } + if (flags & FLAG_WINDOW_ALWAYS_RUN) + { + TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_ALWAYS_RUN is not supported on PLATFORM_DESKTOP_SDL"); + } + if (flags & FLAG_WINDOW_TRANSPARENT) + { + TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_TRANSPARENT is not supported on PLATFORM_DESKTOP_SDL"); + } + if (flags & FLAG_WINDOW_HIGHDPI) + { + // NOTE: Such a function does not seem to exist + TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_SDL"); + } + if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) + { + //SDL_SetWindowGrab(platform.window, SDL_FALSE); + TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_WINDOW_MOUSE_PASSTHROUGH is not supported on PLATFORM_DESKTOP_SDL"); + } + if (flags & FLAG_BORDERLESS_WINDOWED_MODE) + { + // NOTE: Same as FLAG_WINDOW_UNDECORATED with SDL ? + SDL_SetWindowBordered(platform.window, SDL_FALSE); + } + if (flags & FLAG_MSAA_4X_HINT) + { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); // Enable multisampling buffers + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); // Enable multisampling + } + if (flags & FLAG_INTERLACED_HINT) + { + TRACELOG(LOG_WARNING, "SetWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_SDL"); + } } // Clear window configuration state flags void ClearWindowState(unsigned int flags) { - TRACELOG(LOG_WARNING, "ClearWindowState() not available on target platform"); + CORE.Window.flags &= ~flags; + + if (flags & FLAG_VSYNC_HINT) + { + SDL_GL_SetSwapInterval(0); + } + if (flags & FLAG_FULLSCREEN_MODE) + { + SDL_SetWindowFullscreen(platform.window, 0); + } + if (flags & FLAG_WINDOW_RESIZABLE) + { + SDL_SetWindowResizable(platform.window, SDL_FALSE); + } + if (flags & FLAG_WINDOW_UNDECORATED) + { + SDL_SetWindowBordered(platform.window, SDL_TRUE); + } + if (flags & FLAG_WINDOW_HIDDEN) + { + SDL_ShowWindow(platform.window); + } + if (flags & FLAG_WINDOW_MINIMIZED) + { + SDL_RestoreWindow(platform.window); + } + if (flags & FLAG_WINDOW_MAXIMIZED) + { + SDL_RestoreWindow(platform.window); + } + if (flags & FLAG_WINDOW_UNFOCUSED) + { + //SDL_RaiseWindow(platform.window); + TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_UNFOCUSED is not supported on PLATFORM_DESKTOP_SDL"); + } + if (flags & FLAG_WINDOW_TOPMOST) + { + SDL_SetWindowAlwaysOnTop(platform.window, SDL_FALSE); + } + if (flags & FLAG_WINDOW_ALWAYS_RUN) + { + TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_ALWAYS_RUN is not supported on PLATFORM_DESKTOP_SDL"); + } + if (flags & FLAG_WINDOW_TRANSPARENT) + { + TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_TRANSPARENT is not supported on PLATFORM_DESKTOP_SDL"); + } + if (flags & FLAG_WINDOW_HIGHDPI) + { + // NOTE: There also doesn't seem to be a feature to disable high DPI once enabled + TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_HIGHDPI is not supported on PLATFORM_DESKTOP_SDL"); + } + if (flags & FLAG_WINDOW_MOUSE_PASSTHROUGH) + { + //SDL_SetWindowGrab(platform.window, SDL_TRUE); + TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_WINDOW_MOUSE_PASSTHROUGH is not supported on PLATFORM_DESKTOP_SDL"); + } + if (flags & FLAG_BORDERLESS_WINDOWED_MODE) + { + // NOTE: Same as FLAG_WINDOW_UNDECORATED with SDL ? + SDL_SetWindowBordered(platform.window, SDL_TRUE); + } + if (flags & FLAG_MSAA_4X_HINT) + { + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 0); // Disable multisampling buffers + SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, 0); // Disable multisampling + } + if (flags & FLAG_INTERLACED_HINT) + { + TRACELOG(LOG_WARNING, "ClearWindowState() - FLAG_INTERLACED_HINT is not supported on PLATFORM_DESKTOP_SDL"); + } } // Set icon for window void SetWindowIcon(Image image) { - TRACELOG(LOG_WARNING, "SetWindowIcon() not available on target platform"); + SDL_Surface* iconSurface = NULL; + + Uint32 rmask, gmask, bmask, amask; + int depth = 0; // Depth in bits + int pitch = 0; // Pixel spacing (pitch) in bytes + + switch (image.format) + { + case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: + rmask = 0xFF, gmask = 0; + bmask = 0, amask = 0; + depth = 8, pitch = image.width; + break; + case PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: + rmask = 0xFF, gmask = 0xFF00; + bmask = 0, amask = 0; + depth = 16, pitch = image.width * 2; + break; + case PIXELFORMAT_UNCOMPRESSED_R5G6B5: + rmask = 0xF800, gmask = 0x07E0; + bmask = 0x001F, amask = 0; + depth = 16, pitch = image.width * 2; + break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8: + rmask = 0xFF0000, gmask = 0x00FF00; + bmask = 0x0000FF, amask = 0; + depth = 24, pitch = image.width * 3; + break; + case PIXELFORMAT_UNCOMPRESSED_R5G5B5A1: + rmask = 0xF800, gmask = 0x07C0; + bmask = 0x003E, amask = 0x0001; + depth = 16, pitch = image.width * 2; + break; + case PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: + rmask = 0xF000, gmask = 0x0F00; + bmask = 0x00F0, amask = 0x000F; + depth = 16, pitch = image.width * 2; + break; + case PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: + rmask = 0xFF000000, gmask = 0x00FF0000; + bmask = 0x0000FF00, amask = 0x000000FF; + depth = 32, pitch = image.width * 4; + break; + case PIXELFORMAT_UNCOMPRESSED_R32: + rmask = 0xFFFFFFFF, gmask = 0; + bmask = 0, amask = 0; + depth = 32, pitch = image.width * 4; + break; + case PIXELFORMAT_UNCOMPRESSED_R32G32B32: + rmask = 0xFFFFFFFF, gmask = 0xFFFFFFFF; + bmask = 0xFFFFFFFF, amask = 0; + depth = 96, pitch = image.width * 12; + break; + case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: + rmask = 0xFFFFFFFF, gmask = 0xFFFFFFFF; + bmask = 0xFFFFFFFF, amask = 0xFFFFFFFF; + depth = 128, pitch = image.width * 16; + break; + case PIXELFORMAT_UNCOMPRESSED_R16: + rmask = 0xFFFF, gmask = 0; + bmask = 0, amask = 0; + depth = 16, pitch = image.width * 2; + break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16: + rmask = 0xFFFF, gmask = 0xFFFF; + bmask = 0xFFFF, amask = 0; + depth = 48, pitch = image.width * 6; + break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + rmask = 0xFFFF, gmask = 0xFFFF; + bmask = 0xFFFF, amask = 0xFFFF; + depth = 64, pitch = image.width * 8; + break; + default: + // Compressed formats are not supported + return; + } + + iconSurface = SDL_CreateRGBSurfaceFrom( + image.data, image.width, image.height, depth, pitch, + rmask, gmask, bmask, amask + ); + + if (iconSurface) + { + SDL_SetWindowIcon(platform.window, iconSurface); + SDL_FreeSurface(iconSurface); + } } // Set icon for window @@ -294,7 +582,20 @@ void SetWindowPosition(int x, int y) // Set monitor for the current window void SetWindowMonitor(int monitor) { - TRACELOG(LOG_WARNING, "SetWindowMonitor() not available on target platform"); + if (monitor < 0 || monitor >= SDL_GetNumVideoDisplays()) + { + TRACELOG(LOG_ERROR, "Invalid monitor index"); + return; + } + + SDL_Rect displayBounds; + if (SDL_GetDisplayBounds(monitor, &displayBounds) != 0) + { + TRACELOG(LOG_ERROR, "Failed to get display bounds"); + return; + } + + SDL_SetWindowPosition(platform.window, displayBounds.x, displayBounds.y); } // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) @@ -360,8 +661,20 @@ int GetCurrentMonitor(void) // Get selected monitor position Vector2 GetMonitorPosition(int monitor) { - TRACELOG(LOG_WARNING, "GetMonitorPosition() not implemented on target platform"); - return (Vector2){ 0, 0 }; + if (monitor < 0 || monitor >= SDL_GetNumVideoDisplays()) + { + TRACELOG(LOG_ERROR, "Invalid monitor index"); + return (Vector2) { 0, 0 }; + } + + SDL_Rect displayBounds; + if (SDL_GetDisplayBounds(monitor, &displayBounds) != 0) + { + TRACELOG(LOG_ERROR, "Failed to get display bounds"); + return (Vector2) { 0, 0 }; + } + + return (Vector2) { displayBounds.x, displayBounds.y }; } // Get selected monitor width (currently used by monitor) @@ -530,6 +843,7 @@ void EnableCursor(void) SDL_SetRelativeMouseMode(SDL_FALSE); SDL_ShowCursor(SDL_ENABLE); + platform.cursorRelative = false; CORE.Input.Mouse.cursorHidden = false; } @@ -538,6 +852,7 @@ void DisableCursor(void) { SDL_SetRelativeMouseMode(SDL_TRUE); + platform.cursorRelative = true; CORE.Input.Mouse.cursorHidden = true; } @@ -572,7 +887,7 @@ void OpenURL(const char *url) // Set internal gamepad mappings int SetGamepadMappings(const char *mappings) { - SDL_GameControllerAddMapping(mappings); + return SDL_GameControllerAddMapping(mappings); } // Set mouse position XY @@ -612,7 +927,8 @@ void PollInputEvents(void) CORE.Input.Mouse.currentWheelMove.y = 0; // Register previous mouse position - CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + if (platform.cursorRelative) CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f }; + else CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; // Reset last gamepad button/axis registered state CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; @@ -628,7 +944,7 @@ void PollInputEvents(void) // Register previous keys states // NOTE: Android supports up to 260 keys - for (int i = 0; i < 260; i++) + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) { CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; @@ -662,16 +978,16 @@ void PollInputEvents(void) { switch (event.window.event) { - case SDL_WINDOWEVENT_LEAVE: - case SDL_WINDOWEVENT_HIDDEN: - case SDL_WINDOWEVENT_MINIMIZED: - case SDL_WINDOWEVENT_FOCUS_LOST: - case SDL_WINDOWEVENT_ENTER: - case SDL_WINDOWEVENT_SHOWN: - case SDL_WINDOWEVENT_FOCUS_GAINED: - case SDL_WINDOWEVENT_MAXIMIZED: - case SDL_WINDOWEVENT_RESTORED: - default: break; + case SDL_WINDOWEVENT_LEAVE: + case SDL_WINDOWEVENT_HIDDEN: + case SDL_WINDOWEVENT_MINIMIZED: + case SDL_WINDOWEVENT_FOCUS_LOST: + case SDL_WINDOWEVENT_ENTER: + case SDL_WINDOWEVENT_SHOWN: + case SDL_WINDOWEVENT_FOCUS_GAINED: + case SDL_WINDOWEVENT_MAXIMIZED: + case SDL_WINDOWEVENT_RESTORED: + default: break; } } break; @@ -710,8 +1026,17 @@ void PollInputEvents(void) } break; case SDL_MOUSEMOTION: { - CORE.Input.Mouse.currentPosition.x = (float)event.motion.x; - CORE.Input.Mouse.currentPosition.y = (float)event.motion.y; + if (platform.cursorRelative) + { + CORE.Input.Mouse.currentPosition.x = (float)event.motion.xrel; + CORE.Input.Mouse.currentPosition.y = (float)event.motion.yrel; + CORE.Input.Mouse.previousPosition = (Vector2){ 0.0f, 0.0f }; + } + else + { + CORE.Input.Mouse.currentPosition.x = (float)event.motion.x; + CORE.Input.Mouse.currentPosition.y = (float)event.motion.y; + } } break; // Check gamepad events @@ -790,6 +1115,12 @@ static int InitPlatform(void) SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); //SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG | SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); + + if (CORE.Window.flags & FLAG_VSYNC_HINT) + { + SDL_GL_SetSwapInterval(1); + } + if (CORE.Window.flags & FLAG_MSAA_4X_HINT) { SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1); @@ -807,6 +1138,12 @@ static int InitPlatform(void) { CORE.Window.ready = true; + SDL_DisplayMode displayMode; + SDL_GetCurrentDisplayMode(GetCurrentMonitor(), &displayMode); + + CORE.Window.display.width = displayMode.w; + CORE.Window.display.height = displayMode.h; + CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; CORE.Window.currentFbo.width = CORE.Window.render.width; From 19ff0e5fb1bbac46921ab34f922e77461c13c11f Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Oct 2023 13:05:50 +0200 Subject: [PATCH 0385/1350] REVIEWED: `rlLoadTexture()` #3440 --- src/rlgl.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index 40e7b4785..e38642ec9 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3000,7 +3000,8 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipOffset = 0; // Mipmap data offset, only used for tracelog // NOTE: Added pointer math separately from function to avoid UBSAN complaining - unsigned char *dataPtr = (unsigned char *)data; + unsigned char *dataPtr = NULL; + if (data != NULL) dataPtr = (unsigned char *)data; // Load the different mipmap levels for (int i = 0; i < mipmapCount; i++) @@ -3040,7 +3041,7 @@ unsigned int rlLoadTexture(const void *data, int width, int height, int format, mipWidth /= 2; mipHeight /= 2; mipOffset += mipSize; // Increment offset position to next mipmap - dataPtr += mipSize; // Increment data pointer to next mipmap + if (data != NULL) dataPtr += mipSize; // Increment data pointer to next mipmap // Security check for NPOT textures if (mipWidth < 1) mipWidth = 1; From a64d606cb3aa55df0f01f75b8db0ca6b9a06b562 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Thu, 19 Oct 2023 08:09:27 -0300 Subject: [PATCH 0386/1350] Fix GetMonitorPhysical* dpi (#3442) --- src/rcore_desktop_sdl.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rcore_desktop_sdl.c b/src/rcore_desktop_sdl.c index fedebeee4..c574b8199 100644 --- a/src/rcore_desktop_sdl.c +++ b/src/rcore_desktop_sdl.c @@ -230,7 +230,7 @@ void ToggleFullscreen(void) else { SDL_SetWindowFullscreen(platform.window, 0); - CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; + CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; } } @@ -725,12 +725,12 @@ int GetMonitorPhysicalWidth(int monitor) if ((monitor >= 0) && (monitor < monitorCount)) { - float vdpi = 0.0f; - SDL_GetDisplayDPI(monitor, NULL, NULL, &vdpi); + float ddpi = 0.0f; + SDL_GetDisplayDPI(monitor, &ddpi, NULL, NULL); SDL_DisplayMode mode; SDL_GetCurrentDisplayMode(monitor, &mode); // Calculate size on inches, then convert to millimeter - if (vdpi > 0.0f) width = (mode.w/vdpi)*25.4f; + if (ddpi > 0.0f) width = (mode.w/ddpi)*25.4f; } else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); @@ -747,12 +747,12 @@ int GetMonitorPhysicalHeight(int monitor) if ((monitor >= 0) && (monitor < monitorCount)) { - float vdpi = 0.0f; - SDL_GetDisplayDPI(monitor, NULL, NULL, &vdpi); + float ddpi = 0.0f; + SDL_GetDisplayDPI(monitor, &ddpi, NULL, NULL); SDL_DisplayMode mode; SDL_GetCurrentDisplayMode(monitor, &mode); // Calculate size on inches, then convert to millimeter - if (vdpi > 0.0f) height = (mode.h/vdpi)*25.4f; + if (ddpi > 0.0f) height = (mode.h/ddpi)*25.4f; } else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); From 982641228c6ee5732ae99a9c26895305143b21d9 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Oct 2023 13:36:10 +0200 Subject: [PATCH 0387/1350] REDESIGNED: Move platforms to separate directory #3313 --- src/{ => platforms}/rcore_android.c | 0 src/{ => platforms}/rcore_desktop.c | 0 src/{ => platforms}/rcore_desktop_sdl.c | 0 src/{ => platforms}/rcore_drm.c | 0 src/{ => platforms}/rcore_template.c | 0 src/{ => platforms}/rcore_web.c | 0 src/rcore.c | 32 ++++++++++++++++++------- 7 files changed, 23 insertions(+), 9 deletions(-) rename src/{ => platforms}/rcore_android.c (100%) rename src/{ => platforms}/rcore_desktop.c (100%) rename src/{ => platforms}/rcore_desktop_sdl.c (100%) rename src/{ => platforms}/rcore_drm.c (100%) rename src/{ => platforms}/rcore_template.c (100%) rename src/{ => platforms}/rcore_web.c (100%) diff --git a/src/rcore_android.c b/src/platforms/rcore_android.c similarity index 100% rename from src/rcore_android.c rename to src/platforms/rcore_android.c diff --git a/src/rcore_desktop.c b/src/platforms/rcore_desktop.c similarity index 100% rename from src/rcore_desktop.c rename to src/platforms/rcore_desktop.c diff --git a/src/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c similarity index 100% rename from src/rcore_desktop_sdl.c rename to src/platforms/rcore_desktop_sdl.c diff --git a/src/rcore_drm.c b/src/platforms/rcore_drm.c similarity index 100% rename from src/rcore_drm.c rename to src/platforms/rcore_drm.c diff --git a/src/rcore_template.c b/src/platforms/rcore_template.c similarity index 100% rename from src/rcore_template.c rename to src/platforms/rcore_template.c diff --git a/src/rcore_web.c b/src/platforms/rcore_web.c similarity index 100% rename from src/rcore_web.c rename to src/platforms/rcore_web.c diff --git a/src/rcore.c b/src/rcore.c index c6770a5bb..fd0335c6e 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -281,8 +281,8 @@ extern void LoadFontDefault(void); // [Module: text] Loads default font on extern void UnloadFontDefault(void); // [Module: text] Unloads default font from GPU memory #endif -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static void ClosePlatform(void); // Close platform +extern int InitPlatform(void); // Initialize platform (graphics, inputs and more) +extern void ClosePlatform(void); // Close platform static void InitTimer(void); // Initialize timer (hi-resolution if available) static void SetupFramebuffer(int width, int height); // Setup main framebuffer @@ -309,15 +309,15 @@ const char *TextFormat(const char *text, ...); // Formatting of text with // Include platform-specific submodules #if defined(PLATFORM_DESKTOP) - #include "rcore_desktop.c" + #include "platforms/rcore_desktop.c" #elif defined(PLATFORM_DESKTOP_SDL) - #include "rcore_desktop_sdl.c" + #include "platforms/rcore_desktop_sdl.c" #elif defined(PLATFORM_WEB) - #include "rcore_web.c" + #include "platforms/rcore_web.c" #elif defined(PLATFORM_DRM) - #include "rcore_drm.c" + #include "platforms/rcore_drm.c" #elif defined(PLATFORM_ANDROID) - #include "rcore_android.c" + #include "platforms/rcore_android.c" #else // TODO: Include your custom platform backend! // i.e software rendering backend or console backend! @@ -328,8 +328,6 @@ const char *TextFormat(const char *text, ...); // Formatting of text with //---------------------------------------------------------------------------------- // NOTE: Functions with a platform-specific implementation on rcore_.c -//void InitWindow(int width, int height, const char *title) -//void CloseWindow(void) //bool WindowShouldClose(void) //void ToggleFullscreen(void) //void ToggleBorderlessWindowed(void) @@ -378,6 +376,22 @@ void InitWindow(int width, int height, const char *title) { TRACELOG(LOG_INFO, "Initializing raylib %s", RAYLIB_VERSION); +#if defined(PLATFORM_DESKTOP) + TRACELOG(LOG_INFO, "Platform backend: DESKTOP (GLFW)"); +#elif defined(PLATFORM_DESKTOP_SDL) + TRACELOG(LOG_INFO, "Platform backend: DESKTOP (SDL)"); +#elif defined(PLATFORM_WEB) + TRACELOG(LOG_INFO, "Platform backend: WEB (HTML5)"); +#elif defined(PLATFORM_DRM) + TRACELOG(LOG_INFO, "Platform backend: NATIVE DRM"); +#elif defined(PLATFORM_ANDROID) + TRACELOG(LOG_INFO, "Platform backend: ANDROID"); +#else + // TODO: Include your custom platform backend! + // i.e software rendering backend or console backend! + TRACELOG(LOG_INFO, "Platform backend: CUSTOM"); +#endif + TRACELOG(LOG_INFO, "Supported raylib modules:"); TRACELOG(LOG_INFO, " > rcore:..... loaded (mandatory)"); TRACELOG(LOG_INFO, " > rlgl:...... loaded (mandatory)"); From 65dd0afb60770f88d863283aed7098a474535183 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Oct 2023 13:41:09 +0200 Subject: [PATCH 0388/1350] Update Makefile --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index e6c5106cc..ea8bdd5e5 100644 --- a/src/Makefile +++ b/src/Makefile @@ -633,7 +633,7 @@ endif # Compile all modules with their prerequisites # Prerequisites of core module -rcore.o : rcore_android.c rcore_desktop.c rcore_drm.c rcore_template.c rcore_web.c +rcore.o : platforms/rcore_android.c platforms/rcore_desktop.c platforms/rcore_drm.c platforms/rcore_template.c platforms/rcore_web.c # Compile core module rcore.o : rcore.c raylib.h rlgl.h utils.h raymath.h rcamera.h rgestures.h From b674e344a878613881d5e8d0e9f86176e4c0a56f Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Oct 2023 13:46:02 +0200 Subject: [PATCH 0389/1350] REVIEWED: Issue with symbols exposure --- src/platforms/rcore_android.c | 4 ++-- src/platforms/rcore_desktop.c | 4 ++-- src/platforms/rcore_desktop_sdl.c | 5 +++-- src/platforms/rcore_drm.c | 4 ++-- src/platforms/rcore_template.c | 4 ++-- src/platforms/rcore_web.c | 4 ++-- 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index bf55ece27..ddf0c7a45 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -80,8 +80,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static void ClosePlatform(void); // Close platform +int InitPlatform(void); // Initialize platform (graphics, inputs and more) +void ClosePlatform(void); // Close platform static void AndroidCommandCallback(struct android_app *app, int32_t cmd); // Process Android activity lifecycle commands static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event); // Process Android inputs diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index 29969d4ca..4dec2c77b 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -111,8 +111,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static void ClosePlatform(void); // Close platform +int InitPlatform(void); // Initialize platform (graphics, inputs and more) +void ClosePlatform(void); // Close platform // Error callback event static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index c574b8199..b64724334 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -199,8 +199,9 @@ static const int CursorsLUT[] = { //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static void ClosePlatform(void); // Close platform +int InitPlatform(void); // Initialize platform (graphics, inputs and more) +void ClosePlatform(void); // Close platform + static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode); // Help convert SDL scancodes to raylib key //---------------------------------------------------------------------------------- diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 3302ec362..4df40e5b0 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -138,8 +138,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static void ClosePlatform(void); // Close platform +int InitPlatform(void); // Initialize platform (graphics, inputs and more) +void ClosePlatform(void); // Close platform static void InitKeyboard(void); // Initialize raw keyboard system static void RestoreKeyboard(void); // Restore keyboard system diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index f94cca8c1..87aca16da 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -71,8 +71,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static bool InitGraphicsDevice(void); // Initialize graphics device +int InitPlatform(void); // Initialize platform (graphics, inputs and more) +bool InitGraphicsDevice(void); // Initialize graphics device //---------------------------------------------------------------------------------- // Module Functions Declaration diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 465bb8957..bfc5bd790 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -85,8 +85,8 @@ static PlatformData platform = { 0 }; // Platform specific data //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- -static int InitPlatform(void); // Initialize platform (graphics, inputs and more) -static void ClosePlatform(void); // Close platform +int InitPlatform(void); // Initialize platform (graphics, inputs and more) +void ClosePlatform(void); // Close platform // Error callback event static void ErrorCallback(int error, const char *description); // GLFW3 Error Callback, runs on GLFW3 error From 081fffd46e56ae1e6f80b12bf8fe5728828def22 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 19 Oct 2023 13:57:31 +0200 Subject: [PATCH 0390/1350] REVIEWED: Issue with functions definitions --- src/platforms/rcore_android.c | 5 +++-- src/platforms/rcore_desktop.c | 4 ++-- src/platforms/rcore_desktop_sdl.c | 5 +++-- src/platforms/rcore_drm.c | 4 ++-- src/platforms/rcore_template.c | 4 ++-- src/platforms/rcore_web.c | 5 +++-- src/rcore.c | 3 ++- 7 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index ddf0c7a45..1272eecc3 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -516,7 +516,7 @@ void PollInputEvents(void) //---------------------------------------------------------------------------------- // Initialize platform: graphics, inputs and more -static int InitPlatform(void) +int InitPlatform(void) { CORE.Window.currentFbo.width = CORE.Window.screen.width; CORE.Window.currentFbo.height = CORE.Window.screen.height; @@ -589,7 +589,7 @@ static int InitPlatform(void) } // Close platform -static void ClosePlatform(void) +void ClosePlatform(void) { // Close surface, context and display if (platform.device != EGL_NO_DISPLAY) @@ -613,6 +613,7 @@ static void ClosePlatform(void) } } + // Initialize display device and framebuffer // NOTE: width and height represent the screen (framebuffer) desired size, not actual display size // If width or height are 0, default display size will be used for framebuffer size diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index 4dec2c77b..ea1b15fad 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -1214,7 +1214,7 @@ void PollInputEvents(void) //---------------------------------------------------------------------------------- // Initialize platform: graphics, inputs and more -static int InitPlatform(void) +int InitPlatform(void) { glfwSetErrorCallback(ErrorCallback); /* @@ -1531,7 +1531,7 @@ static int InitPlatform(void) } // Close platform -static void ClosePlatform(void) +void ClosePlatform(void) { glfwDestroyWindow(platform.handle); glfwTerminate(); diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index b64724334..c57a94fe9 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1070,7 +1070,7 @@ void PollInputEvents(void) //---------------------------------------------------------------------------------- // Initialize platform: graphics, inputs and more -static int InitPlatform(void) +int InitPlatform(void) { // Initialize SDL internal global state int result = SDL_Init(SDL_INIT_EVERYTHING); @@ -1180,7 +1180,7 @@ static int InitPlatform(void) return 0; } -static void ClosePlatform(void) +void ClosePlatform(void) { SDL_FreeCursor(platform.cursor); // Free cursor SDL_GL_DeleteContext(platform.glContext); // Deinitialize OpenGL context @@ -1188,6 +1188,7 @@ static void ClosePlatform(void) SDL_Quit(); // Deinitialize SDL internal global state } + static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode) { if (sdlScancode >= 0 && sdlScancode < SCANCODE_MAPPED_NUM) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 4df40e5b0..2ecfc2a3a 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -565,7 +565,7 @@ void PollInputEvents(void) //---------------------------------------------------------------------------------- // Initialize platform: graphics, inputs and more -static int InitPlatform(void) +int InitPlatform(void) { platform.fd = -1; platform.connector = NULL; @@ -916,7 +916,7 @@ static int InitPlatform(void) } // Close platform -static void ClosePlatform(void) +void ClosePlatform(void) { if (platform.prevFB) { diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index 87aca16da..dc71a66ca 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -429,7 +429,7 @@ void PollInputEvents(void) //---------------------------------------------------------------------------------- // Initialize platform: graphics, inputs and more -static int InitPlatform(void) +int InitPlatform(void) { CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; @@ -556,7 +556,7 @@ static int InitPlatform(void) } // Close platform -static void ClosePlatform(void) +void ClosePlatform(void) { // TODO: De-initialize graphics, inputs and more } diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index bfc5bd790..e918e7d34 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -669,7 +669,7 @@ void PollInputEvents(void) //---------------------------------------------------------------------------------- // Initialize platform: graphics, inputs and more -static int InitPlatform(void) +int InitPlatform(void) { glfwSetErrorCallback(ErrorCallback); @@ -932,12 +932,13 @@ static int InitPlatform(void) } // Close platform -static void ClosePlatform(void) +void ClosePlatform(void) { glfwDestroyWindow(platform.handle); glfwTerminate(); } + // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description) { diff --git a/src/rcore.c b/src/rcore.c index fd0335c6e..e3cd08188 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2523,7 +2523,8 @@ int GetTouchPointCount(void) //---------------------------------------------------------------------------------- // NOTE: Functions with a platform-specific implementation on rcore_.c -//static bool InitPlatform(void) +//int InitPlatform(void) +//void ClosePlatform(void) // Initialize hi-resolution timer void InitTimer(void) From c66eb491992bebf4462d61cfb9c232f7cfefba28 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Fri, 20 Oct 2023 12:38:14 -0300 Subject: [PATCH 0391/1350] [core] Complement implementations for `SDL` (#3444) * Complement SetWindowMonitor SDL implementation * Complement SetWindowMonitor SDL implementation 2 * Complement SetWindowMonitor SDL implementation 3 * Complement GetMonitorPosition SDL implementation * Small tweaks to various SDL implementation * Small tweaks to various SDL implementation 2 --- src/platforms/rcore_desktop_sdl.c | 104 ++++++++++++++++++------------ 1 file changed, 63 insertions(+), 41 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index c57a94fe9..1b52a960d 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -583,20 +583,51 @@ void SetWindowPosition(int x, int y) // Set monitor for the current window void SetWindowMonitor(int monitor) { - if (monitor < 0 || monitor >= SDL_GetNumVideoDisplays()) + const int monitorCount = SDL_GetNumVideoDisplays(); + if ((monitor >= 0) && (monitor < monitorCount)) { - TRACELOG(LOG_ERROR, "Invalid monitor index"); - return; - } + // NOTE: + // 1. SDL started supporting moving exclusive fullscreen windows between displays on SDL3, + // see commit https://github.com/libsdl-org/SDL/commit/3f5ef7dd422057edbcf3e736107e34be4b75d9ba + // 2. A workround for SDL2 is leaving fullscreen, moving the window, then entering full screen again. + const bool wasFullscreen = ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) ? true : false; - SDL_Rect displayBounds; - if (SDL_GetDisplayBounds(monitor, &displayBounds) != 0) - { - TRACELOG(LOG_ERROR, "Failed to get display bounds"); - return; - } + const int screenWidth = CORE.Window.screen.width; + const int screenHeight = CORE.Window.screen.height; + SDL_Rect usableBounds; + if (SDL_GetDisplayUsableBounds(monitor, &usableBounds) == 0) + { + if (wasFullscreen == 1) ToggleFullscreen(); // Leave fullscreen. - SDL_SetWindowPosition(platform.window, displayBounds.x, displayBounds.y); + // If the screen size is larger than the monitor usable area, anchor it on the top left corner, otherwise, center it + if ((screenWidth >= usableBounds.w) || (screenHeight >= usableBounds.h)) + { + // NOTE: + // 1. There's a known issue where if the window larger than the target display bounds, + // when moving the windows to that display, the window could be clipped back + // ending up positioned partly outside the target display. + // 2. The workaround for that is, previously to moving the window, + // setting the window size to the target display size, so they match. + // 3. It was't done here because we can't assume changing the window size automatically + // is acceptable behavior by the user. + SDL_SetWindowPosition(platform.window, usableBounds.x, usableBounds.y); + CORE.Window.position.x = usableBounds.x; + CORE.Window.position.y = usableBounds.y; + } + else + { + const int x = usableBounds.x + (usableBounds.w/2) - (screenWidth/2); + const int y = usableBounds.y + (usableBounds.h/2) - (screenHeight/2); + SDL_SetWindowPosition(platform.window, x, y); + CORE.Window.position.x = x; + CORE.Window.position.y = y; + } + + if (wasFullscreen == 1) ToggleFullscreen(); // Re-enter fullscreen + } + else TRACELOG(LOG_WARNING, "SDL: Failed to get selected display usable bounds"); + } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); } // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) @@ -656,26 +687,28 @@ int GetMonitorCount(void) // Get number of monitors int GetCurrentMonitor(void) { - return SDL_GetWindowDisplayIndex(platform.window); + int currentMonitor = 0; + + currentMonitor = SDL_GetWindowDisplayIndex(platform.window); + + return currentMonitor; } // Get selected monitor position Vector2 GetMonitorPosition(int monitor) { - if (monitor < 0 || monitor >= SDL_GetNumVideoDisplays()) + const int monitorCount = SDL_GetNumVideoDisplays(); + if ((monitor >= 0) && (monitor < monitorCount)) { - TRACELOG(LOG_ERROR, "Invalid monitor index"); - return (Vector2) { 0, 0 }; + SDL_Rect displayBounds; + if (SDL_GetDisplayUsableBounds(monitor, &displayBounds) == 0) + { + return (Vector2){ (float)displayBounds.x, (float)displayBounds.y }; + } + else TRACELOG(LOG_WARNING, "SDL: Failed to get selected display usable bounds"); } - - SDL_Rect displayBounds; - if (SDL_GetDisplayBounds(monitor, &displayBounds) != 0) - { - TRACELOG(LOG_ERROR, "Failed to get display bounds"); - return (Vector2) { 0, 0 }; - } - - return (Vector2) { displayBounds.x, displayBounds.y }; + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); + return (Vector2){ 0.0f, 0.0f }; } // Get selected monitor width (currently used by monitor) @@ -683,9 +716,7 @@ int GetMonitorWidth(int monitor) { int width = 0; - int monitorCount = 0; - monitorCount = SDL_GetNumVideoDisplays(); - + const int monitorCount = SDL_GetNumVideoDisplays(); if ((monitor >= 0) && (monitor < monitorCount)) { SDL_DisplayMode mode; @@ -702,9 +733,7 @@ int GetMonitorHeight(int monitor) { int height = 0; - int monitorCount = 0; - monitorCount = SDL_GetNumVideoDisplays(); - + const int monitorCount = SDL_GetNumVideoDisplays(); if ((monitor >= 0) && (monitor < monitorCount)) { SDL_DisplayMode mode; @@ -721,9 +750,7 @@ int GetMonitorPhysicalWidth(int monitor) { int width = 0; - int monitorCount = 0; - monitorCount = SDL_GetNumVideoDisplays(); - + const int monitorCount = SDL_GetNumVideoDisplays(); if ((monitor >= 0) && (monitor < monitorCount)) { float ddpi = 0.0f; @@ -743,9 +770,7 @@ int GetMonitorPhysicalHeight(int monitor) { int height = 0; - int monitorCount = 0; - monitorCount = SDL_GetNumVideoDisplays(); - + const int monitorCount = SDL_GetNumVideoDisplays(); if ((monitor >= 0) && (monitor < monitorCount)) { float ddpi = 0.0f; @@ -765,9 +790,7 @@ int GetMonitorRefreshRate(int monitor) { int refresh = 0; - int monitorCount = 0; - monitorCount = SDL_GetNumVideoDisplays(); - + const int monitorCount = SDL_GetNumVideoDisplays(); if ((monitor >= 0) && (monitor < monitorCount)) { SDL_DisplayMode mode; @@ -782,8 +805,7 @@ int GetMonitorRefreshRate(int monitor) // Get the human-readable, UTF-8 encoded name of the selected monitor const char *GetMonitorName(int monitor) { - int monitorCount = 0; - monitorCount = SDL_GetNumVideoDisplays(); + const int monitorCount = SDL_GetNumVideoDisplays(); if ((monitor >= 0) && (monitor < monitorCount)) return SDL_GetDisplayName(monitor); else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); From e5993c4a4b27cb30617f1d07b0d9583e8f556902 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sat, 21 Oct 2023 07:11:54 -0300 Subject: [PATCH 0392/1350] [core] Complement implementations for `SDL` (2) (#3447) * Add note and todo to GetWindowScaleDPI * Complement ToggleFullscreen and change ToggleBorderlessWindowed * Complement SetWindowState and ClearWindowState --- src/platforms/rcore_desktop_sdl.c | 113 ++++++++++++++---------------- 1 file changed, 52 insertions(+), 61 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 1b52a960d..b261b0428 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -223,72 +223,45 @@ bool WindowShouldClose(void) // Toggle fullscreen mode void ToggleFullscreen(void) { - if (!IsWindowState(FLAG_FULLSCREEN_MODE)) + const int monitor = SDL_GetWindowDisplayIndex(platform.window); + const int monitorCount = SDL_GetNumVideoDisplays(); + if ((monitor >= 0) && (monitor < monitorCount)) { - SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN); - CORE.Window.flags |= FLAG_FULLSCREEN_MODE; - } - else - { - SDL_SetWindowFullscreen(platform.window, 0); - CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; + if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) + { + SDL_SetWindowFullscreen(platform.window, 0); + CORE.Window.flags &= ~FLAG_FULLSCREEN_MODE; + CORE.Window.fullscreen = false; + } + else + { + SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN); + CORE.Window.flags |= FLAG_FULLSCREEN_MODE; + CORE.Window.fullscreen = true; + } } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); } // Toggle borderless windowed mode void ToggleBorderlessWindowed(void) { - // Leave fullscreen before attempting to set borderless windowed mode and get screen position from it - bool wasOnFullscreen = false; - if (CORE.Window.fullscreen) + const int monitor = SDL_GetWindowDisplayIndex(platform.window); + const int monitorCount = SDL_GetNumVideoDisplays(); + if ((monitor >= 0) && (monitor < monitorCount)) { - CORE.Window.previousPosition = CORE.Window.position; - ToggleFullscreen(); - wasOnFullscreen = true; - } - - if (!IsWindowState(FLAG_BORDERLESS_WINDOWED_MODE)) - { - // Store the window's current position and size - SDL_GetWindowPosition(platform.window, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y); - CORE.Window.previousScreen = CORE.Window.screen; - - // Set screen position and size inside valid bounds - SDL_Rect displayBounds; - if (SDL_GetDisplayBounds(GetCurrentMonitor(), &displayBounds) == 0) + if ((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0) { - SDL_SetWindowPosition(platform.window, displayBounds.x, displayBounds.y); - SDL_SetWindowSize(platform.window, displayBounds.w, displayBounds.h); + SDL_SetWindowFullscreen(platform.window, 0); + CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; + } + else + { + SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN_DESKTOP); + CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE; } - - // Set borderless mode and flag - SDL_SetWindowBordered(platform.window, SDL_FALSE); - CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; - - // Set topmost modes and flag - SDL_SetWindowAlwaysOnTop(platform.window, SDL_TRUE); - CORE.Window.flags |= FLAG_WINDOW_TOPMOST; - - // Set borderless windowed flag - CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE; - } - else - { - // Remove borderless mode and flag - SDL_SetWindowBordered(platform.window, SDL_TRUE); - CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; - - // Remove topmost modes and flag - SDL_SetWindowAlwaysOnTop(platform.window, SDL_FALSE); - CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; - - // Restore the window's previous size and position - SDL_SetWindowSize(platform.window, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); - SDL_SetWindowPosition(platform.window, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y); - - // Remove borderless windowed flag - CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); } // Set window state: maximized, if resizable @@ -322,7 +295,14 @@ void SetWindowState(unsigned int flags) } if (flags & FLAG_FULLSCREEN_MODE) { - SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN); + const int monitor = SDL_GetWindowDisplayIndex(platform.window); + const int monitorCount = SDL_GetNumVideoDisplays(); + if ((monitor >= 0) && (monitor < monitorCount)) + { + SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN); + CORE.Window.fullscreen = true; + } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); } if (flags & FLAG_WINDOW_RESIZABLE) { @@ -374,8 +354,13 @@ void SetWindowState(unsigned int flags) } if (flags & FLAG_BORDERLESS_WINDOWED_MODE) { - // NOTE: Same as FLAG_WINDOW_UNDECORATED with SDL ? - SDL_SetWindowBordered(platform.window, SDL_FALSE); + const int monitor = SDL_GetWindowDisplayIndex(platform.window); + const int monitorCount = SDL_GetNumVideoDisplays(); + if ((monitor >= 0) && (monitor < monitorCount)) + { + SDL_SetWindowFullscreen(platform.window, SDL_WINDOW_FULLSCREEN_DESKTOP); + } + else TRACELOG(LOG_WARNING, "SDL: Failed to find selected monitor"); } if (flags & FLAG_MSAA_4X_HINT) { @@ -400,6 +385,7 @@ void ClearWindowState(unsigned int flags) if (flags & FLAG_FULLSCREEN_MODE) { SDL_SetWindowFullscreen(platform.window, 0); + CORE.Window.fullscreen = false; } if (flags & FLAG_WINDOW_RESIZABLE) { @@ -450,8 +436,7 @@ void ClearWindowState(unsigned int flags) } if (flags & FLAG_BORDERLESS_WINDOWED_MODE) { - // NOTE: Same as FLAG_WINDOW_UNDECORATED with SDL ? - SDL_SetWindowBordered(platform.window, SDL_TRUE); + SDL_SetWindowFullscreen(platform.window, 0); } if (flags & FLAG_MSAA_4X_HINT) { @@ -827,8 +812,14 @@ Vector2 GetWindowPosition(void) // Get window scale DPI factor for current monitor Vector2 GetWindowScaleDPI(void) { + Vector2 scale = { 1.0f, 1.0f }; + + // NOTE: SDL_GetWindowDisplayScale was only added on SDL3 + // see https://wiki.libsdl.org/SDL3/SDL_GetWindowDisplayScale + // TODO: Implement the window scale factor calculation manually. TRACELOG(LOG_WARNING, "GetWindowScaleDPI() not implemented on target platform"); - return (Vector2){ 1.0f, 1.0f }; + + return scale; } // Set clipboard text content From 8cda4273ece19fe349bad73cb7e118352ea1cc42 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sun, 22 Oct 2023 04:45:04 -0300 Subject: [PATCH 0393/1350] [core] Complement implementations for `SDL` (3) (#3450) * Fix SetWindowMinSize and SetWindowMaxSize * Fix window resizes to update the viewport * Fix window resizes to update the viewport 2 --- src/platforms/rcore_desktop_sdl.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index b261b0428..d2f550ace 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -618,6 +618,8 @@ void SetWindowMonitor(int monitor) // Set window minimum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMinSize(int width, int height) { + SDL_SetWindowMinimumSize(platform.window, width, height); + CORE.Window.screenMin.width = width; CORE.Window.screenMin.height = height; } @@ -625,6 +627,8 @@ void SetWindowMinSize(int width, int height) // Set window maximum dimensions (FLAG_WINDOW_RESIZABLE) void SetWindowMaxSize(int width, int height) { + SDL_SetWindowMaximumSize(platform.window, width, height); + CORE.Window.screenMax.width = width; CORE.Window.screenMax.height = height; } @@ -979,6 +983,8 @@ void PollInputEvents(void) } */ + CORE.Window.resizedLastFrame = false; + SDL_Event event = { 0 }; while (SDL_PollEvent(&event) != 0) { @@ -992,6 +998,18 @@ void PollInputEvents(void) { switch (event.window.event) { + case SDL_WINDOWEVENT_RESIZED: + case SDL_WINDOWEVENT_SIZE_CHANGED: + { + const int width = event.window.data1; + const int height = event.window.data2; + SetupViewport(width, height); + CORE.Window.screen.width = width; + CORE.Window.screen.height = height; + CORE.Window.currentFbo.width = width; + CORE.Window.currentFbo.height = height; + CORE.Window.resizedLastFrame = true; + } break; case SDL_WINDOWEVENT_LEAVE: case SDL_WINDOWEVENT_HIDDEN: case SDL_WINDOWEVENT_MINIMIZED: From bcfa7c6718202d7697c1af4019d0dd4f30eb12e5 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 10:08:39 +0200 Subject: [PATCH 0394/1350] Update rcore_desktop.c --- src/platforms/rcore_desktop.c | 43 ++++++++++++++++++----------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index ea1b15fad..9e653dd0e 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -1349,7 +1349,7 @@ int InitPlatform(void) // Forcing this initialization here avoids doing it on PollInputEvents() called by EndDrawing() after first frame has been just drawn. // The initialization will still happen and possible delays still occur, but before the window is shown, which is a nicer experience. // REF: https://github.com/raysan5/raylib/issues/1554 - if (MAX_GAMEPADS > 0) glfwSetJoystickCallback(NULL); + glfwSetJoystickCallback(NULL); // Find monitor resolution GLFWmonitor *monitor = glfwGetPrimaryMonitor(); @@ -1404,6 +1404,7 @@ int InitPlatform(void) } } } + TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height); // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example, @@ -1447,27 +1448,8 @@ int InitPlatform(void) TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); return -1; } - - // Set window callback events - glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! - glfwSetWindowMaximizeCallback(platform.handle, WindowMaximizeCallback); - glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback); - glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback); - glfwSetDropCallback(platform.handle, WindowDropCallback); - - // Set input callback events - glfwSetKeyCallback(platform.handle, KeyCallback); - glfwSetCharCallback(platform.handle, CharCallback); - glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback); - glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes - glfwSetScrollCallback(platform.handle, MouseScrollCallback); - glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); - glfwSetJoystickCallback(JoystickCallback); - + glfwMakeContextCurrent(platform.handle); - - glfwSetInputMode(platform.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) - glfwSwapInterval(0); // No V-Sync by default // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) @@ -1520,6 +1502,25 @@ int InitPlatform(void) // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); + + + // Set window callback events + glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! + glfwSetWindowMaximizeCallback(platform.handle, WindowMaximizeCallback); + glfwSetWindowIconifyCallback(platform.handle, WindowIconifyCallback); + glfwSetWindowFocusCallback(platform.handle, WindowFocusCallback); + glfwSetDropCallback(platform.handle, WindowDropCallback); + + // Set input callback events + glfwSetKeyCallback(platform.handle, KeyCallback); + glfwSetCharCallback(platform.handle, CharCallback); + glfwSetMouseButtonCallback(platform.handle, MouseButtonCallback); + glfwSetCursorPosCallback(platform.handle, MouseCursorPosCallback); // Track mouse position changes + glfwSetScrollCallback(platform.handle, MouseScrollCallback); + glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); + glfwSetJoystickCallback(JoystickCallback); + + glfwSetInputMode(platform.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) // Initialize hi-res timer InitTimer(); From 1aad6a2fc0eb35c9aff845c84b2a4d798be67b27 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 10:09:03 +0200 Subject: [PATCH 0395/1350] REVIEWED: New platform backend template comments --- src/platforms/rcore_template.c | 75 +++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index dc71a66ca..08210289e 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -431,6 +431,12 @@ void PollInputEvents(void) // Initialize platform: graphics, inputs and more int InitPlatform(void) { + // TODO: Initialize graphic device: display/window + // It usually requires setting up the platform display system configuration + // and connexion with the GPU through some system graphic API + // raylib uses OpenGL so, platform should create that kind of connection + // Below example illustrates that process using EGL library + //---------------------------------------------------------------------------- CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; @@ -496,61 +502,66 @@ int InitPlatform(void) } // Create an EGL window surface - //--------------------------------------------------------------------------------- EGLint displayFormat = 0; // EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry() // As soon as we picked a EGLConfig, we can safely reconfigure the ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID eglGetConfigAttrib(platform.device, platform.config, EGL_NATIVE_VISUAL_ID, &displayFormat); - // At this point we need to manage render size vs screen size - // NOTE: This function use and modify global module variables: - // -> CORE.Window.screen.width/CORE.Window.screen.height - // -> CORE.Window.render.width/CORE.Window.render.height - // -> CORE.Window.screenScale - SetupFramebuffer(CORE.Window.display.width, CORE.Window.display.height); - - ANativeWindow_setBuffersGeometry(platform.app->window, CORE.Window.render.width, CORE.Window.render.height, displayFormat); - //ANativeWindow_setBuffersGeometry(platform.app->window, 0, 0, displayFormat); // Force use of native display size + // Android specific call + ANativeWindow_setBuffersGeometry(platform.app->window, 0, 0, displayFormat); // Force use of native display size platform.surface = eglCreateWindowSurface(platform.device, platform.config, platform.app->window, NULL); // There must be at least one frame displayed before the buffers are swapped - //eglSwapInterval(platform.device, 1); + eglSwapInterval(platform.device, 1); - if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) + EGLBoolean result = eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context); + + // Enabling current display surface and context failed + if (result == EGL_FALSE) { TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); return -1; } - else - { - CORE.Window.render.width = CORE.Window.screen.width; - CORE.Window.render.height = CORE.Window.screen.height; - CORE.Window.currentFbo.width = CORE.Window.render.width; - CORE.Window.currentFbo.height = CORE.Window.render.height; + else CORE.Window.ready = true; + //---------------------------------------------------------------------------- + + // If everything work as expected, we can continue + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); - TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); - TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); - TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); - TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); - } + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); - // Load OpenGL extensions + // TODO: Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions + //---------------------------------------------------------------------------- rlLoadExtensions(eglGetProcAddress); + //---------------------------------------------------------------------------- + + // TODO: Initialize input system + // It could imply keyboard, mouse, gamepad, touch... + // Depending on the platform libraries/SDK it could use a callbacks mechanims + // For system events and inputs evens polling on a per-frame basis, use PollInputEvents() + //---------------------------------------------------------------------------- + // ... + //---------------------------------------------------------------------------- - CORE.Window.ready = true; - - // If graphic device is no properly initialized, we end program - if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } - - // Initialize hi-res timer + // TODO: Initialize hi-res timer + //---------------------------------------------------------------------------- InitTimer(); + //---------------------------------------------------------------------------- - // Initialize base path for storage + // TODO: Initialize base path for storage + //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); + //---------------------------------------------------------------------------- return 0; } From c4fb6c8517d8480afffafdff81155dd35a600f73 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 10:27:22 +0200 Subject: [PATCH 0396/1350] REVIEWED: sinfl, fix #3349 --- src/external/sinfl.h | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/src/external/sinfl.h b/src/external/sinfl.h index 8979fcd7f..03f0b1536 100644 --- a/src/external/sinfl.h +++ b/src/external/sinfl.h @@ -5,6 +5,8 @@ which implements the Deflate (RFC 1951) compressed data format specification sta It is mainly tuned to get as much speed and compression ratio from as little code as needed to keep the implementation as concise as possible. +@raysan5: this file has been reviewed as per https://github.com/raysan5/raylib/issues/3349 + ## Features - Portable single header and source file duo written in ANSI C (ISO C90) - Dual license with either MIT or public domain @@ -122,6 +124,7 @@ extern "C" { struct sinfl { const unsigned char *bitptr; + const unsigned char *bitend; // @raysan5, added unsigned long long bitbuf; int bitcnt; @@ -177,17 +180,23 @@ sinfl_bsr(unsigned n) { return 31 - __builtin_clz(n); #endif } -static unsigned long long -sinfl_read64(const void *p) { - unsigned long long n; - memcpy(&n, p, 8); - return n; -} +// @raysan5, commented +//static unsigned long long +//sinfl_read64(const void *p) { +// unsigned long long n; +// memcpy(&n, p, 8); +// return n; +//} static void sinfl_copy64(unsigned char **dst, unsigned char **src) { - unsigned long long n; - memcpy(&n, *src, 8); - memcpy(*dst, &n, 8); + // @raysan5, reviewed + //---------------------------- + //unsigned long long n; + //memcpy(&n, *src, 8); + //memcpy(*dst, &n, 8); + memcpy(*dst, *src, 8); + //---------------------------- + *dst += 8, *src += 8; } static unsigned char* @@ -210,7 +219,14 @@ sinfl_copy128(unsigned char **dst, unsigned char **src) { #endif static void sinfl_refill(struct sinfl *s) { - s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; + // @raysan5, reviewed + //--------------------------------------------------- + //s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; + unsigned long long n = 0; + memcpy(&n, p, s->bitptr + 8 < s->bitend ? 8 : s->bitend - s->bitptr); + s->bitbuf |= n << s->bitcnt; + //--------------------------------------------------- + s->bitptr += (63 - s->bitcnt) >> 3; s->bitcnt |= 56; /* bitcount in range [56,63] */ } @@ -384,6 +400,8 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) int last = 0; s.bitptr = in; + s.bitend = e; // @raysan5, added + while (1) { switch (state) { case hdr: { From da9bc564d2534ac447b0a22761e38ed2fd3e717b Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 10:31:35 +0200 Subject: [PATCH 0397/1350] Update sinfl.h --- src/external/sinfl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/external/sinfl.h b/src/external/sinfl.h index 03f0b1536..fd9668dcc 100644 --- a/src/external/sinfl.h +++ b/src/external/sinfl.h @@ -223,7 +223,7 @@ sinfl_refill(struct sinfl *s) { //--------------------------------------------------- //s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; unsigned long long n = 0; - memcpy(&n, p, s->bitptr + 8 < s->bitend ? 8 : s->bitend - s->bitptr); + memcpy(&n, s->bitptr, s->bitptr + 8 < s->bitend ? 8 : s->bitend - s->bitptr); s->bitbuf |= n << s->bitcnt; //--------------------------------------------------- From ea325c54e89743432eef7b22e724515b53501767 Mon Sep 17 00:00:00 2001 From: Keith Stellyes Date: Sun, 22 Oct 2023 05:58:35 -0700 Subject: [PATCH 0398/1350] fix examples Makefile to use Makefile.Web when building for web (#3449) Co-authored-by: Keith Stellyes --- examples/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/Makefile b/examples/Makefile index 74b3b874d..136a0ab47 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -156,7 +156,7 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID) MAKE = mingw32-make endif ifeq ($(PLATFORM),PLATFORM_WEB) - MAKE = mingw32-make + MAKE = emmake make endif # Define compiler flags: CFLAGS @@ -533,6 +533,8 @@ others: $(OTHERS) %: %.c ifeq ($(PLATFORM),PLATFORM_ANDROID) $(MAKE) -f Makefile.Android PROJECT_NAME=$@ PROJECT_SOURCE_FILES=$< +else ifeq ($(PLATFORM),PLATFORM_WEB) + $(MAKE) -f Makefile.Web $@ else $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) endif From 2b90b5600c28a6478eff7c2114eb8f7bdb6600dd Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 15:15:56 +0200 Subject: [PATCH 0399/1350] Revert "Update sinfl.h" This reverts commit da9bc564d2534ac447b0a22761e38ed2fd3e717b. --- src/external/sinfl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/external/sinfl.h b/src/external/sinfl.h index fd9668dcc..03f0b1536 100644 --- a/src/external/sinfl.h +++ b/src/external/sinfl.h @@ -223,7 +223,7 @@ sinfl_refill(struct sinfl *s) { //--------------------------------------------------- //s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; unsigned long long n = 0; - memcpy(&n, s->bitptr, s->bitptr + 8 < s->bitend ? 8 : s->bitend - s->bitptr); + memcpy(&n, p, s->bitptr + 8 < s->bitend ? 8 : s->bitend - s->bitptr); s->bitbuf |= n << s->bitcnt; //--------------------------------------------------- From 0e029f719b4a4afb8fee4438cf011e26fa8bdc15 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 15:17:35 +0200 Subject: [PATCH 0400/1350] Revert "REVIEWED: sinfl, fix #3349" This reverts commit c4fb6c8517d8480afffafdff81155dd35a600f73. --- src/external/sinfl.h | 38 ++++++++++---------------------------- 1 file changed, 10 insertions(+), 28 deletions(-) diff --git a/src/external/sinfl.h b/src/external/sinfl.h index 03f0b1536..8979fcd7f 100644 --- a/src/external/sinfl.h +++ b/src/external/sinfl.h @@ -5,8 +5,6 @@ which implements the Deflate (RFC 1951) compressed data format specification sta It is mainly tuned to get as much speed and compression ratio from as little code as needed to keep the implementation as concise as possible. -@raysan5: this file has been reviewed as per https://github.com/raysan5/raylib/issues/3349 - ## Features - Portable single header and source file duo written in ANSI C (ISO C90) - Dual license with either MIT or public domain @@ -124,7 +122,6 @@ extern "C" { struct sinfl { const unsigned char *bitptr; - const unsigned char *bitend; // @raysan5, added unsigned long long bitbuf; int bitcnt; @@ -180,23 +177,17 @@ sinfl_bsr(unsigned n) { return 31 - __builtin_clz(n); #endif } -// @raysan5, commented -//static unsigned long long -//sinfl_read64(const void *p) { -// unsigned long long n; -// memcpy(&n, p, 8); -// return n; -//} +static unsigned long long +sinfl_read64(const void *p) { + unsigned long long n; + memcpy(&n, p, 8); + return n; +} static void sinfl_copy64(unsigned char **dst, unsigned char **src) { - // @raysan5, reviewed - //---------------------------- - //unsigned long long n; - //memcpy(&n, *src, 8); - //memcpy(*dst, &n, 8); - memcpy(*dst, *src, 8); - //---------------------------- - + unsigned long long n; + memcpy(&n, *src, 8); + memcpy(*dst, &n, 8); *dst += 8, *src += 8; } static unsigned char* @@ -219,14 +210,7 @@ sinfl_copy128(unsigned char **dst, unsigned char **src) { #endif static void sinfl_refill(struct sinfl *s) { - // @raysan5, reviewed - //--------------------------------------------------- - //s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; - unsigned long long n = 0; - memcpy(&n, p, s->bitptr + 8 < s->bitend ? 8 : s->bitend - s->bitptr); - s->bitbuf |= n << s->bitcnt; - //--------------------------------------------------- - + s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; s->bitptr += (63 - s->bitcnt) >> 3; s->bitcnt |= 56; /* bitcount in range [56,63] */ } @@ -400,8 +384,6 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) int last = 0; s.bitptr = in; - s.bitend = e; // @raysan5, added - while (1) { switch (state) { case hdr: { From cdb394fac619c5573b3c4328a5e1a9652d4186ff Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 15:21:25 +0200 Subject: [PATCH 0401/1350] Update CHANGELOG for **raylib 5.0** -WIP- --- CHANGELOG | 197 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 197 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 43ab3ab44..cad709bef 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,203 @@ changelog Current Release: raylib 4.5.0 (18 March 2023) +------------------------------------------------------------------------- +Release: raylib 5.0 (xx November 2023) +------------------------------------------------------------------------- +KEY CHANGES: + - REDESIGNED: rcore module split per-platform, by @ubkp, @michaelfiber, @Bigfoot71, @raysan5 + - REMOVED: Deprecated platform: PLATFORM_RPI, use PLATFORM_DRM instead + - ADDED: New platform backend supported: SDL + - ADDED: New platform backend supported: Nintendo Switch (closed source) + - REDESIGNED: Automation Events System (exposed API) + +Detailed changes: +[rcore] ADDED: RAYLIB_VERSION_* values to raylib.h (#2856) by @RobLoach +[rcore] ADDED: IsKeyPressedRepeat() on PLATFORM_DESKTOP (#3245) by @actondev +[rcore] ADDED: SetWindowTitle() for PLATFORM_WEB (#3222) by @VitusVeit +[rcore] ADDED: FLAG_WINDOW_RESIZABLE for web (#3305) by @Peter0x44 +[rcore] ADDED: SetWindowMaxSize() for desktop and web (#3309) by @ubkp +[rcore] REMOVED: PLATFORM_RPI (#3232) by @michaelfiber +[rcore] REVIEWED: GetFileLength(), added comment #3262 by @raysan5 +[rcore] REVIEWED: Default shaders precission issue on PLATFORM_WEB (#3261) by @branc116 +[rcore] REVIEWED: IsKey*() key validation checks (#3256) by @n77y +[rcore] REVIEWED: SetClipboardText() for PLATFORM_WEB (#3257) by @ubkp +[rcore] REVIEWED: Check if Ctrl modifier is among the currently set modifiers (#3230) by @mohad12211 +[rcore] REVIEWED: Android app black screen when reopening by @Bigfoot71 +[rcore] REVIEWED: Warnings when casting int to floats (#3218) by @JeffM2501 +[rcore] REVIEWED: GetCurrentMonitor() detection inconsistency issue (#3215) by @ubkp +[rcore] REVIEWED: SetWindowMonitor() to no longer force fullscreen (#3209) by @ubkp +[rcore] REVIEWED: Fix mouse wheel not working in PLATFORM_RPI or PLATFORM_DRM (#3193) by @ubkp +[rcore] REVIEWED: GetMonitorName() description (#3184) (#3189) by @danilwhale +[rcore] REVIEWED: Window flags order (#3114) by @lesleyrs +[rcore] REVIEWED: Full movement for right analog stick (#3095) by @PixelPhobicGames +[rcore] REVIEWED: Fix Android app freeze after calling CloseWindow() (#3067) by @Bigfoot71 +[rcore] REVIEWED: Lazy loading of default font used on image drawing (no InitWindow) by @raysan5 +[rcore] REVIEWED: Minor tweaks to raylib events automation system @raysan5 +[rcore] REVIEWED: GetCurrentMonitor() bugfix (#3058) by @hamyyy +[rcore] REVIEWED: Update CORE.Input.Touch.pointCount #3024 by @raysan5 +[rcore] REVIEWED: Mouse offset and scaling must be considered also on web! +[rcore] REVIEWED: CompressData(), possible stack overflow +[rcore] REVIEWED: GetWorldToScreenEx() (#3351) by @Brian-ED +[rlgl] ADDED: Experimental support for OpenGL ES 3.0 by @raysan5 +[rlgl] ADDED: Support 16-Bit HDR textures (#3220) by @Not-Nik +[rlgl] REVIEWED: Improved support for ES3/WebGL2 (#3107) by @chemaguerra +[rlgl] REVIEWED: OpenGL 2.1 half floats support as part of an extension by @Not-Nik +[rlgl] REVIEWED: Avoid shader attribute not found log by @raysan5 +[rlgl] REVIEWED: Avoid tracelog about not found uniforms #3003 by @raysan5 +[rlgl] REVIEWED: rLoadTexture() UBSAN complaints #1891 (#3321) by @Codom +[rlgl] REVIEWED: glInternalFormat as unsigned int +[rshapes] ADDED: Spline drawing functions by @raysan5 +[rshapes] REVIEWED: DrawLineCatmullRom() by @raysan5 +[rshapes] REVIEWED: Minor fix in DrawLineBezier* (#3006) by @eternalStudent +[rshapes] REVIEWED: GetCollisionRec(), more performant (#3052) by @manuel5975p +[rshapes] REVIEWED: Fix off-by-one error in CheckCollisionPointRec() (#3022) by @dbechrd +[rtextures] ADDED: Basic SVG loading support (#2738) by @bXi +[rtextures] ADDED: Support 16-Bit HDR textures (#3220) by @Not-Nik +[rtextures] ADDED: ExportImageToMemory() by @raysan5 +[rtextures] ADDED: ImageRotate() (#3078) by @danemadsen +[rtextures] ADDED: GenImageGradientSquare() (#3077) by @danemadsen +[rtextures] ADDED: GenImageLinearGradient() by @danemadsen +[rtextures] REMOVED: GenImageGradientH() and GenImageGradientV() by @danemadsen +[rtextures] REVIEWED: LoadImageSvg() by @raysan5 +[rtextures] REVIEWED: Uninitialized thread-locals in stbi #3282 (#3283) by @jbarthelmes +[rtextures] REVIEWED: ImageDrawRectangleRec(), validate drawing inside bounds by @JeffM2501 +[rtextures] REVIEWED: LoadTextureCubemap() for manual layouts (#3204) by @Not-Nik +[rtextures] REVIEWED: Optimization of ImageDrawRectangleRec() (#3185) by @smalltimewizard +[rtextures] REVIEWED: ImageRotate() formatting by @raysan5 +[rtextures] REVIEWED: GenImagePerlinNoise(), clamp values #3071 by @raysan5 +[rtextures] REVIEWED: Packing logic error in GenImageFontAtlas() (#2979) by @hanaxar +[rtextures] REVIEWED: Calculate exact image size in GenImageFontAtlas() (#2963) by @hanaxar +[rtextures] REVIEWED: ImageDrawRectangleRec() #3027 by @raysan5 +[rtextures] REVIEWED: ImageDraw() source clipping when drawing beyond top left (#3306) by @RobLoach +[rtextures] REVIEWED: UnloadRenderTexture(), additional checks +[rtext] ADDED: Font altas white rectangle and flag SUPPORT_FONT_ATLAS_WHITE_REC by @raysan5 +[rtext] ADDED: SetTextLineSpacing() to define line breaks text drawing spacing by @raysan5 +[rtext] RENAMED: LoadFont*() parameter names for consistency and coherence by @raysan5 +[rtext] REVIEWED: GetCodepointCount(), ignore unused return value of GetCodepointNext by @ashn-dot-dev +[rtext] REVIEWED: TextFormat() warn user if buffer overflow occured. (#3399) by @Murlocohol +[rtext] REVIEWED: GetGlyphIndex() #3000 by @raysan5 +[rtext] REVIEWED: GetCodepointNext() to return default value on invalid inp… by @chocolate42 +[rtext] REVIEWED: TextToPascal() issue when first char is uppercase +[rmodels] ADDED: ModelAnimation.name field, initially with GLTF animation names loaded (… by @alfredbaudisch +[rmodels] REVIEWED: Support .vox model files version 200 (#3097) by @Bigfoot71 +[rmodels] REVIEWED: Materials loading #3126 @raysan5 +[rmodels] REVIEWED: DrawBillboardPro() to allow source of negative size (#3197) by @bohonghuang +[rmodels] REVIEWED: glTF loading segfault in animNormals memcpy by @charles-l +[rmodels] REVIEWED: LoadModelAnimationsGLTF(), free fileData after use (#3065) by @crynux +[rmodels] REVIEWED: GenMeshCubicmap(), correction of values (#3032) by @Bigfoot71 +[rmodels] REVIEWED: DrawMesh() to avoid UBSAN complaining (#1891) +[raudio] ADDED: LoadSoundAlias() by @JeffM2501 +[raudio] ADDED: Missing structure on standalone mode #3160 by @raysan5 +[raudio] REVIEWED: Comments about sample format to AttachAudioStreamProcessor() (#3188) by @AlbertoGP +[raudio] REVIEWED: Documented buffer format for audio processors (#3186) by @AlbertoGP +[raudio] REVIEWED: ExportWaveAsCode() file saving by @RadsammyT +[raudio] REVIEWED: Fix warning on discarded const qualifier (#2967) by @RobLoach +[raudio] REVIEWED: Move mutex initialization before ma_device_start() (#3325) by @Bigfoot71 +[rcamera] REVIEWED: File-macros for consistency #3161 by @raysan5 +[rcamera] REVIEWED: Support analog stick camera controls (#3066) by @PixelPhobicGames +[rcamera] REVIEWED: CameraMoveToTarget(), ensure distance is greater than 0 (#3031) by @kolunmi +[rcamera] REVIEWED: Exposing rcamera functions to the dll (#3355) by @JeffM2501 +[raymath] ADDED: Vector3Projection() and Vector3Rejection() (#3263) by @Dial0 +[raymath] ADDED: EPSILON macro to each function requiring it (#3330) by @Brian-ED +[raymath] REVIEWED: Usage of 'sinf()' and 'cosf()' to be correct (#3181) by @RokasPuzonas +[raymath] REVIEWED: Slightly optimized Vector3Normalize() (#2982) by @RicoP +[raymath] REVIEWED: Comment to clarify raymath semantics by @raysan5 +[raymath] REVIEWED: Comment about Matrix conventions by @raysan5 +[raymath] REVIEWED: Vector2Angle() and Vector2LineAngle() (#3396) by @Murlocohol +[rgestures] REVIEWED: Optimize and simplify the gesture system (#3190) by @ubkp +[rgestures] REVIEWED: GESTURE_DRAG and GESTURE_SWIPE_* issues (mostly) for web (#3183) by @ubkp +[rgestures] REVIEWED: Touch pointCount for web (#3163) by @ubkp +[rgestures] REVIEWED: IsGestureDetected() parameter type +[utils] ADDED: Security checks to file reading (memory allocations) by @raysan5 +[utils] REVIEWED: LoadFileData() potential issues with dataSize +[examples] ADDED: shaders_lightmap (#3043) by @nullstare +[examples] ADDED: core_2d_camera_split_screen (#3298) by @gabrielssanches +[examples] ADDED: LoadSoundAlias() usage example (#3223) by @JeffM2501 +[examples] ADDED: textures_tiling (#3353) by @luis605 +[examples] RENAMED: 2d_camera examples for consistency +[examples] REVIEWED: Text examples SetTextLineSpacing() to multiline examples by @raysan5 +[examples] REVIEWED: examples/shapes/shapes_collision_area.c help instructions (#3279) by @asdqwe +[examples] REVIEWED: examples/shaders/shaders_texture_outline.c help instructions (#3278) by @asdqwe +[examples] REVIEWED: examples/others/easings_testbed.c help instructions and small twe… by @asdqwe +[examples] REVIEWED: example/audio/audio_module_player.c help instructions and small b… by @asdqwe +[examples] REVIEWED: example/models/models_loading_m3d.c controls (#3269) by @asdqwe +[examples] REVIEWED: example/models/models_loading_gltf.c controls (#3268) by @asdqwe +[examples] REVIEWED: text_unicode.c example crashing (#3250) by @ubkp +[examples] REVIEWED: rlgl_standalone.c compilation issue (#3242) by @ubkp +[examples] REVIEWED: core_input_gestures for Web (#3172) by @ubkp +[examples] REVIEWED: core_input_gamepad (#3110) by @iacore +[examples] REVIEWED: examples using raygui to raygui 4.0 by @raysan5 +[build] ADDED: CMake option for SUPPORT_CUSTOM_FRAME_CONTROL (#3221) by @ubkp +[build] ADDED: New BORDERLESS_WINDOWED_MODE for PLATFORM_DESKTOP (#3216) by @ubkp +[build] REVIEWED: Fix CMake extraneous -lglfw (#3266) by @iacore +[build] REVIEWED: Add missing cmake options (#3267) by @asdqwe +[build] REVIEWED: Match CMakeOptions.txt options default values (#3258) by @asdqwe +[build] REVIEWED: Add build.zig options for individual modules (#3254) by @actondev +[build] REVIEWED: build.zig to work with cross-compiling (#3225) by @yujiri8 +[build] REVIEWED: Makefile build on PLATFORM_ANDROID, soname (#3211) by @ndytts +[build] REVIEWED: src/Makefile, fix misleading indentation (#3202) by @ashn-dot-dev +[build] REVIEWED: build.zig: Support for building with PLAFORM_DRM (#3191) by @jakubvf +[build] REVIEWED: Update CMakeOptions.txt by @raysan5 +[build] REVIEWED: fix: cmake option "OPENGL_VERSION" doesn't work (#3170) by @royqh1979 +[build] REVIEWED: Add error if raylib.h is included in a C++98 program (#3093) by @Peter0x44 +[build] REVIEWED: Cross compilation for PLATFORM_DRM (#3091) by @TheLastBilly +[build] REVIEWED: build.zigm fixed cross-compiling from Linux (#3090)by @yujiri8 +[build] REVIEWED: Enhanced cmake part for OpenBSD (#3086) by @rayit +[build] REVIEWED: Fixed compile on OpenBSD (#3085)by @rayit +[build] REVIEWED: CMake project example: fix a couple of typos (#3014) by @benjamin-thomas +[build] REVIEWED: Fix warnings in raylib for MSVC (#3004) by @JeffM2501 +[build] REVIEWED: Update cmake example project (#3062) by @lesleyrs +[build] REVIEWED: Update build.zig be be able to build with current zig master (#3064) by @ryupold +[build] REVIEWED: VSCode project template (#3048) by @Shoozza +[build] REVIEWED: Fixed broken build.zig files. Now works with latest stable compiler (… by @Gamer-Kold +[build] REVIEWED: Fix missing symbol when rglfw.c on BSD platforms (#2968) by @Koromix +[build] REVIEWED: Update Makefile comment to indicate arm64 as a supported Linux deskto… @ashn-dot-dev +[build] REVIEWED: Update Makefile : clean raygui.c & physac.c (#3296) by @SuperUserNameMan +[build] REVIEWED: Update webassembly.yml and linux.yml +[build] REVIEWED: Update zig build system to zig version 0.11.0 (#3393) by @purple4pur +[build] REVIEWED: Fix for latest zig master (#3037) by @star-tek-mb +[bindings] ADDED: fortran-raylib +[bindings] ADDED: raylib-raku to bindings (#3299) by @vushu +[bindings] ADDED: claw-raylib to BINDINGS.md (#3310) by @bohonghuang +[bindings] ADDED: vaiorabbit/raylib-bindings (#3318) by @wilsonsilva +[bindings] ADDED: TurboRaylib (#3317) by @turborium +[bindings] ADDED: raylib-ffi to bindings list (#3164) by @ewpratten +[bindings] ADDED: raylib-pkpy-bindings (#3361) by @blueloveTH +[bindings] UPDATED: BINDINGS.md (#3217) by @joseph-montanez +[bindings] UPDATED: BINDINGS.md to include rayjs (#3212) by @mode777 +[bindings] UPDATED: latest h-raylib version (#3166) by @Anut-py +[bindings] UPDATED: bindbd-raylib3 to raylib 4.5 (#3157) by @o3o +[bindings] UPDATED: Janet bindings supported version update (#3083)by @archydragon +[bindings] UPDATED: BINDINGS.md (raylib-py -> 4.5) (#2992) by @overdev +[bindings] UPDATED: BINDINGS.md (raylib-lua -> 4.5) (#2989) by @TSnake41 +[bindings] UPDATED: raylib-d binding version to 4.5 (#2988) by @schveiguy +[bindings] UPDATED: raylib-freebasic to 4.5 (#2986) by @WIITD +[bindings] UPDATED: BINDINGS.md (#2983) by @jarroddavis68 +[bindings] UPDATED: BINDINGS.md for raylib Odin 4.5 (#2981) by @gingerBill +[bindings] UPDATED: BINDINGS.md (#2980) by @GuvaCode +[bindings] UPDATED: BINDINGS.md (#3002) by @fubark +[bindings] UPDATED: BINDINGS.md (#3053) by @JupiterRider +[bindings] UPDATED: BINDINGS.md (#3050) by @Its-Kenta +[bindings] UPDATED: CL bindings version (#3049) by @shelvick +[bindings] UPDATED: BINDINGS.md (#3026) by @ChrisDill +[bindings] UPDATED: BINDINGS.md (#3023) by @sDos280 +[bindings] UPDATED: BINDINGS.md (#3017) by @Soutaisei +[bindings] UPDATED: Various versions to 4.5 (#2974) by @RobLoach +[bindings] UPDATED: raylib.zig version to 4.5 (#2971) by @ryupold +[bindings] UPDATED: h-raylib version (#2970) by @Anut-py +[bindings] UPDATED: Factor's raylib binding to v4.5 (#3350) by @WraithGlade +[bindings] UPDATED: raylib-ocaml bindings to 4.5 version (#3322) by @tjammer +[external] UPDATED: sdefl and sinfl DEFLATE compression libraries by @raysan5 +[external] UPDATED: miniaudio v0.11.12 --> v0.11.18 by @raysan5 +[external] UPDATED: rl_gputex.h compressed images loading library by @raysan5 +[external] REVIEWED: msf_gif.h, some warnings +[misc] ADDED: New task point to issue template about checking the wiki (#3169) by @ubkp +[misc] REVIEWED: Update FAQ.md by @raysan5 +[misc] REVIEWED: Fix a link in the FAQ (#3082)by @jasonliang-dev +[misc] REVIEWED: New file formats to FAQ (#3079) by @Luramoth +[misc] REVIEWED: Make assets loading extension case insensitive #3008 by @raysan5 + ------------------------------------------------------------------------- Release: raylib 4.5 (18 March 2023) ------------------------------------------------------------------------- From f0124df0e8bbeb3e9ad2acf08b0f3610e812c8d6 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 22 Oct 2023 15:34:24 +0200 Subject: [PATCH 0402/1350] Update CHANGELOG --- CHANGELOG | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index cad709bef..1da04cc73 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -4,7 +4,7 @@ changelog Current Release: raylib 4.5.0 (18 March 2023) ------------------------------------------------------------------------- -Release: raylib 5.0 (xx November 2023) +Release: raylib 5.0 (xx November 2023) -WIP- ------------------------------------------------------------------------- KEY CHANGES: - REDESIGNED: rcore module split per-platform, by @ubkp, @michaelfiber, @Bigfoot71, @raysan5 @@ -19,8 +19,9 @@ Detailed changes: [rcore] ADDED: SetWindowTitle() for PLATFORM_WEB (#3222) by @VitusVeit [rcore] ADDED: FLAG_WINDOW_RESIZABLE for web (#3305) by @Peter0x44 [rcore] ADDED: SetWindowMaxSize() for desktop and web (#3309) by @ubkp +[rcore] ADDED: SetMouseCursor() for PLATFORM_WEB (#3414) by @BeardedBread [rcore] REMOVED: PLATFORM_RPI (#3232) by @michaelfiber -[rcore] REVIEWED: GetFileLength(), added comment #3262 by @raysan5 +[rcore] REVIEWED: GetFileLength(), added comment (#3262) by @raysan5 [rcore] REVIEWED: Default shaders precission issue on PLATFORM_WEB (#3261) by @branc116 [rcore] REVIEWED: IsKey*() key validation checks (#3256) by @n77y [rcore] REVIEWED: SetClipboardText() for PLATFORM_WEB (#3257) by @ubkp @@ -37,16 +38,17 @@ Detailed changes: [rcore] REVIEWED: Lazy loading of default font used on image drawing (no InitWindow) by @raysan5 [rcore] REVIEWED: Minor tweaks to raylib events automation system @raysan5 [rcore] REVIEWED: GetCurrentMonitor() bugfix (#3058) by @hamyyy -[rcore] REVIEWED: Update CORE.Input.Touch.pointCount #3024 by @raysan5 +[rcore] REVIEWED: Update CORE.Input.Touch.pointCount (#3024) by @raysan5 [rcore] REVIEWED: Mouse offset and scaling must be considered also on web! [rcore] REVIEWED: CompressData(), possible stack overflow [rcore] REVIEWED: GetWorldToScreenEx() (#3351) by @Brian-ED +[rcore] REVIEWED: Fix GetMouseDelta() issue for Android (#3404) by @Bigfoot71 [rlgl] ADDED: Experimental support for OpenGL ES 3.0 by @raysan5 [rlgl] ADDED: Support 16-Bit HDR textures (#3220) by @Not-Nik [rlgl] REVIEWED: Improved support for ES3/WebGL2 (#3107) by @chemaguerra [rlgl] REVIEWED: OpenGL 2.1 half floats support as part of an extension by @Not-Nik [rlgl] REVIEWED: Avoid shader attribute not found log by @raysan5 -[rlgl] REVIEWED: Avoid tracelog about not found uniforms #3003 by @raysan5 +[rlgl] REVIEWED: Avoid tracelog about not found uniforms (#3003) by @raysan5 [rlgl] REVIEWED: rLoadTexture() UBSAN complaints #1891 (#3321) by @Codom [rlgl] REVIEWED: glInternalFormat as unsigned int [rshapes] ADDED: Spline drawing functions by @raysan5 @@ -62,41 +64,45 @@ Detailed changes: [rtextures] ADDED: GenImageLinearGradient() by @danemadsen [rtextures] REMOVED: GenImageGradientH() and GenImageGradientV() by @danemadsen [rtextures] REVIEWED: LoadImageSvg() by @raysan5 -[rtextures] REVIEWED: Uninitialized thread-locals in stbi #3282 (#3283) by @jbarthelmes +[rtextures] REVIEWED: Uninitialized thread-locals in stbi (#3282) (#3283) by @jbarthelmes [rtextures] REVIEWED: ImageDrawRectangleRec(), validate drawing inside bounds by @JeffM2501 [rtextures] REVIEWED: LoadTextureCubemap() for manual layouts (#3204) by @Not-Nik [rtextures] REVIEWED: Optimization of ImageDrawRectangleRec() (#3185) by @smalltimewizard [rtextures] REVIEWED: ImageRotate() formatting by @raysan5 -[rtextures] REVIEWED: GenImagePerlinNoise(), clamp values #3071 by @raysan5 +[rtextures] REVIEWED: GenImagePerlinNoise(), clamp values (#3071) by @raysan5 [rtextures] REVIEWED: Packing logic error in GenImageFontAtlas() (#2979) by @hanaxar [rtextures] REVIEWED: Calculate exact image size in GenImageFontAtlas() (#2963) by @hanaxar -[rtextures] REVIEWED: ImageDrawRectangleRec() #3027 by @raysan5 +[rtextures] REVIEWED: ImageDrawRectangleRec() (#3027) by @raysan5 [rtextures] REVIEWED: ImageDraw() source clipping when drawing beyond top left (#3306) by @RobLoach [rtextures] REVIEWED: UnloadRenderTexture(), additional checks [rtext] ADDED: Font altas white rectangle and flag SUPPORT_FONT_ATLAS_WHITE_REC by @raysan5 [rtext] ADDED: SetTextLineSpacing() to define line breaks text drawing spacing by @raysan5 [rtext] RENAMED: LoadFont*() parameter names for consistency and coherence by @raysan5 [rtext] REVIEWED: GetCodepointCount(), ignore unused return value of GetCodepointNext by @ashn-dot-dev -[rtext] REVIEWED: TextFormat() warn user if buffer overflow occured. (#3399) by @Murlocohol -[rtext] REVIEWED: GetGlyphIndex() #3000 by @raysan5 +[rtext] REVIEWED: TextFormat() warn user if buffer overflow occured (#3399) by @Murlocohol +[rtext] REVIEWED: TextFormat(), added "..." for truncation (#3366) by @raysan5 +[rtext] REVIEWED: GetGlyphIndex() (#3000) by @raysan5 [rtext] REVIEWED: GetCodepointNext() to return default value on invalid inp… by @chocolate42 [rtext] REVIEWED: TextToPascal() issue when first char is uppercase [rmodels] ADDED: ModelAnimation.name field, initially with GLTF animation names loaded (… by @alfredbaudisch [rmodels] REVIEWED: Support .vox model files version 200 (#3097) by @Bigfoot71 -[rmodels] REVIEWED: Materials loading #3126 @raysan5 +[rmodels] REVIEWED: Materials loading (#3126) @raysan5 [rmodels] REVIEWED: DrawBillboardPro() to allow source of negative size (#3197) by @bohonghuang [rmodels] REVIEWED: glTF loading segfault in animNormals memcpy by @charles-l [rmodels] REVIEWED: LoadModelAnimationsGLTF(), free fileData after use (#3065) by @crynux [rmodels] REVIEWED: GenMeshCubicmap(), correction of values (#3032) by @Bigfoot71 [rmodels] REVIEWED: DrawMesh() to avoid UBSAN complaining (#1891) +[rmodels] REVIEWED: GenMeshPlane() when resX != resZ (#3425) by @neyrox, @s-yablonskiy [raudio] ADDED: LoadSoundAlias() by @JeffM2501 -[raudio] ADDED: Missing structure on standalone mode #3160 by @raysan5 +[raudio] ADDED: Missing structure on standalone mode (#3160) by @raysan5 +[raudio] ADDED: GetMasterVolume() (#3434) by @rexim [raudio] REVIEWED: Comments about sample format to AttachAudioStreamProcessor() (#3188) by @AlbertoGP [raudio] REVIEWED: Documented buffer format for audio processors (#3186) by @AlbertoGP [raudio] REVIEWED: ExportWaveAsCode() file saving by @RadsammyT [raudio] REVIEWED: Fix warning on discarded const qualifier (#2967) by @RobLoach [raudio] REVIEWED: Move mutex initialization before ma_device_start() (#3325) by @Bigfoot71 -[rcamera] REVIEWED: File-macros for consistency #3161 by @raysan5 +[raudio] REVIEWED: Fix UpdateSound() parameter name (#3405) by @KislyjKisel +[rcamera] REVIEWED: File-macros for consistency (#3161) by @raysan5 [rcamera] REVIEWED: Support analog stick camera controls (#3066) by @PixelPhobicGames [rcamera] REVIEWED: CameraMoveToTarget(), ensure distance is greater than 0 (#3031) by @kolunmi [rcamera] REVIEWED: Exposing rcamera functions to the dll (#3355) by @JeffM2501 @@ -159,6 +165,7 @@ Detailed changes: [build] REVIEWED: Update webassembly.yml and linux.yml [build] REVIEWED: Update zig build system to zig version 0.11.0 (#3393) by @purple4pur [build] REVIEWED: Fix for latest zig master (#3037) by @star-tek-mb +[build] REVIEWED: Examples Makefile to use Makefile.Web when building for web (#3449) by @keithstellyes [bindings] ADDED: fortran-raylib [bindings] ADDED: raylib-raku to bindings (#3299) by @vushu [bindings] ADDED: claw-raylib to BINDINGS.md (#3310) by @bohonghuang @@ -166,6 +173,7 @@ Detailed changes: [bindings] ADDED: TurboRaylib (#3317) by @turborium [bindings] ADDED: raylib-ffi to bindings list (#3164) by @ewpratten [bindings] ADDED: raylib-pkpy-bindings (#3361) by @blueloveTH +[bindings] ADDED: Raylib.lean to BINDINGS.md (#3409) by @KislyjKisel [bindings] UPDATED: BINDINGS.md (#3217) by @joseph-montanez [bindings] UPDATED: BINDINGS.md to include rayjs (#3212) by @mode777 [bindings] UPDATED: latest h-raylib version (#3166) by @Anut-py @@ -193,6 +201,7 @@ Detailed changes: [external] UPDATED: sdefl and sinfl DEFLATE compression libraries by @raysan5 [external] UPDATED: miniaudio v0.11.12 --> v0.11.18 by @raysan5 [external] UPDATED: rl_gputex.h compressed images loading library by @raysan5 +[external] UPDATED: Replaced stb_image_resize.c by stb_image_resize2.h (#3403) by @BabakSamimi [external] REVIEWED: msf_gif.h, some warnings [misc] ADDED: New task point to issue template about checking the wiki (#3169) by @ubkp [misc] REVIEWED: Update FAQ.md by @raysan5 From e33e9da277865207123158430ebf42cc5626e5b7 Mon Sep 17 00:00:00 2001 From: Peter0x44 Date: Sun, 22 Oct 2023 16:13:49 +0100 Subject: [PATCH 0403/1350] Add DrawCircleLinesV for consistency (#3452) ImageDrawCircleLinesV already existed, so I'm not sure why this was missing. It is trivial to implement, anyway --- src/raylib.h | 1 + src/rshapes.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index e701f0454..b172562d0 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1207,6 +1207,7 @@ RLAPI void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, RLAPI void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Color color2); // Draw a gradient-filled circle RLAPI void DrawCircleV(Vector2 center, float radius, Color color); // Draw a color-filled circle (Vector version) RLAPI void DrawCircleLines(int centerX, int centerY, float radius, Color color); // Draw circle outline +RLAPI void DrawCircleLinesV(Vector2 center, float radius, Color color); // Draw circle outline (Vector version) RLAPI void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse RLAPI void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Color color); // Draw ellipse outline RLAPI void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startAngle, float endAngle, int segments, Color color); // Draw ring diff --git a/src/rshapes.c b/src/rshapes.c index f3061f8b3..de64f1593 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -670,6 +670,12 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co // Draw circle outline void DrawCircleLines(int centerX, int centerY, float radius, Color color) +{ + DrawCircleLinesV((Vector2){ (float)centerX, (float)centerY }, radius, color); +} + +// Draw circle outline (Vector version) +void DrawCircleLinesV(Vector2 center, float radius, Color color) { rlBegin(RL_LINES); rlColor4ub(color.r, color.g, color.b, color.a); @@ -677,8 +683,8 @@ void DrawCircleLines(int centerX, int centerY, float radius, Color color) // NOTE: Circle outline is drawn pixel by pixel every degree (0 to 360) for (int i = 0; i < 360; i += 10) { - rlVertex2f(centerX + cosf(DEG2RAD*i)*radius, centerY + sinf(DEG2RAD*i)*radius); - rlVertex2f(centerX + cosf(DEG2RAD*(i + 10))*radius, centerY + sinf(DEG2RAD*(i + 10))*radius); + rlVertex2f(center.x + cosf(DEG2RAD*i)*radius, center.y + sinf(DEG2RAD*i)*radius); + rlVertex2f(center.x + cosf(DEG2RAD*(i + 10))*radius, center.y + sinf(DEG2RAD*(i + 10))*radius); } rlEnd(); } From b3028e4891083c077236df437011184b16b6d293 Mon Sep 17 00:00:00 2001 From: Peter0x44 Date: Sun, 22 Oct 2023 18:45:49 +0100 Subject: [PATCH 0404/1350] Review prerequisites of rcore.c (#3453) rcore_desktop_sdl.c was not present in the list of prerequisites this patch changes them to use a wildcard, so any other platforms added in future will be tracked properly --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index ea8bdd5e5..c9094f096 100644 --- a/src/Makefile +++ b/src/Makefile @@ -633,7 +633,7 @@ endif # Compile all modules with their prerequisites # Prerequisites of core module -rcore.o : platforms/rcore_android.c platforms/rcore_desktop.c platforms/rcore_drm.c platforms/rcore_template.c platforms/rcore_web.c +rcore.o : platforms/*.c # Compile core module rcore.o : rcore.c raylib.h rlgl.h utils.h raymath.h rcamera.h rgestures.h From 8f517b76516c207c89cabf055fa7db768ba6d960 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Mon, 23 Oct 2023 05:05:40 -0300 Subject: [PATCH 0405/1350] Fix compilation for PLATFORM_WEB examples (#3454) --- examples/Makefile.Web | 60 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 4 deletions(-) diff --git a/examples/Makefile.Web b/examples/Makefile.Web index ac8d5af89..3512f7407 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -149,7 +149,7 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID) MAKE = mingw32-make endif ifeq ($(PLATFORM),PLATFORM_WEB) - MAKE = mingw32-make + MAKE = emmake make endif # Define compiler flags: CFLAGS @@ -268,7 +268,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) # are specified per-example for optimization # Define a custom shell .html and output extension - LDFLAGS += --shell-file $(RAYLIB_PATH)/src/shell.html + LDFLAGS += --shell-file $(RAYLIB_PATH)/src/minshell.html EXT = .html endif @@ -340,6 +340,7 @@ CORE = \ core/core_input_mouse \ core/core_input_mouse_wheel \ core/core_input_gamepad \ + core/core_input_gamepad_info \ core/core_input_multitouch \ core/core_input_gestures \ core/core_input_gestures_web \ @@ -392,6 +393,7 @@ TEXTURES = \ textures/textures_image_generation \ textures/textures_image_loading \ textures/textures_image_processing \ + textures/textures_image_rotate \ textures/textures_image_text \ textures/textures_to_image \ textures/textures_raw_data \ @@ -473,9 +475,18 @@ AUDIO = \ audio/audio_music_stream \ audio/audio_raw_stream \ audio/audio_sound_loading \ + audio/audio_sound_multi \ audio/audio_stream_effects \ audio/audio_mixed_processor +OTHERS = \ + others/easings_testbed \ + others/embedded_files_loading \ + others/raylib_opengl_interop \ + others/raymath_vector_angle \ + others/rlgl_compute_shader \ + others/rlgl_standalone + CURRENT_MAKEFILE = $(lastword $(MAKEFILE_LIST)) # Define processes to execute @@ -512,6 +523,9 @@ core/core_input_gamepad: core/core_input_gamepad.c --preload-file core/resources/ps3.png@resources/ps3.png \ --preload-file core/resources/xbox.png@resources/xbox.png +core/core_input_gamepad_info: core/core_input_gamepad.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + core/core_input_multitouch: core/core_input_multitouch.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -529,7 +543,7 @@ core/core_2d_camera_platformer: core/core_2d_camera_platformer.c core/core_2d_camera_mouse_zoom: core/core_2d_camera_mouse_zoom.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - + core/core_2d_camera_split_screen: core/core_2d_camera_split_screen.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -541,7 +555,7 @@ core/core_3d_camera_free: core/core_3d_camera_free.c core/core_3d_camera_first_person: core/core_3d_camera_first_person.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - + core/core_3d_camera_split_screen: core/core_3d_camera_split_screen.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -675,6 +689,10 @@ textures/textures_image_processing: textures/textures_image_processing.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file textures/resources/parrots.png@resources/parrots.png +textures/textures_image_rotate: textures/textures_image_rotate.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file textures/resources/raylib_logo.png + textures/textures_image_text: textures/textures_image_text.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s TOTAL_MEMORY=67108864 \ --preload-file textures/resources/parrots.png@resources/parrots.png \ @@ -740,6 +758,10 @@ textures/textures_gif_player: textures/textures_gif_player.c textures/textures_fog_of_war: textures/textures_fog_of_war.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +textures/textures_svg_loading: textures/textures_svg_loading.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file textures/resources/test.svg + # Compile TEXT examples text/text_raylib_fonts: text/text_raylib_fonts.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -963,6 +985,13 @@ shaders/shaders_hot_reloading: shaders/shaders_hot_reloading.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s FORCE_FILESYSTEM=1 \ --preload-file shaders/resources/shaders/glsl100/reload.fs@resources/shaders/glsl100/reload.fs +shaders/shaders_lightmap: shaders/shaders_lightmap.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s FORCE_FILESYSTEM=1 \ + --preload-file shaders/resources/shaders/glsl330/lightmap.vs \ + --preload-file shaders/resources/shaders/glsl330/lightmap.fs \ + --preload-file shaders/resources/cubicmap_atlas.png \ + --preload-file shaders/resources/spark_flame.png + shaders/shaders_mesh_instancing: shaders/shaders_mesh_instancing.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/shaders/glsl100/lighting_instancing.vs@resources/shaders/glsl100/lighting_instancing.vs \ @@ -1003,6 +1032,10 @@ audio/audio_sound_loading: audio/audio_sound_loading.c --preload-file audio/resources/sound.wav@resources/sound.wav \ --preload-file audio/resources/target.ogg@resources/target.ogg +audio/audio_sound_multi: audio/audio_sound_multi.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ + --preload-file audio/resources/sound.wav@resources/sound.wav + audio/audio_stream_effects: audio/audio_stream_effects.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s TOTAL_MEMORY=67108864 \ --preload-file audio/resources/country.mp3@resources/country.mp3 @@ -1012,6 +1045,25 @@ audio/audio_mixed_processor: audio/audio_mixed_processor.c --preload-file audio/resources/country.mp3@resources/country.mp3 \ --preload-file audio/resources/coin.wav@resources/coin.wav +# Compile OTHERS examples +others/easings_testbed: others/easings_testbed.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + +others/embedded_files_loading: others/embedded_files_loading.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + +others/raylib_opengl_interop: + $(info Skipping_others_raylib_opengl_interop) + +others/raymath_vector_angle: others/raymath_vector_angle.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + +others/rlgl_compute_shader: + $(info Skipping_others_rlgl_compute_shader) + +others/rlgl_standalone: + $(info Skipping_others_rlgl_standalone) + # Clean everything clean: ifeq ($(PLATFORM),PLATFORM_DESKTOP) From 4ed776368a488b242781d77753f4b11396f97ce7 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Mon, 23 Oct 2023 01:11:50 -0700 Subject: [PATCH 0406/1350] When the frame counter gets to 0, reset the FPS average counter. This allows the window to be closed and reopened with clean FPS stats. (#3445) --- src/rcore.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index e3cd08188..9bd47983a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -484,9 +484,9 @@ void InitWindow(int width, int height, const char *title) } #endif + CORE.Time.frameCounter = 0; #if defined(SUPPORT_EVENTS_AUTOMATION) events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); - CORE.Time.frameCounter = 0; #endif // Initialize random seed @@ -1396,6 +1396,16 @@ int GetFPS(void) static float average = 0, last = 0; float fpsFrame = GetFrameTime(); + // if we reset the window, reset the FPS info + if (CORE.Time.frameCounter == 0) + { + average = 0; + last = 0; + index = 0; + for (int i = 0; i < FPS_CAPTURE_FRAMES_COUNT; i++) + history[i] = 0; + } + if (fpsFrame == 0) return 0; if ((GetTime() - last) > FPS_STEP) From daf227a185808d1e37e4269cf4a5be6b690226f6 Mon Sep 17 00:00:00 2001 From: Lukas <116672956+gk646@users.noreply.github.com> Date: Mon, 23 Oct 2023 18:16:28 +0200 Subject: [PATCH 0407/1350] Fixes a memory leak as a result of creating an AudioBuffer* with the old source.frameCount. This internally allocates memory to the structs data pointer which is then later overridden by the correct sound data of the source sound. (#3458) Additionally added a volume assignment from old to new as currently there is no way to get the volume of a sound and the AudioBuffer struct is not reachable from user code due to opaque definition. --- src/raudio.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 6a1096767..dcc9f706a 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -928,7 +928,6 @@ Sound LoadSoundFromWave(Wave wave) } // Clone sound from existing sound data, clone does not own wave data -// Wave data must // NOTE: Wave data must be unallocated manually and will be shared across all clones Sound LoadSoundAlias(Sound source) { @@ -936,13 +935,16 @@ Sound LoadSoundAlias(Sound source) if (source.stream.buffer->data != NULL) { - AudioBuffer* audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, source.frameCount, AUDIO_BUFFER_USAGE_STATIC); + AudioBuffer* audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, 0, AUDIO_BUFFER_USAGE_STATIC); if (audioBuffer == NULL) { TRACELOG(LOG_WARNING, "SOUND: Failed to create buffer"); return sound; // early return to avoid dereferencing the audioBuffer null pointer } + audioBuffer->sizeInFrames = source.stream.buffer->sizeInFrames; + audioBuffer->volume = source.stream.buffer->volume; audioBuffer->data = source.stream.buffer->data; + sound.frameCount = source.frameCount; sound.stream.sampleRate = AUDIO.System.device.sampleRate; sound.stream.sampleSize = 32; @@ -953,6 +955,7 @@ Sound LoadSoundAlias(Sound source) return sound; } + // Checks if a sound is ready bool IsSoundReady(Sound sound) { From 3ff60269174d0f264c152875be4d1808b7fe0195 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 23 Oct 2023 18:32:24 +0200 Subject: [PATCH 0408/1350] REVIEWED: Move screen capture logic to `rcore.c`, available for all platforms --- src/platforms/rcore_desktop.c | 55 -------------------------------- src/platforms/rcore_web.c | 59 ----------------------------------- src/rcore.c | 40 +++++++++++++++++++++++- 3 files changed, 39 insertions(+), 115 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index 9e653dd0e..b7def6e67 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -1658,61 +1658,6 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i // Check the exit key to set close window if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE); - -#if defined(SUPPORT_SCREEN_CAPTURE) - if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) - { -#if defined(SUPPORT_GIF_RECORDING) - if (mods & GLFW_MOD_CONTROL) - { - if (gifRecording) - { - gifRecording = false; - - MsfGifResult result = msf_gif_end(&gifState); - - SaveFileData(TextFormat("%s/screenrec%03i.gif", CORE.Storage.basePath, screenshotCounter), result.data, (unsigned int)result.dataSize); - msf_gif_free(result); - - TRACELOG(LOG_INFO, "SYSTEM: Finish animated GIF recording"); - } - else - { - gifRecording = true; - gifFrameCounter = 0; - - Vector2 scale = GetWindowScaleDPI(); - msf_gif_begin(&gifState, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); - screenshotCounter++; - - TRACELOG(LOG_INFO, "SYSTEM: Start animated GIF recording: %s", TextFormat("screenrec%03i.gif", screenshotCounter)); - } - } - else -#endif // SUPPORT_GIF_RECORDING - { - TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); - screenshotCounter++; - } - } -#endif // SUPPORT_SCREEN_CAPTURE - -#if defined(SUPPORT_EVENTS_AUTOMATION) - if ((key == GLFW_KEY_F11) && (action == GLFW_PRESS)) - { - eventsRecording = !eventsRecording; - - // On finish recording, we export events into a file - if (!eventsRecording) ExportAutomationEvents("eventsrec.rep"); - } - else if ((key == GLFW_KEY_F9) && (action == GLFW_PRESS)) - { - LoadAutomationEvents("eventsrec.rep"); - eventsPlaying = true; - - TRACELOG(LOG_WARNING, "eventsPlaying enabled!"); - } -#endif } // GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index e918e7d34..7943e18f8 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -1044,65 +1044,6 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i // Check the exit key to set close window if ((key == CORE.Input.Keyboard.exitKey) && (action == GLFW_PRESS)) glfwSetWindowShouldClose(platform.handle, GLFW_TRUE); - -#if defined(SUPPORT_SCREEN_CAPTURE) - if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) - { -#if defined(SUPPORT_GIF_RECORDING) - if (mods & GLFW_MOD_CONTROL) - { - if (gifRecording) - { - gifRecording = false; - - MsfGifResult result = msf_gif_end(&gifState); - - SaveFileData(TextFormat("%s/screenrec%03i.gif", CORE.Storage.basePath, screenshotCounter), result.data, (unsigned int)result.dataSize); - msf_gif_free(result); - - // Download file from MEMFS (emscripten memory filesystem) - // saveFileFromMEMFSToDisk() function is defined in raylib/templates/web_shel/shell.html - emscripten_run_script(TextFormat("saveFileFromMEMFSToDisk('%s','%s')", TextFormat("screenrec%03i.gif", screenshotCounter - 1), TextFormat("screenrec%03i.gif", screenshotCounter - 1))); - - TRACELOG(LOG_INFO, "SYSTEM: Finish animated GIF recording"); - } - else - { - gifRecording = true; - gifFrameCounter = 0; - - Vector2 scale = GetWindowScaleDPI(); - msf_gif_begin(&gifState, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); - screenshotCounter++; - - TRACELOG(LOG_INFO, "SYSTEM: Start animated GIF recording: %s", TextFormat("screenrec%03i.gif", screenshotCounter)); - } - } - else -#endif // SUPPORT_GIF_RECORDING - { - TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); - screenshotCounter++; - } - } -#endif // SUPPORT_SCREEN_CAPTURE - -#if defined(SUPPORT_EVENTS_AUTOMATION) - if ((key == GLFW_KEY_F11) && (action == GLFW_PRESS)) - { - eventsRecording = !eventsRecording; - - // On finish recording, we export events into a file - if (!eventsRecording) ExportAutomationEvents("eventsrec.rep"); - } - else if ((key == GLFW_KEY_F9) && (action == GLFW_PRESS)) - { - LoadAutomationEvents("eventsrec.rep"); - eventsPlaying = true; - - TRACELOG(LOG_WARNING, "eventsPlaying enabled!"); - } -#endif } // GLFW3 Char Key Callback, runs on key down (gets equivalent unicode char value) diff --git a/src/rcore.c b/src/rcore.c index 9bd47983a..0cd3f4408 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -739,6 +739,44 @@ void EndDrawing(void) PollInputEvents(); // Poll user events (before next frame update) #endif +#if defined(SUPPORT_SCREEN_CAPTURE) + if (IsKeyPressed(KEY_F12)) + { +#if defined(SUPPORT_GIF_RECORDING) + if (IsKeyDown(KEY_LEFT_CONTROL)) + { + if (gifRecording) + { + gifRecording = false; + + MsfGifResult result = msf_gif_end(&gifState); + + SaveFileData(TextFormat("%s/screenrec%03i.gif", CORE.Storage.basePath, screenshotCounter), result.data, (unsigned int)result.dataSize); + msf_gif_free(result); + + TRACELOG(LOG_INFO, "SYSTEM: Finish animated GIF recording"); + } + else + { + gifRecording = true; + gifFrameCounter = 0; + + Vector2 scale = GetWindowScaleDPI(); + msf_gif_begin(&gifState, (int)((float)CORE.Window.render.width*scale.x), (int)((float)CORE.Window.render.height*scale.y)); + screenshotCounter++; + + TRACELOG(LOG_INFO, "SYSTEM: Start animated GIF recording: %s", TextFormat("screenrec%03i.gif", screenshotCounter)); + } + } + else +#endif // SUPPORT_GIF_RECORDING + { + TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); + screenshotCounter++; + } + } +#endif // SUPPORT_SCREEN_CAPTURE + #if defined(SUPPORT_EVENTS_AUTOMATION) // Events recording and playing logic if (eventsRecording) RecordAutomationEvent(CORE.Time.frameCounter); @@ -748,7 +786,7 @@ void EndDrawing(void) if (CORE.Time.frameCounter >= eventCount) eventsPlaying = false; PlayAutomationEvent(CORE.Time.frameCounter); } -#endif +#endif // SUPPORT_EVENTS_AUTOMATION CORE.Time.frameCounter++; } From a0f00343523a8898dfed46ee21afaa99d0c15f66 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 23 Oct 2023 19:15:40 +0200 Subject: [PATCH 0409/1350] REVIEWED: `InitPlatform()` organization and code-gardening --- src/platforms/rcore_android.c | 39 +++++----- src/platforms/rcore_desktop.c | 115 ++++++++++++++++++------------ src/platforms/rcore_desktop_sdl.c | 34 ++++++--- src/platforms/rcore_drm.c | 56 +++++++++------ src/platforms/rcore_template.c | 30 ++++++-- src/platforms/rcore_web.c | 79 ++++++++++++-------- src/rcore.c | 2 - 7 files changed, 221 insertions(+), 134 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 1272eecc3..4faf73b7d 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -518,6 +518,8 @@ void PollInputEvents(void) // Initialize platform: graphics, inputs and more int InitPlatform(void) { + // Initialize display basic configuration + //---------------------------------------------------------------------------- CORE.Window.currentFbo.width = CORE.Window.screen.width; CORE.Window.currentFbo.height = CORE.Window.screen.height; @@ -545,27 +547,33 @@ int InitPlatform(void) //AConfiguration_getKeyboard(platform.app->config); //AConfiguration_getScreenSize(platform.app->config); //AConfiguration_getScreenLong(platform.app->config); - - // Initialize App command system - // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()... - platform.app->onAppCmd = AndroidCommandCallback; - - // Initialize input events system - platform.app->onInputEvent = AndroidInputCallback; - - // Initialize assets manager - InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath); - - // Initialize base path for storage - CORE.Storage.basePath = platform.app->activity->internalDataPath; - + // Set some default window flags CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false + //---------------------------------------------------------------------------- - TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Application initialized successfully"); + // Initialize App command system + // NOTE: On APP_CMD_INIT_WINDOW -> InitGraphicsDevice(), InitTimer(), LoadFontDefault()... + //---------------------------------------------------------------------------- + platform.app->onAppCmd = AndroidCommandCallback; + //---------------------------------------------------------------------------- + + // Initialize input events system + //---------------------------------------------------------------------------- + platform.app->onInputEvent = AndroidInputCallback; + //---------------------------------------------------------------------------- + + // Initialize storage system + //---------------------------------------------------------------------------- + InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath); // Initialize assets manager + + CORE.Storage.basePath = platform.app->activity->internalDataPath; // Define base path for storage + //---------------------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: ANDROID: Initialized successfully"); // Android ALooper_pollAll() variables int pollResult = 0; @@ -613,7 +621,6 @@ void ClosePlatform(void) } } - // Initialize display device and framebuffer // NOTE: width and height represent the screen (framebuffer) desired size, not actual display size // If width or height are 0, default display size will be used for framebuffer size diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index b7def6e67..f1ad8a9a3 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -1236,6 +1236,8 @@ int InitPlatform(void) int result = glfwInit(); if (result == GLFW_FALSE) { TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); return -1; } + // Initialize graphic device: display/window and graphic context + //---------------------------------------------------------------------------- glfwDefaultWindowHints(); // Set default windows hints //glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits //glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits @@ -1450,60 +1452,73 @@ int InitPlatform(void) } glfwMakeContextCurrent(platform.handle); - glfwSwapInterval(0); // No V-Sync by default - - // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) - // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need - // to be activated on web platforms since VSync is enforced there. - if (CORE.Window.flags & FLAG_VSYNC_HINT) + result = glfwGetError(NULL); + + // Check context activation + if ((result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) { - // WARNING: It seems to hit a critical render path in Intel HD Graphics - glfwSwapInterval(1); - TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC"); + CORE.Window.ready = true; + + glfwSwapInterval(0); // No V-Sync by default + + // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) + // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need + // to be activated on web platforms since VSync is enforced there. + if (CORE.Window.flags & FLAG_VSYNC_HINT) + { + // WARNING: It seems to hit a critical render path in Intel HD Graphics + glfwSwapInterval(1); + TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC"); + } + + int fbWidth = CORE.Window.screen.width; + int fbHeight = CORE.Window.screen.height; + + if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) + { + // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling. + // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE); + #if !defined(__APPLE__) + glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight); + + // Screen scaling matrix is required in case desired screen area is different from display area + CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f); + + // Mouse input scaling for the new screen size + SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight); + #endif + } + + CORE.Window.render.width = fbWidth; + CORE.Window.render.height = fbHeight; + CORE.Window.currentFbo.width = fbWidth; + CORE.Window.currentFbo.height = fbHeight; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); } - - int fbWidth = CORE.Window.screen.width; - int fbHeight = CORE.Window.screen.height; - - if ((CORE.Window.flags & FLAG_WINDOW_HIGHDPI) > 0) - { - // NOTE: On APPLE platforms system should manage window/input scaling and also framebuffer scaling. - // Framebuffer scaling should be activated with: glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE); -#if !defined(__APPLE__) - glfwGetFramebufferSize(platform.handle, &fbWidth, &fbHeight); - - // Screen scaling matrix is required in case desired screen area is different from display area - CORE.Window.screenScale = MatrixScale((float)fbWidth/CORE.Window.screen.width, (float)fbHeight/CORE.Window.screen.height, 1.0f); - - // Mouse input scaling for the new screen size - SetMouseScale((float)CORE.Window.screen.width/fbWidth, (float)CORE.Window.screen.height/fbHeight); -#endif + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + return -1; } - CORE.Window.render.width = fbWidth; - CORE.Window.render.height = fbHeight; - CORE.Window.currentFbo.width = fbWidth; - CORE.Window.currentFbo.height = fbHeight; - - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); - TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); - TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); - TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); - TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); - - // Load OpenGL extensions - // NOTE: GL procedures address loader is required to load extensions - rlLoadExtensions(glfwGetProcAddress); - if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); - CORE.Window.ready = true; // TODO: Proper validation on windows/context creation - // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(glfwGetProcAddress); + //---------------------------------------------------------------------------- + // Initialize input events callbacks + //---------------------------------------------------------------------------- // Set window callback events glfwSetWindowSizeCallback(platform.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! glfwSetWindowMaximizeCallback(platform.handle, WindowMaximizeCallback); @@ -1521,12 +1536,19 @@ int InitPlatform(void) glfwSetJoystickCallback(JoystickCallback); glfwSetInputMode(platform.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) + //---------------------------------------------------------------------------- - // Initialize hi-res timer + // Initialize timming system + //---------------------------------------------------------------------------- InitTimer(); - - // Initialize base path for storage + //---------------------------------------------------------------------------- + + // Initialize storage system + //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); + //---------------------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (GLFW): Initialized successfully"); return 0; } @@ -1542,7 +1564,6 @@ void ClosePlatform(void) #endif } - // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description) { diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index d2f550ace..7fe9673fa 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1107,6 +1107,8 @@ int InitPlatform(void) int result = SDL_Init(SDL_INIT_EVERYTHING); if (result < 0) { TRACELOG(LOG_WARNING, "SDL: Failed to initialize SDL"); return -1; } + // Initialize graphic device: display/window and graphic context + //---------------------------------------------------------------------------- unsigned int flags = 0; flags |= SDL_WINDOW_SHOWN; flags |= SDL_WINDOW_OPENGL; @@ -1143,6 +1145,7 @@ int InitPlatform(void) //if ((CORE.Window.flags & FLAG_FULLSCREEN_DESKTOP) > 0) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; // NOTE: Some OpenGL context attributes must be set before window creation + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); @@ -1170,7 +1173,7 @@ int InitPlatform(void) { CORE.Window.ready = true; - SDL_DisplayMode displayMode; + SDL_DisplayMode displayMode = { 0 }; SDL_GetCurrentDisplayMode(GetCurrentMonitor(), &displayMode); CORE.Window.display.width = displayMode.w; @@ -1187,30 +1190,43 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); } - else { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + return -1; + } // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(SDL_GL_GetProcAddress); + //---------------------------------------------------------------------------- - - // Init input gamepad + // Initialize input events system + //---------------------------------------------------------------------------- if (SDL_NumJoysticks() >= 1) { SDL_Joystick *gamepad = SDL_JoystickOpen(0); //if (SDL_Joystick *gamepad == NULL) SDL_Log("WARNING: Unable to open game controller! SDL Error: %s\n", SDL_GetError()); } + //---------------------------------------------------------------------------- - // Initialize hi-res timer - //InitTimer(); + // Initialize timming system + //---------------------------------------------------------------------------- + // NOTE: No need to call InitTimer(), let SDL manage it internally CORE.Time.previous = GetTime(); // Get time as double + //---------------------------------------------------------------------------- - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); + // Initialize storage system + //---------------------------------------------------------------------------- + CORE.Storage.basePath = GetWorkingDirectory(); // Define base path for storage + //---------------------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (SDL): Initialized successfully"); return 0; } +// Close platform void ClosePlatform(void) { SDL_FreeCursor(platform.cursor); // Free cursor @@ -1219,7 +1235,7 @@ void ClosePlatform(void) SDL_Quit(); // Deinitialize SDL internal global state } - +// Scancode to keycode mapping static KeyboardKey ConvertScancodeToKey(SDL_Scancode sdlScancode) { if (sdlScancode >= 0 && sdlScancode < SCANCODE_MAPPED_NUM) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 2ecfc2a3a..9609c50d2 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -576,6 +576,8 @@ int InitPlatform(void) platform.prevBO = NULL; platform.prevFB = 0; + // Initialize graphic device: display/window and graphic context + //---------------------------------------------------------------------------- CORE.Window.fullscreen = true; CORE.Window.flags |= FLAG_FULLSCREEN_MODE; @@ -846,7 +848,6 @@ int InitPlatform(void) } // Create an EGL window surface - //--------------------------------------------------------------------------------- platform.surface = eglCreateWindowSurface(platform.device, platform.config, (EGLNativeWindowType)platform.gbmSurface, NULL); if (EGL_NO_SURFACE == platform.surface) { @@ -863,14 +864,14 @@ int InitPlatform(void) // There must be at least one frame displayed before the buffers are swapped //eglSwapInterval(platform.device, 1); + + EGLBoolean result = eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context); - if (eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context) == EGL_FALSE) - { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); - return -1; - } - else + // Check surface and context activation + if (result != EGL_FALSE) { + CORE.Window.ready = true; + CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; CORE.Window.currentFbo.width = CORE.Window.render.width; @@ -882,16 +883,15 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); } - - // Load OpenGL extensions - // NOTE: GL procedures address loader is required to load extensions - rlLoadExtensions(eglGetProcAddress); + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + return -1; + } if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); - CORE.Window.ready = true; // TODO: Proper validation on windows/context creation - - // If graphic device is no properly initialized, we end program + // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor()) / 2 - CORE.Window.screen.width / 2, GetMonitorHeight(GetCurrentMonitor()) / 2 - CORE.Window.screen.height / 2); @@ -901,16 +901,29 @@ int InitPlatform(void) CORE.Window.flags |= FLAG_WINDOW_MAXIMIZED; // true CORE.Window.flags &= ~FLAG_WINDOW_UNFOCUSED; // false - // Initialize hi-res timer + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(eglGetProcAddress); + //---------------------------------------------------------------------------- + + // Initialize input events system + //---------------------------------------------------------------------------- + InitEvdevInput(); // Evdev inputs initialization + InitGamepad(); // Gamepad init + InitKeyboard(); // Keyboard init (stdin) + //---------------------------------------------------------------------------- + + // Initialize timming system + //---------------------------------------------------------------------------- InitTimer(); + //---------------------------------------------------------------------------- - // Initialize base path for storage + // Initialize storage system + //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); - - // Initialize raw input system - InitEvdevInput(); // Evdev inputs initialization - InitGamepad(); // Gamepad init - InitKeyboard(); // Keyboard init (stdin) + //---------------------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully"); return 0; } @@ -1005,7 +1018,6 @@ void ClosePlatform(void) if (platform.gamepadThreadId) pthread_join(platform.gamepadThreadId, NULL); } - // Initialize Keyboard system (using standard input) static void InitKeyboard(void) { diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index 08210289e..11ce45c1e 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -518,13 +518,27 @@ int InitPlatform(void) EGLBoolean result = eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context); - // Enabling current display surface and context failed - if (result == EGL_FALSE) + // Check surface and context activation + if (result != EGL_FALSE) { - TRACELOG(LOG_WARNING, "DISPLAY: Failed to attach EGL rendering context to EGL surface"); + CORE.Window.ready = true; + + CORE.Window.render.width = CORE.Window.screen.width; + CORE.Window.render.height = CORE.Window.screen.height; + CORE.Window.currentFbo.width = CORE.Window.render.width; + CORE.Window.currentFbo.height = CORE.Window.render.height; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + } + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); return -1; } - else CORE.Window.ready = true; //---------------------------------------------------------------------------- // If everything work as expected, we can continue @@ -545,7 +559,7 @@ int InitPlatform(void) rlLoadExtensions(eglGetProcAddress); //---------------------------------------------------------------------------- - // TODO: Initialize input system + // TODO: Initialize input events system // It could imply keyboard, mouse, gamepad, touch... // Depending on the platform libraries/SDK it could use a callbacks mechanims // For system events and inputs evens polling on a per-frame basis, use PollInputEvents() @@ -553,15 +567,17 @@ int InitPlatform(void) // ... //---------------------------------------------------------------------------- - // TODO: Initialize hi-res timer + // TODO: Initialize timming system //---------------------------------------------------------------------------- InitTimer(); //---------------------------------------------------------------------------- - // TODO: Initialize base path for storage + // TODO: Initialize storage system //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); //---------------------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Initialized successfully"); return 0; } diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 7943e18f8..3da23a054 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -677,6 +677,8 @@ int InitPlatform(void) int result = glfwInit(); if (result == GLFW_FALSE) { TRACELOG(LOG_WARNING, "GLFW: Failed to initialize GLFW"); return -1; } + // Initialize graphic device: display/window and graphic context + //---------------------------------------------------------------------------- glfwDefaultWindowHints(); // Set default windows hints // glfwWindowHint(GLFW_RED_BITS, 8); // Framebuffer red color component bits // glfwWindowHint(GLFW_GREEN_BITS, 8); // Framebuffer green color component bits @@ -862,43 +864,46 @@ int InitPlatform(void) glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); glfwMakeContextCurrent(platform.handle); + result = glfwGetError(NULL); + + // Check context activation + if ((result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) + { + CORE.Window.ready = true; // TODO: Proper validation on windows/context creation - // Load OpenGL extensions - // NOTE: GL procedures address loader is required to load extensions - rlLoadExtensions(glfwGetProcAddress); + int fbWidth = CORE.Window.screen.width; + int fbHeight = CORE.Window.screen.height; + + CORE.Window.render.width = fbWidth; + CORE.Window.render.height = fbHeight; + CORE.Window.currentFbo.width = fbWidth; + CORE.Window.currentFbo.height = fbHeight; + + TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); + TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); + TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); + TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); + TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); + } + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + return -1; + } if ((CORE.Window.flags & FLAG_WINDOW_MINIMIZED) > 0) MinimizeWindow(); - // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) - // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need - // to be activated on web platforms since VSync is enforced there. - - int fbWidth = CORE.Window.screen.width; - int fbHeight = CORE.Window.screen.height; - - CORE.Window.render.width = fbWidth; - CORE.Window.render.height = fbHeight; - CORE.Window.currentFbo.width = fbWidth; - CORE.Window.currentFbo.height = fbHeight; - - TRACELOG(LOG_INFO, "DISPLAY: Device initialized successfully"); - TRACELOG(LOG_INFO, " > Display size: %i x %i", CORE.Window.display.width, CORE.Window.display.height); - TRACELOG(LOG_INFO, " > Screen size: %i x %i", CORE.Window.screen.width, CORE.Window.screen.height); - TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); - TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); - - CORE.Window.ready = true; // TODO: Proper validation on windows/context creation - // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); - - // Initialize hi-res timer - InitTimer(); - - // Initialize base path for storage - CORE.Storage.basePath = GetWorkingDirectory(); - + + // Load OpenGL extensions + // NOTE: GL procedures address loader is required to load extensions + rlLoadExtensions(glfwGetProcAddress); + //---------------------------------------------------------------------------- + + // Initialize input events callbacks + //---------------------------------------------------------------------------- // Setup callback functions for the DOM events emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); @@ -927,6 +932,19 @@ int InitPlatform(void) // Support gamepad events (not provided by GLFW3 on emscripten) emscripten_set_gamepadconnected_callback(NULL, 1, EmscriptenGamepadCallback); emscripten_set_gamepaddisconnected_callback(NULL, 1, EmscriptenGamepadCallback); + //---------------------------------------------------------------------------- + + // Initialize timming system + //---------------------------------------------------------------------------- + InitTimer(); + //---------------------------------------------------------------------------- + + // Initialize storage system + //---------------------------------------------------------------------------- + CORE.Storage.basePath = GetWorkingDirectory(); + //---------------------------------------------------------------------------- + + TRACELOG(LOG_INFO, "PLATFORM: WEB: Initialized successfully"); return 0; } @@ -938,7 +956,6 @@ void ClosePlatform(void) glfwTerminate(); } - // GLFW3 Error Callback, runs on GLFW3 error static void ErrorCallback(int error, const char *description) { diff --git a/src/rcore.c b/src/rcore.c index 0cd3f4408..ef3beaee3 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -491,8 +491,6 @@ void InitWindow(int width, int height, const char *title) // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); - - TRACELOG(LOG_INFO, "PLATFORM: DESKTOP: Application initialized successfully"); } // Close window and unload OpenGL context From 803b1a910e0b23986688e9d4e53b77d18ba41767 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 23 Oct 2023 19:59:29 +0200 Subject: [PATCH 0410/1350] REVIEWED: Check OpenGL version required, fix #3457 --- src/platforms/rcore_desktop_sdl.c | 43 +++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 7fe9673fa..ad051a95b 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1145,11 +1145,44 @@ int InitPlatform(void) //if ((CORE.Window.flags & FLAG_FULLSCREEN_DESKTOP) > 0) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; // NOTE: Some OpenGL context attributes must be set before window creation - - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); - //SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG | SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); + + // Check selection OpenGL version + if (rlGetVersion() == RL_OPENGL_21) + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + } + else if (rlGetVersion() == RL_OPENGL_33) + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); +#if defined(__APPLE__) + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // OSX Requires forward compatibility +#else + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); +#endif + } + else if (rlGetVersion() == RL_OPENGL_43) + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); +#if defined(RLGL_ENABLE_OPENGL_DEBUG_CONTEXT) + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); // Enable OpenGL Debug Context +#endif + } + else if (rlGetVersion() == RL_OPENGL_ES_20) // Request OpenGL ES 2.0 context + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + } + else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context + { + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + } if (CORE.Window.flags & FLAG_VSYNC_HINT) { From 8fbd42d592c22612e18d2c6f9bcef8a107984675 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 25 Oct 2023 10:14:17 +0200 Subject: [PATCH 0411/1350] Fix #3461 --- src/platforms/rcore_desktop.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index f1ad8a9a3..de34c8711 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -1120,7 +1120,11 @@ void PollInputEvents(void) // NOTE: We do it here in case of disconnection for (int i = 0; i < MAX_GAMEPADS; i++) { - if (glfwJoystickPresent(i)) CORE.Input.Gamepad.ready[i] = true; + if (glfwJoystickPresent(i)) + { + CORE.Input.Gamepad.ready[i] = true; + strcpy(CORE.Input.Gamepad.name[i], glfwGetJoystickName(i)); + } else CORE.Input.Gamepad.ready[i] = false; } From 7e5eff8a29525df247110268133dcf11f9e72b11 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 25 Oct 2023 10:15:19 +0200 Subject: [PATCH 0412/1350] Revert "Fix #3461" This reverts commit 8fbd42d592c22612e18d2c6f9bcef8a107984675. --- src/platforms/rcore_desktop.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index de34c8711..f1ad8a9a3 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -1120,11 +1120,7 @@ void PollInputEvents(void) // NOTE: We do it here in case of disconnection for (int i = 0; i < MAX_GAMEPADS; i++) { - if (glfwJoystickPresent(i)) - { - CORE.Input.Gamepad.ready[i] = true; - strcpy(CORE.Input.Gamepad.name[i], glfwGetJoystickName(i)); - } + if (glfwJoystickPresent(i)) CORE.Input.Gamepad.ready[i] = true; else CORE.Input.Gamepad.ready[i] = false; } From b0c0f2e5606f129175a10919a16b9eaea248f150 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 25 Oct 2023 07:17:54 -0300 Subject: [PATCH 0413/1350] Fix OpenURL on SDL (#3460) --- src/platforms/rcore_desktop_sdl.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index ad051a95b..ccc2acf0a 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -893,9 +893,15 @@ double GetTime(void) } // Open URL with default system browser (if available) +// NOTE: This function is only safe to use if you control the URL given. +// A user could craft a malicious string performing another action. +// Only call this function yourself not with user input or make sure to check the string yourself. +// Ref: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { - SDL_OpenURL(url); + // Security check to (partially) avoid malicious code + if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); + else SDL_OpenURL(url); } //---------------------------------------------------------------------------------- @@ -1145,7 +1151,7 @@ int InitPlatform(void) //if ((CORE.Window.flags & FLAG_FULLSCREEN_DESKTOP) > 0) flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; // NOTE: Some OpenGL context attributes must be set before window creation - + // Check selection OpenGL version if (rlGetVersion() == RL_OPENGL_21) { @@ -1224,9 +1230,9 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); } else - { - TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); - return -1; + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + return -1; } // Load OpenGL extensions @@ -1253,7 +1259,7 @@ int InitPlatform(void) //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); // Define base path for storage //---------------------------------------------------------------------------- - + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (SDL): Initialized successfully"); return 0; From cb1c2ffda133a43b6dd7237c9139110954712d75 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 25 Oct 2023 14:13:51 -0300 Subject: [PATCH 0414/1350] Fix gamepad names for PLATFORM_DESKTOP/GLFW (#3462) --- src/platforms/rcore_desktop.c | 36 ++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index f1ad8a9a3..b43a6b5ae 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -649,12 +649,12 @@ void SetWindowMinSize(int width, int height) { CORE.Window.screenMin.width = width; CORE.Window.screenMin.height = height; - + int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.width; int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.height; int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.width; int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.height; - + glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight); } @@ -663,12 +663,12 @@ void SetWindowMaxSize(int width, int height) { CORE.Window.screenMax.width = width; CORE.Window.screenMax.height = height; - + int minWidth = (CORE.Window.screenMin.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.width; int minHeight = (CORE.Window.screenMin.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMin.height; int maxWidth = (CORE.Window.screenMax.width == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.width; int maxHeight = (CORE.Window.screenMax.height == 0)? GLFW_DONT_CARE : (int)CORE.Window.screenMax.height; - + glfwSetWindowSizeLimits(platform.handle, minWidth, minHeight, maxWidth, maxHeight); } @@ -1108,7 +1108,7 @@ void PollInputEvents(void) // Reset touch positions //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; - + // Map touch position to mouse position for convenience // WARNING: If the target desktop device supports touch screen, this behavious should be reviewed! // TODO: GLFW does not support multi-touch input just yet @@ -1406,7 +1406,7 @@ int InitPlatform(void) } } } - + TRACELOG(LOG_WARNING, "SYSTEM: Closest fullscreen videomode: %i x %i", CORE.Window.display.width, CORE.Window.display.height); // NOTE: ISSUE: Closest videomode could not match monitor aspect-ratio, for example, @@ -1450,10 +1450,10 @@ int InitPlatform(void) TRACELOG(LOG_WARNING, "GLFW: Failed to initialize Window"); return -1; } - + glfwMakeContextCurrent(platform.handle); result = glfwGetError(NULL); - + // Check context activation if ((result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) { @@ -1500,9 +1500,9 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); } - else - { - TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); return -1; } @@ -1511,12 +1511,12 @@ int InitPlatform(void) // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); - + // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(glfwGetProcAddress); //---------------------------------------------------------------------------- - + // Initialize input events callbacks //---------------------------------------------------------------------------- // Set window callback events @@ -1536,6 +1536,12 @@ int InitPlatform(void) glfwSetJoystickCallback(JoystickCallback); glfwSetInputMode(platform.handle, GLFW_LOCK_KEY_MODS, GLFW_TRUE); // Enable lock keys modifiers (CAPS, NUM) + + // Retrieve gamepad names + for (int i = 0; i < MAX_GAMEPADS; i++) + { + if (glfwJoystickPresent(i)) strcpy(CORE.Input.Gamepad.name[i], glfwGetJoystickName(i)); + } //---------------------------------------------------------------------------- // Initialize timming system @@ -1547,9 +1553,9 @@ int InitPlatform(void) //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); //---------------------------------------------------------------------------- - + TRACELOG(LOG_INFO, "PLATFORM: DESKTOP (GLFW): Initialized successfully"); - + return 0; } From 9a687e3153633479067fbb0439a7e0214dec665b Mon Sep 17 00:00:00 2001 From: 2Bear Date: Thu, 26 Oct 2023 16:15:25 +0800 Subject: [PATCH 0415/1350] Fix missing `PLATFORM_DESKTOP_SDL` checks. (#3469) --- src/rlgl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rlgl.h b/src/rlgl.h index e38642ec9..7c93bf929 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -803,7 +803,7 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #if defined(GRAPHICS_API_OPENGL_ES2) // NOTE: OpenGL ES 2.0 can be enabled on PLATFORM_DESKTOP, // in that case, functions are loaded from a custom glad for OpenGL ES 2.0 - #if defined(PLATFORM_DESKTOP) + #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_DESKTOP_SDL) #define GLAD_GLES2_IMPLEMENTATION #include "external/glad_gles2.h" #else @@ -2248,7 +2248,7 @@ void rlLoadExtensions(void *loader) // RLGL.ExtSupported.maxAnisotropyLevel #elif defined(GRAPHICS_API_OPENGL_ES2) - #if defined(PLATFORM_DESKTOP) + #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_DESKTOP_SDL) // TODO: Support OpenGL ES 3.0 if (gladLoadGLES2((GLADloadfunc)loader) == 0) TRACELOG(RL_LOG_WARNING, "GLAD: Cannot load OpenGL ES2.0 functions"); else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL ES 2.0 loaded successfully"); From 2f6b2897fe9d6a777b4f32ff6490436fbbb1b54b Mon Sep 17 00:00:00 2001 From: Alexandre Almeida Date: Thu, 26 Oct 2023 05:18:00 -0300 Subject: [PATCH 0416/1350] GetCurrentMonitor() - check window center instead of top-left corner (#3468) --- src/platforms/rcore_desktop.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index b43a6b5ae..1114181a4 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -755,6 +755,8 @@ int GetCurrentMonitor(void) int y = 0; glfwGetWindowPos(platform.handle, &x, &y); + x += (int)CORE.Window.screen.width / 2; + y += (int)CORE.Window.screen.height / 2; for (int i = 0; i < monitorCount; i++) { From 804f1a83eba7aa5cbb701457e8d6cda372b1a10d Mon Sep 17 00:00:00 2001 From: jestarray <34615798+jestarray@users.noreply.github.com> Date: Thu, 26 Oct 2023 01:24:21 -0700 Subject: [PATCH 0417/1350] Fix IsGestureDetected parameter inconsistency in raylib.h with rgextures.h (#3464) closes https://github.com/raysan5/raylib/issues/3463 --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index b172562d0..40fcd98fd 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1167,7 +1167,7 @@ RLAPI int GetTouchPointCount(void); // Get number of t // Gestures and Touch Handling Functions (Module: rgestures) //------------------------------------------------------------------------------------ RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags -RLAPI bool IsGestureDetected(unsigned int gesture); // Check if a gesture have been detected +RLAPI bool IsGestureDetected(int gesture); // Check if a gesture have been detected RLAPI int GetGestureDetected(void); // Get latest detected gesture RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector From eddeafd2ed3be0b738a9ebcb5083448bd9640542 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Oct 2023 10:28:00 +0200 Subject: [PATCH 0418/1350] Revert "Fix IsGestureDetected parameter inconsistency in raylib.h with rgextures.h (#3464)" This reverts commit 804f1a83eba7aa5cbb701457e8d6cda372b1a10d. --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index 40fcd98fd..b172562d0 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1167,7 +1167,7 @@ RLAPI int GetTouchPointCount(void); // Get number of t // Gestures and Touch Handling Functions (Module: rgestures) //------------------------------------------------------------------------------------ RLAPI void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags -RLAPI bool IsGestureDetected(int gesture); // Check if a gesture have been detected +RLAPI bool IsGestureDetected(unsigned int gesture); // Check if a gesture have been detected RLAPI int GetGestureDetected(void); // Get latest detected gesture RLAPI float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds RLAPI Vector2 GetGestureDragVector(void); // Get gesture drag vector From 77730c80d903083751cc81a6912593c95f4f7165 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Oct 2023 10:34:39 +0200 Subject: [PATCH 0419/1350] Updated to miniaudio v0.11.19 #3448 --- src/external/miniaudio.h | 168 +++++++++++++++++++++++++-------------- 1 file changed, 110 insertions(+), 58 deletions(-) diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h index 181f45289..518e3c43a 100644 --- a/src/external/miniaudio.h +++ b/src/external/miniaudio.h @@ -1,6 +1,6 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.18 - 2023-08-07 +miniaudio - v0.11.19 - TBD David Reid - mackron@gmail.com @@ -87,7 +87,7 @@ device on the stack, but you could allocate it on the heap if that suits your si // Do something here. Probably your program's main loop. - ma_device_uninit(&device); // This will stop the device so no need to do that manually. + ma_device_uninit(&device); return 0; } ``` @@ -1675,7 +1675,7 @@ an example for initializing a data source: // ... - ma_resource_manager_data_source_uninit(pResourceManager, &dataSource); + ma_resource_manager_data_source_uninit(&dataSource); ``` The `flags` parameter specifies how you want to perform loading of the sound file. It can be a @@ -1912,10 +1912,10 @@ once after the other: ```c ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer0); // Refcount = 1. Initial load. - ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer0); // Refcount = 0. Unloaded. + ma_resource_manager_data_source_uninit(&myDataBuffer0); // Refcount = 0. Unloaded. ma_resource_manager_data_source_init(pResourceManager, "my_file", ..., &myDataBuffer1); // Refcount = 1. Reloaded because previous uninit() unloaded it. - ma_resource_manager_data_source_uninit(pResourceManager, &myDataBuffer1); // Refcount = 0. Unloaded. + ma_resource_manager_data_source_uninit(&myDataBuffer1); // Refcount = 0. Unloaded. ``` A binary search tree (BST) is used for storing data buffers as it has good balance between @@ -3409,7 +3409,7 @@ miniaudio supports reading from a buffer of raw audio data via the `ma_audio_buf read from memory that's managed by the application, but can also handle the memory management for you internally. Memory management is flexible and should support most use cases. -Audio buffers are initialised using the standard configuration system used everywhere in miniaudio: +Audio buffers are initialized using the standard configuration system used everywhere in miniaudio: ```c ma_audio_buffer_config config = ma_audio_buffer_config_init( @@ -3716,7 +3716,7 @@ extern "C" { #define MA_VERSION_MAJOR 0 #define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 18 +#define MA_VERSION_REVISION 19 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #if defined(_MSC_VER) && !defined(__clang__) @@ -4267,7 +4267,7 @@ typedef enum ma_standard_sample_rate_192000 = 192000, ma_standard_sample_rate_16000 = 16000, /* Extreme lows */ - ma_standard_sample_rate_11025 = 11250, + ma_standard_sample_rate_11025 = 11025, ma_standard_sample_rate_8000 = 8000, ma_standard_sample_rate_352800 = 352800, /* Extreme highs */ @@ -5390,7 +5390,7 @@ MA_API void ma_resampler_uninit(ma_resampler* pResampler, const ma_allocation_ca /* Converts the given input data. -Both the input and output frames must be in the format specified in the config when the resampler was initilized. +Both the input and output frames must be in the format specified in the config when the resampler was initialized. On input, [pFrameCountOut] contains the number of output frames to process. On output it contains the number of output frames that were actually processed, which may be less than the requested amount which will happen if there's not enough input data. You can use @@ -9133,8 +9133,6 @@ speakers or received from the microphone which can in turn result in de-syncs. Do not call this in any callback. -This will be called implicitly by `ma_device_uninit()`. - See Also -------- @@ -10171,7 +10169,7 @@ MA_API ma_noise_config ma_noise_config_init(ma_format format, ma_uint32 channels typedef struct { - ma_data_source_vtable ds; + ma_data_source_base ds; ma_noise_config config; ma_lcg lcg; union @@ -10569,7 +10567,7 @@ typedef struct /* Extended processing callback. This callback is used for effects that process input and output at different rates (i.e. they perform resampling). This is similar to the simple version, only - they take two seperate frame counts: one for input, and one for output. + they take two separate frame counts: one for input, and one for output. On input, `pFrameCountOut` is equal to the capacity of the output buffer for each bus, whereas `pFrameCountIn` will be equal to the number of PCM frames in each of the buffers in `ppFramesIn`. @@ -12238,7 +12236,7 @@ static MA_INLINE void ma_zero_memory_default(void* p, size_t sz) #define ma_abs(x) (((x) > 0) ? (x) : -(x)) #define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi))) #define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) -#define ma_align(x, a) ((x + (a-1)) & ~(a-1)) +#define ma_align(x, a) (((x) + ((a)-1)) & ~((a)-1)) #define ma_align_64(x) ma_align(x, 8) #define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels)) @@ -13639,7 +13637,7 @@ MA_API ma_result ma_log_postv(ma_log* pLog, ma_uint32 level, const char* pFormat /* First try formatting into our fixed sized stack allocated buffer. If this is too small we'll fallback to a heap allocation. */ length = vsnprintf(pFormattedMessageStack, sizeof(pFormattedMessageStack), pFormat, args); if (length < 0) { - return MA_INVALID_OPERATION; /* An error occured when trying to convert the buffer. */ + return MA_INVALID_OPERATION; /* An error occurred when trying to convert the buffer. */ } if ((size_t)length < sizeof(pFormattedMessageStack)) { @@ -16180,7 +16178,15 @@ static void ma_thread_wait__posix(ma_thread* pThread) static ma_result ma_mutex_init__posix(ma_mutex* pMutex) { - int result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL); + int result; + + if (pMutex == NULL) { + return MA_INVALID_ARGS; + } + + MA_ZERO_OBJECT(pMutex); + + result = pthread_mutex_init((pthread_mutex_t*)pMutex, NULL); if (result != 0) { return ma_result_from_errno(result); } @@ -18452,7 +18458,7 @@ Timing *******************************************************************************/ #if defined(MA_WIN32) && !defined(MA_POSIX) static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */ - void ma_timer_init(ma_timer* pTimer) + static void ma_timer_init(ma_timer* pTimer) { LARGE_INTEGER counter; @@ -18464,7 +18470,7 @@ Timing pTimer->counter = counter.QuadPart; } - double ma_timer_get_time_in_seconds(ma_timer* pTimer) + static double ma_timer_get_time_in_seconds(ma_timer* pTimer) { LARGE_INTEGER counter; if (!QueryPerformanceCounter(&counter)) { @@ -18637,30 +18643,36 @@ static void ma_device__on_notification(ma_device_notification notification) } } -void ma_device__on_notification_started(ma_device* pDevice) +static void ma_device__on_notification_started(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_started)); } -void ma_device__on_notification_stopped(ma_device* pDevice) +static void ma_device__on_notification_stopped(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_stopped)); } -void ma_device__on_notification_rerouted(ma_device* pDevice) +/* Not all platforms support reroute notifications. */ +#if !defined(MA_EMSCRIPTEN) +static void ma_device__on_notification_rerouted(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_rerouted)); } +#endif -void ma_device__on_notification_interruption_began(ma_device* pDevice) +/* Interruptions are only used on some platforms. */ +#if defined(MA_APPLE_MOBILE) +static void ma_device__on_notification_interruption_began(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_began)); } -void ma_device__on_notification_interruption_ended(ma_device* pDevice) +static void ma_device__on_notification_interruption_ended(ma_device* pDevice) { ma_device__on_notification(ma_device_notification_init(pDevice, ma_device_notification_type_interruption_ended)); } +#endif static void ma_device__on_data_inner(ma_device* pDevice, void* pFramesOut, const void* pFramesIn, ma_uint32 frameCount) @@ -19115,10 +19127,10 @@ static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state n #if defined(MA_WIN32) - GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; - /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ - /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ + static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ + /*static GUID MA_GUID_KSDATAFORMAT_SUBTYPE_MULAW = {0x00000007, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ #endif @@ -23270,7 +23282,7 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui /* At this point we should be able to loop back to the start of the loop and try retrieving a data buffer again. */ } else { - /* An error occured and we need to abort. */ + /* An error occurred and we need to abort. */ ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to retrieve internal buffer from capture device in preparation for reading from the device. HRESULT = %d. Stopping device.\n", (int)hr); result = ma_result_from_HRESULT(hr); break; @@ -34834,7 +34846,7 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte #endif #if !defined(MA_NO_RUNTIME_LINKING) && !defined(MA_APPLE_MOBILE) - pContext->coreaudio.hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), "CoreFoundation.framework/CoreFoundation"); + pContext->coreaudio.hCoreFoundation = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation"); if (pContext->coreaudio.hCoreFoundation == NULL) { return MA_API_NOT_FOUND; } @@ -34843,7 +34855,7 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte pContext->coreaudio.CFRelease = ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation, "CFRelease"); - pContext->coreaudio.hCoreAudio = ma_dlopen(ma_context_get_log(pContext), "CoreAudio.framework/CoreAudio"); + pContext->coreaudio.hCoreAudio = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/CoreAudio.framework/CoreAudio"); if (pContext->coreaudio.hCoreAudio == NULL) { ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); return MA_API_NOT_FOUND; @@ -34861,7 +34873,7 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte The way it'll work is that it'll first try AudioUnit, and if the required symbols are not present there we'll fall back to AudioToolbox. */ - pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "AudioUnit.framework/AudioUnit"); + pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioUnit.framework/AudioUnit"); if (pContext->coreaudio.hAudioUnit == NULL) { ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); @@ -34871,7 +34883,7 @@ static ma_result ma_context_init__coreaudio(ma_context* pContext, const ma_conte if (ma_dlsym(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit, "AudioComponentFindNext") == NULL) { /* Couldn't find the required symbols in AudioUnit, so fall back to AudioToolbox. */ ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hAudioUnit); - pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "AudioToolbox.framework/AudioToolbox"); + pContext->coreaudio.hAudioUnit = ma_dlopen(ma_context_get_log(pContext), "/System/Library/Frameworks/AudioToolbox.framework/AudioToolbox"); if (pContext->coreaudio.hAudioUnit == NULL) { ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreAudio); ma_dlclose(ma_context_get_log(pContext), pContext->coreaudio.hCoreFoundation); @@ -39956,7 +39968,7 @@ static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T a /* With the audio worklet initialized we can now attach it to the graph. */ if (pParameters->pConfig->deviceType == ma_device_type_capture || pParameters->pConfig->deviceType == ma_device_type_duplex) { - ma_result attachmentResult = EM_ASM_INT({ + ma_result attachmentResult = (ma_result)EM_ASM_INT({ var getUserMediaResult = 0; var audioWorklet = emscriptenGetAudioObject($0); var audioContext = emscriptenGetAudioObject($1); @@ -39987,7 +39999,7 @@ static void ma_audio_worklet_processor_created__webaudio(EMSCRIPTEN_WEBAUDIO_T a /* If it's playback only we can now attach the worklet node to the graph. This has already been done for the duplex case. */ if (pParameters->pConfig->deviceType == ma_device_type_playback) { - ma_result attachmentResult = EM_ASM_INT({ + ma_result attachmentResult = (ma_result)EM_ASM_INT({ var audioWorklet = emscriptenGetAudioObject($0); var audioContext = emscriptenGetAudioObject($1); audioWorklet.connect(audioContext.destination); @@ -40202,7 +40214,7 @@ static ma_result ma_device_init__webaudio(ma_device* pDevice, const ma_device_co /* First thing we need is an AudioContext. */ var audioContextOptions = {}; - if (deviceType == window.miniaudio.device_type.playback) { + if (deviceType == window.miniaudio.device_type.playback && sampleRate != 0) { audioContextOptions.sampleRate = sampleRate; } @@ -42100,10 +42112,23 @@ MA_API void ma_device_uninit(ma_device* pDevice) return; } - /* Make sure the device is stopped first. The backends will probably handle this naturally, but I like to do it explicitly for my own sanity. */ - if (ma_device_is_started(pDevice)) { - ma_device_stop(pDevice); + /* + It's possible for the miniaudio side of the device and the backend to not be in sync due to + system-level situations such as the computer being put into sleep mode and the backend not + notifying miniaudio of the fact the device has stopped. It's possible for this to result in a + deadlock due to miniaudio thinking the device is in a running state, when in fact it's not + running at all. For this reason I am no longer explicitly stopping the device. I don't think + this should affect anyone in practice since uninitializing the backend will naturally stop the + device anyway. + */ + #if 0 + { + /* Make sure the device is stopped first. The backends will probably handle this naturally, but I like to do it explicitly for my own sanity. */ + if (ma_device_is_started(pDevice)) { + ma_device_stop(pDevice); + } } + #endif /* Putting the device into an uninitialized state will make the worker thread return. */ ma_device__set_state(pDevice, ma_device_state_uninitialized); @@ -52835,7 +52860,7 @@ static ma_result ma_channel_map_apply_mono_in_f32(float* MA_RESTRICT pFramesOut, for (iFrame = 0; iFrame < unrolledFrameCount; iFrame += 1) { __m128 in0 = _mm_set1_ps(pFramesIn[iFrame*2 + 0]); __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); - _mm_storeu_ps(&pFramesOut[iFrame*4 + 0], _mm_shuffle_ps(in1, in0, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_storeu_ps(&pFramesOut[iFrame*4 + 0], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); } /* Tail. */ @@ -52861,7 +52886,7 @@ static ma_result ma_channel_map_apply_mono_in_f32(float* MA_RESTRICT pFramesOut, __m128 in1 = _mm_set1_ps(pFramesIn[iFrame*2 + 1]); _mm_storeu_ps(&pFramesOut[iFrame*12 + 0], in0); - _mm_storeu_ps(&pFramesOut[iFrame*12 + 4], _mm_shuffle_ps(in1, in0, _MM_SHUFFLE(0, 0, 0, 0))); + _mm_storeu_ps(&pFramesOut[iFrame*12 + 4], _mm_shuffle_ps(in0, in1, _MM_SHUFFLE(0, 0, 0, 0))); _mm_storeu_ps(&pFramesOut[iFrame*12 + 8], in1); } @@ -59701,7 +59726,7 @@ extern "C" { #define MA_DR_WAV_XSTRINGIFY(x) MA_DR_WAV_STRINGIFY(x) #define MA_DR_WAV_VERSION_MAJOR 0 #define MA_DR_WAV_VERSION_MINOR 13 -#define MA_DR_WAV_VERSION_REVISION 12 +#define MA_DR_WAV_VERSION_REVISION 13 #define MA_DR_WAV_VERSION_STRING MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MAJOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_MINOR) "." MA_DR_WAV_XSTRINGIFY(MA_DR_WAV_VERSION_REVISION) #include #define MA_DR_WAVE_FORMAT_PCM 0x1 @@ -64826,7 +64851,7 @@ MA_API ma_result ma_decoder_init_file(const char* pFilePath, const ma_decoder_co /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ result = ma_decoder_init_vfs(NULL, pFilePath, pConfig, pDecoder); if (result != MA_SUCCESS) { - return MA_SUCCESS; + return result; } } @@ -64976,7 +65001,7 @@ MA_API ma_result ma_decoder_init_file_w(const wchar_t* pFilePath, const ma_decod /* Probably no implementation for loading from a file path. Use miniaudio's file IO instead. */ result = ma_decoder_init_vfs_w(NULL, pFilePath, pConfig, pDecoder); if (result != MA_SUCCESS) { - return MA_SUCCESS; + return result; } } @@ -68744,7 +68769,7 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma async = (flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC) != 0; /* - Fences need to be acquired before doing anything. These must be aquired and released outside of + Fences need to be acquired before doing anything. These must be acquired and released outside of the node to ensure there's no holes where ma_fence_wait() could prematurely return before the data buffer has completed initialization. @@ -72016,7 +72041,7 @@ MA_API ma_result ma_node_init_preallocated(ma_node_graph* pNodeGraph, const ma_n } if (heapLayout.outputBusOffset != MA_SIZE_MAX) { - pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.inputBusOffset); + pNodeBase->pOutputBuses = (ma_node_output_bus*)ma_offset_ptr(pHeap, heapLayout.outputBusOffset); } else { pNodeBase->pOutputBuses = pNodeBase->_outputBuses; } @@ -72507,11 +72532,11 @@ static ma_result ma_node_read_pcm_frames(ma_node* pNode, ma_uint32 outputBusInde /* At this point we know that we are inside our start/stop times. However, we may need to adjust - our frame count and output pointer to accomodate since we could be straddling the time period + our frame count and output pointer to accommodate since we could be straddling the time period that this function is getting called for. It's possible (and likely) that the start time does not line up with the output buffer. We - therefore need to offset it by a number of frames to accomodate. The same thing applies for + therefore need to offset it by a number of frames to accommodate. The same thing applies for the stop time. */ timeOffsetBeg = (globalTimeBeg < startTime) ? (ma_uint32)(globalTimeEnd - startTime) : 0; @@ -74097,7 +74122,7 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo if (fadeStartOffsetInFrames == (ma_int64)(~(ma_uint64)0)) { fadeStartOffsetInFrames = 0; } else { - fadeStartOffsetInFrames -= ma_engine_get_time(pEngineNode->pEngine); + fadeStartOffsetInFrames -= ma_engine_get_time_in_pcm_frames(pEngineNode->pEngine); } ma_fader_set_fade_ex(&pEngineNode->fader, fadeVolumeBeg, fadeVolumeEnd, fadeLengthInFrames, fadeStartOffsetInFrames); @@ -75534,6 +75559,10 @@ MA_API void ma_engine_listener_get_cone(const ma_engine* pEngine, ma_uint32 list *pOuterGain = 0; } + if (pEngine == NULL || listenerIndex >= pEngine->listenerCount) { + return; + } + ma_spatializer_listener_get_cone(&pEngine->listeners[listenerIndex], pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); } @@ -76103,7 +76132,7 @@ MA_API ma_result ma_sound_stop_with_fade_in_pcm_frames(ma_sound* pSound, ma_uint } /* Stopping with a fade out requires us to schedule the stop into the future by the fade length. */ - ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, ma_engine_get_time(ma_sound_get_engine(pSound)) + fadeLengthInFrames, fadeLengthInFrames); + ma_sound_set_stop_time_with_fade_in_pcm_frames(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound)) + fadeLengthInFrames, fadeLengthInFrames); return MA_SUCCESS; } @@ -76476,6 +76505,10 @@ MA_API void ma_sound_get_cone(const ma_sound* pSound, float* pInnerAngleInRadian *pOuterGain = 0; } + if (pSound == NULL) { + return; + } + ma_spatializer_get_cone(&pSound->engineNode.spatializer, pInnerAngleInRadians, pOuterAngleInRadians, pOuterGain); } @@ -76757,6 +76790,8 @@ MA_API ma_result ma_sound_get_data_format(ma_sound* pSound, ma_format* pFormat, MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* pCursor) { + ma_uint64 seekTarget; + if (pSound == NULL) { return MA_INVALID_ARGS; } @@ -76766,7 +76801,12 @@ MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* return MA_INVALID_OPERATION; } - return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor); + seekTarget = ma_atomic_load_64(&pSound->seekTarget); + if (seekTarget != MA_SEEK_TARGET_NONE) { + *pCursor = seekTarget; + } else { + return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor); + } } MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength) @@ -76785,16 +76825,28 @@ MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor) { - if (pSound == NULL) { - return MA_INVALID_ARGS; + ma_result result; + ma_uint64 cursorInPCMFrames; + ma_uint32 sampleRate; + + if (pCursor != NULL) { + *pCursor = 0; } - /* The notion of a cursor is only valid for sounds that are backed by a data source. */ - if (pSound->pDataSource == NULL) { - return MA_INVALID_OPERATION; + result = ma_sound_get_cursor_in_pcm_frames(pSound, &cursorInPCMFrames); + if (result != MA_SUCCESS) { + return result; } - return ma_data_source_get_cursor_in_seconds(pSound->pDataSource, pCursor); + result = ma_sound_get_data_format(pSound, NULL, NULL, &sampleRate, NULL, 0); + if (result != MA_SUCCESS) { + return result; + } + + /* VC6 does not support division of unsigned 64-bit integers with floating point numbers. Need to use a signed number. This shouldn't effect anything in practice. */ + *pCursor = (ma_int64)cursorInPCMFrames / (float)sampleRate; + + return MA_SUCCESS; } MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength) @@ -77193,8 +77245,8 @@ code below please report the bug to the respective repository for the relevant p #define ma_dr_wav_clamp(x, lo, hi) (ma_dr_wav_max((lo), ma_dr_wav_min((hi), (x)))) #define ma_dr_wav_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) #define MA_DR_WAV_MAX_SIMD_VECTOR_SIZE 32 -#define MA_DR_WAV_INT64_MIN ((ma_int64)0x80000000 << 32) -#define MA_DR_WAV_INT64_MAX ((((ma_int64)0x7FFFFFFF) << 32) | 0xFFFFFFFF) +#define MA_DR_WAV_INT64_MIN ((ma_int64) ((ma_uint64)0x80000000 << 32)) +#define MA_DR_WAV_INT64_MAX ((ma_int64)(((ma_uint64)0x7FFFFFFF << 32) | 0xFFFFFFFF)) #if defined(_MSC_VER) && _MSC_VER >= 1400 #define MA_DR_WAV_HAS_BYTESWAP16_INTRINSIC #define MA_DR_WAV_HAS_BYTESWAP32_INTRINSIC From 1cef62cf052432755e9955aa8798eca339dcf1d3 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Oct 2023 11:10:41 +0200 Subject: [PATCH 0420/1350] REVIEWED: `glfwGetError()` not availbale on `PLATFORM_WEB` fix #3470 --- src/platforms/rcore_web.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 3da23a054..e2373e45c 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -864,12 +864,12 @@ int InitPlatform(void) glfwSetCursorEnterCallback(platform.handle, CursorEnterCallback); glfwMakeContextCurrent(platform.handle); - result = glfwGetError(NULL); + result = true; // TODO: WARNING: glfwGetError(NULL); symbol can not be found in Web // Check context activation - if ((result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) + if (result == true) //(result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) { - CORE.Window.ready = true; // TODO: Proper validation on windows/context creation + CORE.Window.ready = true; int fbWidth = CORE.Window.screen.width; int fbHeight = CORE.Window.screen.height; From e4547eb4225189eadd2c6f4e87b5e32c4a285b88 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Oct 2023 23:56:03 +0200 Subject: [PATCH 0421/1350] Remove trail spaces --- HISTORY.md | 22 +++++++++++----------- src/rshapes.c | 6 +++--- src/rtext.c | 6 +++--- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 735e66514..0a0b1ce2b 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -379,13 +379,13 @@ Some numbers to start with: Highlights for `raylib 4.2`: - **raylib extra libraries cleanup**: raylib has been on diet and all the _extra_ libraries included on previous releases have been removed from raylib. Now raylib only includes the original **7** raylib modules: `rcore`, `rlgl`, `rshapes`, `rtextures`, `rtext`, `rmodels` and `raudio`. But no worries, _extra_ libraries have not been deleted, they have been moved to their own repos for better maintainability and more focus on its functionality. The libraries moved out from raylib repo are: [`raygui`](https://github.com/raysan5/raygui), [`physac`](https://github.com/raysan5/physac), [`rmem`](https://github.com/raylib-extras/rmem), [`reasings`](https://github.com/raylib-extras/reasings) and [`raudio`](https://github.com/raysan5/raudio) (standalone mode). On that same line, a new **amazing GitHub group:** [`raylib-extras`](https://github.com/raylib-extras) has been created by @JeffM2501 to contain raylib extra libraries as well as other raylib add-ons provided by the community. Jeff has done an amazing work on that line, providing multiple libraries and examples for raylib, like [custom first-person and third person camera systems](https://github.com/raylib-extras/extras-c/tree/main/cameras), [Dear ImGui raylib integration](https://github.com/raylib-extras/rlImGui), [multiple specific examples](https://github.com/raylib-extras/examples-c) and even a complete [RPG Game Example](https://github.com/raylib-extras/RPGExample)! Great work Jeff! :D - - - **raylib examples review**: The +120 raylib examples have been reviewed to add clearer information about when the were first created (raylib version used) and when they were updated for the last time. But the greatest improvement for users has been the **addition of an estimated difficulty level** for every example, [web has been updated accordingly](https://www.raylib.com/examples.html) to reflect those difficulty levels. Now examples are classified with **1 to 4 stars** depending on difficulty to help users with their learning process. Personally, I think this "small" addition could be a game-changer to better guide new users on the library adoption! Additionally, this new raylib release includes 7 new examples; the most interesting one: [`text_codepoints_loading`](https://www.raylib.com/examples/text/loader.html?name=text_codepoints_loading) that illustrates how to load and draw custom codepoints from a font file, very useful for Asian languages. + + - **raylib examples review**: The +120 raylib examples have been reviewed to add clearer information about when the were first created (raylib version used) and when they were updated for the last time. But the greatest improvement for users has been the **addition of an estimated difficulty level** for every example, [web has been updated accordingly](https://www.raylib.com/examples.html) to reflect those difficulty levels. Now examples are classified with **1 to 4 stars** depending on difficulty to help users with their learning process. Personally, I think this "small" addition could be a game-changer to better guide new users on the library adoption! Additionally, this new raylib release includes 7 new examples; the most interesting one: [`text_codepoints_loading`](https://www.raylib.com/examples/text/loader.html?name=text_codepoints_loading) that illustrates how to load and draw custom codepoints from a font file, very useful for Asian languages. - [**`rres 1.0`**](https://github.com/raysan5/rres): New `rres` **resources packaging file-format**, including a [`rres-raylib`](https://github.com/raysan5/rres/blob/master/src/rres-raylib.h) library implementation and [`rrespacker`](https://raylibtech.itch.io/rrespacker) tool. `rres` file format has been [under development for +8 years](https://github.com/raysan5/rres#design-history) and it was originally created to be part of raylib. It was highly inspired by _XNA XNB_ resources file format but design has changed a lot along the years. This first release of the format specs is engine-agnostic and has been designed to be portable to any engine, including lots of professional features like data processing, compression and encryption. - [**`raygui 3.2`**](https://github.com/raysan5/raygui): The **official raylib immediate-mode gui library** designed for tools development has been updated to a new version aligned with raylib 4.2. Multiple controls have been reviewed for library consistency, now all controls follow a similar function signature. It has been battle-tested with the development of +8 published tools in the last months. The tools can be seen and used for free in the [raylib technologies tools page](https://raylibtech.itch.io/). Worth mentioning that several of those **tools have been open sourced** for anyone to use, compile, contribute or learn how the code works. - + - [**`raylib_parser`**](https://github.com/raysan5/raylib/tree/master/parser): Multiple contributors **using the tool to automatize bindings creation** have contributed with improvements of this **tool to parse `raylib.h`** (and other raylib-style headers) to tokenize its enums, structs and functions. Processed data can be exported to custom file formats (i.e XML, JSON, LUA) for bindings generation or even docs generation if required. - **New file system API**: Current API has been redesigned to be more comprehensive and better aligned with raylib naming conventions, two new functions are provided `LoadDirectoryFiles()`/`LoadDirectoryFilesEx()` to load a `FilePathList` for provided path, supporting extension filtering and recursive directory scan. `LoadDroppedFiles()` has been renamed to better reflect its internal functionality. Now, all raylib functions that start with `Load*()` allocate memory internally and a equivalent `Unload*()` function is defined to take care of that memory internally when not required any more! @@ -414,21 +414,21 @@ Highlights for `raylib 4.5`: - **`NEW` Improved ANGLE support on Desktop platforms**: Support for OpenGL ES 2.0 on Desktop platforms (Windows, Linux, macOS) has been reviewed by @wtnbgo GitHub user. Now raylib can be compiled on desktop for OpenGL ES 2.0 and linked against [`ANGLE`](https://github.com/google/angle). This _small_ addition open the door to building raylib for all **ANGLE supported backends: Direct3D 11, Vulkan and Metal**. Please note that this new feature is still experimental and requires further testing! - **`NEW` Camera module**: A brand new implementation from scratch for `rcamera` module, contributed by @Crydsch GitHub user! **New camera system is simpler, more flexible, more granular and more extendable**. Specific camera math transformations (movement/rotation) have been moved to individual functions, exposing them to users if required. Global state has been removed from the module and standalone usage has been greatly improved; now `rcamera.h` single-file header-only library can be used externally, independently of raylib. A new `UpdateCameraPro()` function has been added to address input-dependency of `UpdateCamera()`, now advance users have **full control over camera inputs and movement/rotation speeds**! - + - **`NEW` Support for M3D models and M3D/GLTF animations**: 3d models animations support has been a limited aspect of raylib for long time, some versions ago IQM animations were supported but raylib 4.5 also adds support for the brand new [M3D file format](https://bztsrc.gitlab.io/model3d/), including animations and the long expected support for **GLTF animations**! The new M3D file format is **simple, portable, feature complete, extensible and open source**. It also provides a complete set of tools to export/visualize M3D models from/to Blender! Now raylib supports up to **3 model file-formats with animations**: `IQM`, `GLTF` and `M3D`. - + - **`NEW` Support QOA audio format (import/export)**: Just a couple of months ago the new [QOA file format](https://qoaformat.org/) was published, a very simple, portable and open source quite-ok-audio file format. raylib already supports it, added to `raudio` module and including audio loading from file, loading from memory, streaming from file, streaming from memory and **exporting to QOA** audio format. **Because simplicity really matters to raylib!** - + - **`NEW` Module for compressed textures loading**: [`rl_gputex`](https://github.com/raysan5/raylib/blob/master/src/external/rl_gputex.h), a portable single-file header-only small library to load compressed texture file-formats (DDS, PKM, KTX, PVR, ASTC). Provided functionality is not new to raylib but it was part of the raylib `rtextures` module, now it has been moved into a separate self-contained library, **improving portability**. Note that this module is only intended to **load compressed data from files, ready to be uploaded to GPU**, no compression/decompression functionality is provided. This change is a first step towards a better modularization of raylib library. - + - **Reviewed `rlgl` module for automatic limits checking**: Again, [`rlgl`](https://github.com/raysan5/raylib/blob/master/src/rlgl.h) has been reviewed to simplify usage. Now users do not need to worry about reaching the internal render-batch limits when they send their triangles to draw 2d/3d, `rlgl` manages it automatically! This change allows a **great simplification for other modules** like `rshapes`, `rtextures` and `rmodels` that do not need to worry about bufffer overflows and can just define as many vertex as desired! - - - **Reviewed `rshapes` module to minimize the rlgl dependency**: Now `rshapes` 2d shapes drawing functions **only depend on 6 low-level functions**: `rlBegin()`, `rlEnd()`, `rlVertex3f()`, `rlTexCoord2f()`, `rlNormal3f()`, `rlSetTexture()`. With only those pseudo-OpenGl 1.1 minimal functionality, everything can be drawn! This improvement converts `rshapes` module in a **self-contained, portable shapes-drawing library that can be used independently of raylib**, as far as entry points for those 6 functions are provided by the user. It even allows to be used for software rendering, with the proper backend! + + - **Reviewed `rshapes` module to minimize the rlgl dependency**: Now `rshapes` 2d shapes drawing functions **only depend on 6 low-level functions**: `rlBegin()`, `rlEnd()`, `rlVertex3f()`, `rlTexCoord2f()`, `rlNormal3f()`, `rlSetTexture()`. With only those pseudo-OpenGl 1.1 minimal functionality, everything can be drawn! This improvement converts `rshapes` module in a **self-contained, portable shapes-drawing library that can be used independently of raylib**, as far as entry points for those 6 functions are provided by the user. It even allows to be used for software rendering, with the proper backend! - **Added data structures validation functions**: Multiple functions have been added by @RobLoach GitHub user to ease the validation of raylib data structures: `IsImageReady()`, `IsTextureReady()`, `IsSoundReady()`... Now users have a simple mechanism to **make sure data has been correctly loaded**, instead of checking internal structure values by themselfs. - + As usual, those are only some highlights but there is much more! New image generators, new color transformation functionality, improved blending support for color/alpha, etc... Make sure to check raylib [CHANGELOG]([CHANGELOG](https://github.com/raysan5/raylib/blob/master/CHANGELOG)) for a detailed list of changes! Please, note that all breaking changes have been flagged with a `WARNING` in the CHANGELOG, specially useful for binding creators! -**raylib keeps improving one more version** with a special focus on maintainability and sustainability. Always working towards making the library more **simple and easy-to-use**. +**raylib keeps improving one more version** with a special focus on maintainability and sustainability. Always working towards making the library more **simple and easy-to-use**. Let's keep **enjoying games/tools/graphics programming!** :) diff --git a/src/rshapes.c b/src/rshapes.c index de64f1593..e8e533d30 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -190,7 +190,7 @@ void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color) if ((length > 0) && (thick > 0)) { float scale = thick/(2*length); - + Vector2 radius = { -scale*delta.y, scale*delta.x }; Vector2 strip[4] = { { startPos.x - radius.x, startPos.y - radius.y }, @@ -255,7 +255,7 @@ void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, fl for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++) { t = step*i; - + float a = powf(1.0f - t, 2); float b = 2.0f*(1.0f - t)*t; float c = powf(t, 2); @@ -301,7 +301,7 @@ void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlP for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++) { t = step*i; - + float a = powf(1.0f - t, 3); float b = 3.0f*powf(1.0f - t, 2)*t; float c = 3.0f*(1.0f - t)*powf(t, 2); diff --git a/src/rtext.c b/src/rtext.c index 5b43bfb92..b83eb171b 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1349,7 +1349,7 @@ unsigned int TextLength(const char *text) if (text != NULL) { // NOTE: Alternative: use strlen(text) - + while (*text++) length++; } @@ -1418,7 +1418,7 @@ int TextCopy(char *dst, const char *src) if ((src != NULL) && (dst != NULL)) { // NOTE: Alternative: use strcpy(dst, src) - + while (*src != '\0') { *dst = *src; @@ -1463,7 +1463,7 @@ const char *TextSubtext(const char *text, int position, int length) } if (length >= textLength) length = textLength; - + // NOTE: Alternative: memcpy(buffer, text + position, length) for (int c = 0 ; c < length ; c++) From d0141bd105b491fbef9ea5fb8c3ba26ba0432717 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Oct 2023 23:56:38 +0200 Subject: [PATCH 0422/1350] Remove trail spaces --- src/platforms/rcore_android.c | 4 ++-- src/platforms/rcore_drm.c | 14 +++++++------- src/platforms/rcore_template.c | 14 +++++++------- src/platforms/rcore_web.c | 12 ++++++------ 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 4faf73b7d..83450bb0a 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -547,7 +547,7 @@ int InitPlatform(void) //AConfiguration_getKeyboard(platform.app->config); //AConfiguration_getScreenSize(platform.app->config); //AConfiguration_getScreenLong(platform.app->config); - + // Set some default window flags CORE.Window.flags &= ~FLAG_WINDOW_HIDDEN; // false CORE.Window.flags &= ~FLAG_WINDOW_MINIMIZED; // false @@ -569,7 +569,7 @@ int InitPlatform(void) // Initialize storage system //---------------------------------------------------------------------------- InitAssetManager(platform.app->activity->assetManager, platform.app->activity->internalDataPath); // Initialize assets manager - + CORE.Storage.basePath = platform.app->activity->internalDataPath; // Define base path for storage //---------------------------------------------------------------------------- diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 9609c50d2..6f459b1af 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -864,14 +864,14 @@ int InitPlatform(void) // There must be at least one frame displayed before the buffers are swapped //eglSwapInterval(platform.device, 1); - + EGLBoolean result = eglMakeCurrent(platform.device, platform.surface, platform.surface, platform.context); // Check surface and context activation if (result != EGL_FALSE) { CORE.Window.ready = true; - + CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; CORE.Window.currentFbo.width = CORE.Window.render.width; @@ -883,9 +883,9 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); } - else - { - TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); return -1; } @@ -905,7 +905,7 @@ int InitPlatform(void) // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(eglGetProcAddress); //---------------------------------------------------------------------------- - + // Initialize input events system //---------------------------------------------------------------------------- InitEvdevInput(); // Evdev inputs initialization @@ -922,7 +922,7 @@ int InitPlatform(void) //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); //---------------------------------------------------------------------------- - + TRACELOG(LOG_INFO, "PLATFORM: DRM: Initialized successfully"); return 0; diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index 11ce45c1e..1cebfa798 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -522,7 +522,7 @@ int InitPlatform(void) if (result != EGL_FALSE) { CORE.Window.ready = true; - + CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; CORE.Window.currentFbo.width = CORE.Window.render.width; @@ -534,13 +534,13 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); } - else - { - TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); return -1; } //---------------------------------------------------------------------------- - + // If everything work as expected, we can continue CORE.Window.render.width = CORE.Window.screen.width; CORE.Window.render.height = CORE.Window.screen.height; @@ -558,7 +558,7 @@ int InitPlatform(void) //---------------------------------------------------------------------------- rlLoadExtensions(eglGetProcAddress); //---------------------------------------------------------------------------- - + // TODO: Initialize input events system // It could imply keyboard, mouse, gamepad, touch... // Depending on the platform libraries/SDK it could use a callbacks mechanims @@ -576,7 +576,7 @@ int InitPlatform(void) //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); //---------------------------------------------------------------------------- - + TRACELOG(LOG_INFO, "PLATFORM: CUSTOM: Initialized successfully"); return 0; diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index e2373e45c..d797d99d7 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -865,7 +865,7 @@ int InitPlatform(void) glfwMakeContextCurrent(platform.handle); result = true; // TODO: WARNING: glfwGetError(NULL); symbol can not be found in Web - + // Check context activation if (result == true) //(result != GLFW_NO_WINDOW_CONTEXT) && (result != GLFW_PLATFORM_ERROR)) { @@ -885,9 +885,9 @@ int InitPlatform(void) TRACELOG(LOG_INFO, " > Render size: %i x %i", CORE.Window.render.width, CORE.Window.render.height); TRACELOG(LOG_INFO, " > Viewport offsets: %i, %i", CORE.Window.renderOffset.x, CORE.Window.renderOffset.y); } - else - { - TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); + else + { + TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphics device"); return -1; } @@ -896,12 +896,12 @@ int InitPlatform(void) // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); - + // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions rlLoadExtensions(glfwGetProcAddress); //---------------------------------------------------------------------------- - + // Initialize input events callbacks //---------------------------------------------------------------------------- // Setup callback functions for the DOM events From 067dbe8657436e4778a91ea69c260f5beba48ec6 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Oct 2023 23:57:07 +0200 Subject: [PATCH 0423/1350] ADDED: Drop files support to `PLATFORM_DESKTOP_SDL` --- src/platforms/rcore_desktop_sdl.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index ccc2acf0a..8543c751c 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -998,6 +998,33 @@ void PollInputEvents(void) switch (event.type) { case SDL_QUIT: CORE.Window.shouldClose = true; break; + + case SDL_DROPFILE: // Dropped file + { + if (CORE.Window.dropFileCount == 0) + { + // When a new file is dropped, we reserve a fixed number of slots for all possible dropped files + // at the moment we limit the number of drops at once to 1024 files but this behaviour should probably be reviewed + // TODO: Pointers should probably be reallocated for any new file added... + CORE.Window.dropFilepaths = (char **)RL_CALLOC(1024, sizeof(char *)); + + CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); + strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file); + SDL_free(event.drop.file); + + CORE.Window.dropFileCount++; + } + else if (CORE.Window.dropFileCount < 1024) + { + CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); + strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file); + SDL_free(event.drop.file); + + CORE.Window.dropFileCount++; + } + else TRACELOG(LOG_WARNING, "FILE: Maximum drag and drop files at once is limited to 1024 files!"); + + } break; // Window events are also polled (Minimized, maximized, close...) case SDL_WINDOWEVENT: @@ -1247,6 +1274,8 @@ int InitPlatform(void) SDL_Joystick *gamepad = SDL_JoystickOpen(0); //if (SDL_Joystick *gamepad == NULL) SDL_Log("WARNING: Unable to open game controller! SDL Error: %s\n", SDL_GetError()); } + + SDL_EventState(SDL_DROPFILE, SDL_ENABLE); //---------------------------------------------------------------------------- // Initialize timming system From 99dac5451cad1c45c0a6b1da5c6175a7575f3403 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 26 Oct 2023 23:59:19 +0200 Subject: [PATCH 0424/1350] ADDED: Automation Events System, exposed to users Added new API to record and play events Added examples illustrating functionality --- examples/core/core_automation_events.c | 289 +++++++ .../examples/core_automation_events.vcxproj | 390 +++++++++ projects/VS2022/raylib.sln | 19 + src/config.h | 3 +- src/raylib.h | 23 + src/rcore.c | 761 ++++++++++-------- src/rcore.h | 4 + 7 files changed, 1155 insertions(+), 334 deletions(-) create mode 100644 examples/core/core_automation_events.c create mode 100644 projects/VS2022/examples/core_automation_events.vcxproj diff --git a/examples/core/core_automation_events.c b/examples/core/core_automation_events.c new file mode 100644 index 000000000..d011d6304 --- /dev/null +++ b/examples/core/core_automation_events.c @@ -0,0 +1,289 @@ +/******************************************************************************************* +* +* raylib [core] example - automation events +* +* Example originally created with raylib 5.0, last time updated with raylib 5.0 +* +* Example based on 2d_camera_platformer example by arvyy (@arvyy) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2023 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" +#include "raymath.h" + +#define GRAVITY 400 +#define PLAYER_JUMP_SPD 350.0f +#define PLAYER_HOR_SPD 200.0f + +#define MAX_ENVIRONMENT_ELEMENTS 5 + +typedef struct Player { + Vector2 position; + float speed; + bool canJump; +} Player; + +typedef struct EnvElement { + Rectangle rect; + int blocking; + Color color; +} EnvElement; + + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [core] example - automation events"); + + // Define player + Player player = { 0 }; + player.position = (Vector2){ 400, 280 }; + player.speed = 0; + player.canJump = false; + + // Define environment elements (platforms) + EnvElement envElements[MAX_ENVIRONMENT_ELEMENTS] = { + {{ 0, 0, 1000, 400 }, 0, LIGHTGRAY }, + {{ 0, 400, 1000, 200 }, 1, GRAY }, + {{ 300, 200, 400, 10 }, 1, GRAY }, + {{ 250, 300, 100, 10 }, 1, GRAY }, + {{ 650, 300, 100, 10 }, 1, GRAY } + }; + + // Define camera + Camera2D camera = { 0 }; + camera.target = player.position; + camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f }; + camera.rotation = 0.0f; + camera.zoom = 1.0f; + + // Automation events + AutomationEventList aelist = LoadAutomationEventList(0); // Initialize list of automation events to record new events + SetAutomationEventList(&aelist); + bool eventRecording = false; + bool eventPlaying = false; + + int frameCounter = 0; + int playFrameCounter = 0; + int currentFrame = 0; + + SetTargetFPS(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) + { + // Update + //---------------------------------------------------------------------------------- + float deltaTime = GetFrameTime(); + + // Dropped files logic + //---------------------------------------------------------------------------------- + if (IsFileDropped()) + { + FilePathList droppedFiles = LoadDroppedFiles(); + + // Supports loading .rgs style files (text or binary) and .png style palette images + if (IsFileExtension(droppedFiles.paths[0], ".txt;.rae")) + { + UnloadAutomationEventList(&aelist); + aelist = LoadAutomationEventList(droppedFiles.paths[0]); + + eventRecording = false; + + // Reset scene state to play + eventPlaying = true; + playFrameCounter = 0; + + player.position = (Vector2){ 400, 280 }; + player.speed = 0; + player.canJump = false; + + camera.target = player.position; + camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f }; + camera.rotation = 0.0f; + camera.zoom = 1.0f; + } + + UnloadDroppedFiles(droppedFiles); // Unload filepaths from memory + } + //---------------------------------------------------------------------------------- + + // Update player + //---------------------------------------------------------------------------------- + if (IsKeyDown(KEY_LEFT)) player.position.x -= PLAYER_HOR_SPD*deltaTime; + if (IsKeyDown(KEY_RIGHT)) player.position.x += PLAYER_HOR_SPD*deltaTime; + if (IsKeyDown(KEY_SPACE) && player.canJump) + { + player.speed = -PLAYER_JUMP_SPD; + player.canJump = false; + } + + int hitObstacle = 0; + for (int i = 0; i < MAX_ENVIRONMENT_ELEMENTS; i++) + { + EnvElement *element = &envElements[i]; + Vector2 *p = &(player.position); + if (element->blocking && + element->rect.x <= p->x && + element->rect.x + element->rect.width >= p->x && + element->rect.y >= p->y && + element->rect.y <= p->y + player.speed*deltaTime) + { + hitObstacle = 1; + player.speed = 0.0f; + p->y = element->rect.y; + } + } + + if (!hitObstacle) + { + player.position.y += player.speed*deltaTime; + player.speed += GRAVITY*deltaTime; + player.canJump = false; + } + else player.canJump = true; + + camera.zoom += ((float)GetMouseWheelMove()*0.05f); + + if (camera.zoom > 3.0f) camera.zoom = 3.0f; + else if (camera.zoom < 0.25f) camera.zoom = 0.25f; + + if (IsKeyPressed(KEY_R)) + { + camera.zoom = 1.0f; + player.position = (Vector2){ 400, 280 }; + } + //---------------------------------------------------------------------------------- + + // Update camera + //---------------------------------------------------------------------------------- + camera.target = player.position; + camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f }; + float minX = 1000, minY = 1000, maxX = -1000, maxY = -1000; + + for (int i = 0; i < MAX_ENVIRONMENT_ELEMENTS; i++) + { + EnvElement *element = &envElements[i]; + minX = fminf(element->rect.x, minX); + maxX = fmaxf(element->rect.x + element->rect.width, maxX); + minY = fminf(element->rect.y, minY); + maxY = fmaxf(element->rect.y + element->rect.height, maxY); + } + + Vector2 max = GetWorldToScreen2D((Vector2){ maxX, maxY }, camera); + Vector2 min = GetWorldToScreen2D((Vector2){ minX, minY }, camera); + + if (max.x < screenWidth) camera.offset.x = screenWidth - (max.x - screenWidth/2); + if (max.y < screenHeight) camera.offset.y = screenHeight - (max.y - screenHeight/2); + if (min.x > 0) camera.offset.x = screenWidth/2 - min.x; + if (min.y > 0) camera.offset.y = screenHeight/2 - min.y; + //---------------------------------------------------------------------------------- + + // Toggle events recording + if (IsKeyPressed(KEY_ONE)) + { + if (!eventPlaying) + { + if (eventRecording) + { + StopAutomationEventRecording(); + eventRecording = false; + + ExportAutomationEventList(aelist, "automation.rae"); + } + else + { + StartAutomationEventRecording(); + eventRecording = true; + } + } + } + + if (eventPlaying) + { + if (playFrameCounter == aelist.events[currentFrame].frame) + { + PlayAutomationEvent(aelist.events[currentFrame]); + currentFrame++; + + if (currentFrame == aelist.count) + { + eventPlaying = false; + currentFrame = 0; + playFrameCounter = 0; + } + } + + playFrameCounter++; + } + + if (eventRecording || eventPlaying) frameCounter++; + else frameCounter = 0; + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(LIGHTGRAY); + + BeginMode2D(camera); + + // Draw environment elements + for (int i = 0; i < MAX_ENVIRONMENT_ELEMENTS; i++) + { + DrawRectangleRec(envElements[i].rect, envElements[i].color); + } + + // Draw player rectangle + DrawRectangleRec((Rectangle){ player.position.x - 20, player.position.y - 40, 40, 40 }, RED); + + EndMode2D(); + + // Draw automation events recording indicator + if (eventRecording) + { + if (((frameCounter/15)%2) == 1) + { + DrawCircle(GetScreenWidth() - 200, 20, 10, MAROON); + DrawText(TextFormat("RECORDING EVENTS... [%i]", aelist.count), GetScreenWidth() - 180, 15, 10, RED); + } + } + else if (eventPlaying) + { + if (((frameCounter/15)%2) == 1) + { + DrawTriangle((Vector2){ GetScreenWidth() - 200, 10 }, (Vector2){ GetScreenWidth() - 200, 30 }, (Vector2){ GetScreenWidth() - 200 + 20, 20 }, DARKGREEN); + DrawText(TextFormat("PLAYING EVENTS... [%i]", currentFrame), GetScreenWidth() - 170, 15, 10, LIME); + } + } + + DrawText("Controls:", 20, 20, 10, BLACK); + DrawText("- Right/Left to move", 30, 40, 10, DARKGRAY); + DrawText("- Space to jump", 30, 60, 10, DARKGRAY); + DrawText("- Mouse Wheel to Zoom in-out, R to reset zoom", 30, 80, 10, DARKGRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/projects/VS2022/examples/core_automation_events.vcxproj b/projects/VS2022/examples/core_automation_events.vcxproj new file mode 100644 index 000000000..e70a6b1ea --- /dev/null +++ b/projects/VS2022/examples/core_automation_events.vcxproj @@ -0,0 +1,390 @@ + + + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + Win32 + + + Release + x64 + + + + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26} + Win32Proj + core_automation_events + 10.0 + core_automation_events + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\core + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index 007f796f9..167c60f3f 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -273,6 +273,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_sound_multi", "exampl EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_2d_camera_split_screen", "examples\core_2d_camera_split_screen.vcxproj", "{CC62F7DB-D089-4677-8575-CAB7A7815C43}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "core_automation_events", "examples\core_automation_events.vcxproj", "{7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|x64 = Debug.DLL|x64 @@ -2297,6 +2299,22 @@ Global {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x64.Build.0 = Release|x64 {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x86.ActiveCfg = Release|Win32 {CC62F7DB-D089-4677-8575-CAB7A7815C43}.Release|x86.Build.0 = Release|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x64.ActiveCfg = Debug|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x64.Build.0 = Debug|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x86.ActiveCfg = Debug|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Debug|x86.Build.0 = Debug|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x64.ActiveCfg = Release|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x64.Build.0 = Release|x64 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x86.ActiveCfg = Release|Win32 + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2435,6 +2453,7 @@ Global {3755E9F4-CB48-4EC3-B561-3B85964EBDEF} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {F81C5819-85B4-4D2E-B6DC-104A7634461B} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} {CC62F7DB-D089-4677-8575-CAB7A7815C43} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} + {7AF97D44-707E-48DC-81CB-C9D8D7C9ED26} = {6C82BAAE-BDDF-457D-8FA8-7E2490B07035} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} diff --git a/src/config.h b/src/config.h index 7886aeeba..96fdd065f 100644 --- a/src/config.h +++ b/src/config.h @@ -63,7 +63,7 @@ // Support CompressData() and DecompressData() functions #define SUPPORT_COMPRESSION_API 1 // Support automatic generated events, loading and recording of those events when required -//#define SUPPORT_EVENTS_AUTOMATION 1 +#define SUPPORT_AUTOMATION_EVENTS 1 // Support custom frame control, only for advance users // By default EndDrawing() does this job: draws everything + SwapScreenBuffer() + manage frame timing + PollInputEvents() // Enabling this flag allows manual control of the frame processes, use at your own risk @@ -85,6 +85,7 @@ #define MAX_DECOMPRESSION_SIZE 64 // Max size allocated for decompression in MB +#define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record //------------------------------------------------------------------------------------ // Module: rlgl - Configuration values diff --git a/src/raylib.h b/src/raylib.h index b172562d0..7b3eddaee 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -506,6 +506,20 @@ typedef struct FilePathList { char **paths; // Filepaths entries } FilePathList; +// Automation event (opaque struct) +typedef struct AutomationEvent { + unsigned int frame; // Event frame + unsigned int type; // Event type (AutomationEventType) + int params[4]; // Event parameters (if required) +} AutomationEvent; + +// Automation event list +typedef struct AutomationEventList { + unsigned int capacity; // Events max entries (MAX_AUTOMATION_EVENTS) + unsigned int count; // Events entries count + AutomationEvent *events; // Events entries +} AutomationEventList; + //---------------------------------------------------------------------------------- // Enumerators Definition //---------------------------------------------------------------------------------- @@ -1114,6 +1128,15 @@ RLAPI unsigned char *DecompressData(const unsigned char *compData, int compDataS RLAPI char *EncodeDataBase64(const unsigned char *data, int dataSize, int *outputSize); // Encode data to Base64 string, memory must be MemFree() RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize); // Decode Base64 string data, memory must be MemFree() +// Automation events functionality +RLAPI AutomationEventList LoadAutomationEventList(const char *fileName); // Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS +RLAPI void UnloadAutomationEventList(AutomationEventList *list); // Unload automation events list from file +RLAPI bool ExportAutomationEventList(AutomationEventList list, const char *fileName); // Export automation events list as text file +RLAPI void SetAutomationEventList(AutomationEventList *list); // Set automation event list to record to +RLAPI void StartAutomationEventRecording(void); // Start recording automation events (AutomationEventList must be set) +RLAPI void StopAutomationEventRecording(void); // Stop recording automation events +RLAPI void PlayAutomationEvent(AutomationEvent event); // Play a recorded automation event + //------------------------------------------------------------------------------------ // Input Handling Functions (Module: core) //------------------------------------------------------------------------------------ diff --git a/src/rcore.c b/src/rcore.c index ef3beaee3..67c79efa9 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3,17 +3,17 @@ * rcore - Window/display management, Graphic device/context management and input management * * PLATFORMS SUPPORTED: -* - PLATFORM_DESKTOP: +* - PLATFORM_DESKTOP: * > Windows (Win32, Win64) * > Linux (X11/Wayland desktop mode) * > macOS/OSX (x64, arm64) * > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) -* - PLATFORM_WEB: +* - PLATFORM_WEB: * > HTML5 (WebAssembly) -* - PLATFORM_DRM: +* - PLATFORM_DRM: * > Raspberry Pi 0-5 * > Linux native mode (KMS driver) -* - PLATFORM_ANDROID: +* - PLATFORM_ANDROID: * > Android (ARM, ARM64) * * CONFIGURATION: @@ -48,8 +48,8 @@ * provided by stb_image and stb_image_write libraries, so, those libraries must be enabled on textures module * for linkage * -* #define SUPPORT_EVENTS_AUTOMATION -* Support automatic generated events, loading and recording of those events when required +* #define SUPPORT_AUTOMATION_EVENTS +* Support automatic events recording and playing, useful for automated testing systems or AI based game playing * * DEPENDENCIES: * raymath - 3D math functionality (Vector2, Vector3, Matrix, Quaternion) @@ -183,9 +183,8 @@ bool gifRecording = false; // GIF recording state MsfGifState gifState = { 0 }; // MSGIF context state #endif -#if defined(SUPPORT_EVENTS_AUTOMATION) -#define MAX_CODE_AUTOMATION_EVENTS 16384 - +#if defined(SUPPORT_AUTOMATION_EVENTS) +// Automation events type typedef enum AutomationEventType { EVENT_NONE = 0, // Input events @@ -212,12 +211,12 @@ typedef enum AutomationEventType { WINDOW_MINIMIZE, // no params WINDOW_RESIZE, // param[0]: width, param[1]: height // Custom events - ACTION_TAKE_SCREENSHOT, - ACTION_SETTARGETFPS + ACTION_TAKE_SCREENSHOT, // no params + ACTION_SETTARGETFPS // param[0]: fps } AutomationEventType; -// Event type -// Used to enable events flags +// Event type to config events flags +// TODO: Not used at the moment typedef enum { EVENT_INPUT_KEYBOARD = 0, EVENT_INPUT_MOUSE = 1, @@ -228,6 +227,7 @@ typedef enum { EVENT_CUSTOM = 32 } EventType; +// Event type name strings, required for export static const char *autoEventTypeName[] = { "EVENT_NONE", "INPUT_KEY_UP", @@ -255,19 +255,19 @@ static const char *autoEventTypeName[] = { "ACTION_SETTARGETFPS" }; +/* // Automation event (24 bytes) -typedef struct AutomationEvent { +// NOTE: Opaque struct, internal to raylib +struct AutomationEvent { unsigned int frame; // Event frame unsigned int type; // Event type (AutomationEventType) int params[4]; // Event parameters (if required) -} AutomationEvent; +}; +*/ -static AutomationEvent *events = NULL; // Events array -static unsigned int eventCount = 0; // Events count -static bool eventsPlaying = false; // Play events -static bool eventsRecording = false; // Record events - -//static short eventsEnabled = 0b0000001111111111; // Events enabled for checking +static AutomationEventList *currentEventList = NULL; // Current automation events list, set by user, keep internal pointer +static bool automationEventRecording = false; // Recording automation events flag +//static short automationEventEnabled = 0b0000001111111111; // TODO: Automation events enabled for recording/playing #endif //----------------------------------------------------------------------------------- @@ -291,11 +291,8 @@ static void SetupViewport(int width, int height); // Set viewport for static void ScanDirectoryFiles(const char *basePath, FilePathList *list, const char *filter); // Scan all files and directories in a base path static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *list, const char *filter); // Scan all files and directories recursively from a base path -#if defined(SUPPORT_EVENTS_AUTOMATION) -static void LoadAutomationEvents(const char *fileName); // Load automation events from file -static void ExportAutomationEvents(const char *fileName); // Export recorded automation events into a file -static void RecordAutomationEvent(unsigned int frame); // Record frame events (to internal events array) -static void PlayAutomationEvent(unsigned int frame); // Play frame events (from internal events array) +#if defined(SUPPORT_AUTOMATION_EVENTS) +static void RecordAutomationEvent(void); // Record frame events (to internal events array) #endif #if defined(_WIN32) @@ -304,14 +301,14 @@ void __stdcall Sleep(unsigned long msTimeout); // Required for: Wai #endif #if !defined(SUPPORT_MODULE_RTEXT) -const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' +const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' #endif // !SUPPORT_MODULE_RTEXT // Include platform-specific submodules #if defined(PLATFORM_DESKTOP) #include "platforms/rcore_desktop.c" #elif defined(PLATFORM_DESKTOP_SDL) - #include "platforms/rcore_desktop_sdl.c" + #include "platforms/rcore_desktop_sdl.c" #elif defined(PLATFORM_WEB) #include "platforms/rcore_web.c" #elif defined(PLATFORM_DRM) @@ -434,12 +431,12 @@ void InitWindow(int width, int height, const char *title) CORE.Input.Mouse.scale = (Vector2){ 1.0f, 1.0f }; CORE.Input.Mouse.cursor = MOUSE_CURSOR_ARROW; CORE.Input.Gamepad.lastButtonPressed = GAMEPAD_BUTTON_UNKNOWN; - + // Initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- InitPlatform(); //-------------------------------------------------------------- - + // Initialize rlgl default data (buffers and shaders) // NOTE: CORE.Window.currentFbo.width and CORE.Window.currentFbo.height not used, just stored as globals in rlgl rlglInit(CORE.Window.currentFbo.width, CORE.Window.currentFbo.height); @@ -485,9 +482,6 @@ void InitWindow(int width, int height, const char *title) #endif CORE.Time.frameCounter = 0; -#if defined(SUPPORT_EVENTS_AUTOMATION) - events = (AutomationEvent *)RL_CALLOC(MAX_CODE_AUTOMATION_EVENTS, sizeof(AutomationEvent)); -#endif // Initialize random seed SetRandomSeed((unsigned int)time(NULL)); @@ -512,14 +506,10 @@ void CloseWindow(void) rlglClose(); // De-init rlgl // De-initialize platform - //-------------------------------------------------------------- + //-------------------------------------------------------------- ClosePlatform(); //-------------------------------------------------------------- -#if defined(SUPPORT_EVENTS_AUTOMATION) - RL_FREE(events); -#endif - CORE.Window.ready = false; TRACELOG(LOG_INFO, "Window closed successfully"); } @@ -684,34 +674,6 @@ void EndDrawing(void) } #endif -#if defined(SUPPORT_EVENTS_AUTOMATION) - // Draw record/play indicator - if (eventsRecording) - { - gifFrameCounter++; - - if (((gifFrameCounter/15)%2) == 1) - { - DrawCircle(30, CORE.Window.screen.height - 20, 10, MAROON); - DrawText("EVENTS RECORDING", 50, CORE.Window.screen.height - 25, 10, RED); - } - - rlDrawRenderBatchActive(); // Update and draw internal render batch - } - else if (eventsPlaying) - { - gifFrameCounter++; - - if (((gifFrameCounter/15)%2) == 1) - { - DrawCircle(30, CORE.Window.screen.height - 20, 10, LIME); - DrawText("EVENTS PLAYING", 50, CORE.Window.screen.height - 25, 10, GREEN); - } - - rlDrawRenderBatchActive(); // Update and draw internal render batch - } -#endif - #if !defined(SUPPORT_CUSTOM_FRAME_CONTROL) SwapScreenBuffer(); // Copy back buffer to front buffer (screen) @@ -775,16 +737,9 @@ void EndDrawing(void) } #endif // SUPPORT_SCREEN_CAPTURE -#if defined(SUPPORT_EVENTS_AUTOMATION) - // Events recording and playing logic - if (eventsRecording) RecordAutomationEvent(CORE.Time.frameCounter); - else if (eventsPlaying) - { - // TODO: When should we play? After/before/replace PollInputEvents()? - if (CORE.Time.frameCounter >= eventCount) eventsPlaying = false; - PlayAutomationEvent(CORE.Time.frameCounter); - } -#endif // SUPPORT_EVENTS_AUTOMATION +#if defined(SUPPORT_AUTOMATION_EVENTS) + if (automationEventRecording) RecordAutomationEvent(); // Event recording +#endif CORE.Time.frameCounter++; } @@ -1470,7 +1425,7 @@ float GetFrameTime(void) //---------------------------------------------------------------------------------- // NOTE: Functions with a platform-specific implementation on rcore_.c -//void SwapScreenBuffer(void); +//void SwapScreenBuffer(void); //void PollInputEvents(void); // Wait for some time (stop program execution) @@ -1481,7 +1436,7 @@ float GetFrameTime(void) void WaitTime(double seconds) { if (seconds < 0) return; - + #if defined(SUPPORT_BUSY_WAIT_LOOP) || defined(SUPPORT_PARTIALBUSY_WAIT_LOOP) double destinationTime = GetTime() + seconds; #endif @@ -2180,6 +2135,237 @@ unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize) return decodedData; } +//---------------------------------------------------------------------------------- +// Module Functions Definition: Automation Events Recording and Playing +//---------------------------------------------------------------------------------- + +// Load automation events list from file, NULL for empty list, capacity = MAX_AUTOMATION_EVENTS +AutomationEventList LoadAutomationEventList(const char *fileName) +{ + AutomationEventList list = { 0 }; + + // Allocate and empty automation event list, ready to record new events + list.events = (AutomationEvent *)RL_CALLOC(MAX_AUTOMATION_EVENTS, sizeof(AutomationEvent)); + list.capacity = MAX_AUTOMATION_EVENTS; + +#if defined(SUPPORT_AUTOMATION_EVENTS) + if (fileName == NULL) TRACELOG(LOG_INFO, "AUTOMATION: New empty events list loaded successfully"); + else + { + // Load automation events file (binary) + /* + //int dataSize = 0; + //unsigned char *data = LoadFileData(fileName, &dataSize); + + FILE *raeFile = fopen(fileName, "rb"); + unsigned char fileId[4] = { 0 }; + + fread(fileId, 1, 4, raeFile); + + if ((fileId[0] == 'r') && (fileId[1] == 'A') && (fileId[2] == 'E') && (fileId[1] == ' ')) + { + fread(&eventCount, sizeof(int), 1, raeFile); + TRACELOG(LOG_WARNING, "Events loaded: %i\n", eventCount); + fread(events, sizeof(AutomationEvent), eventCount, raeFile); + } + + fclose(raeFile); + */ + + // Load events file (text) + //unsigned char *buffer = LoadFileText(fileName); + FILE *raeFile = fopen(fileName, "rt"); + + if (raeFile != NULL) + { + unsigned int counter = 0; + char buffer[256] = { 0 }; + char eventDesc[64] = { 0 }; + + fgets(buffer, 256, raeFile); + + while (!feof(raeFile)) + { + switch (buffer[0]) + { + case 'c': sscanf(buffer, "c %i", &list.count); break; + case 'e': + { + sscanf(buffer, "e %d %d %d %d %d %d %[^\n]s", &list.events[counter].frame, &list.events[counter].type, + &list.events[counter].params[0], &list.events[counter].params[1], &list.events[counter].params[2], &list.events[counter].params[3], eventDesc); + + counter++; + } break; + default: break; + } + + fgets(buffer, 256, raeFile); + } + + if (counter != list.count) + { + TRACELOG(LOG_WARNING, "AUTOMATION: Events read from file [%i] do not mach event count specified [%i]", counter, list.count); + list.count = counter; + } + + fclose(raeFile); + + TRACELOG(LOG_INFO, "AUTOMATION: Events file loaded successfully"); + } + + TRACELOG(LOG_INFO, "AUTOMATION: Events loaded from file: %i", list.count); + } +#endif + return list; +} + +// Unload automation events list from file +void UnloadAutomationEventList(AutomationEventList *list) +{ +#if defined(SUPPORT_AUTOMATION_EVENTS) + RL_FREE(list->events); + list->events = NULL; + list->count = 0; + list->capacity = 0; +#endif +} + +// Export automation events list as text file +bool ExportAutomationEventList(AutomationEventList list, const char *fileName) +{ + bool success = false; + +#if defined(SUPPORT_AUTOMATION_EVENTS) + // Export events as binary file + // TODO: Save to memory buffer and SaveFileData() + /* + unsigned char fileId[4] = "rAE "; + FILE *raeFile = fopen(fileName, "wb"); + fwrite(fileId, sizeof(unsigned char), 4, raeFile); + fwrite(&eventCount, sizeof(int), 1, raeFile); + fwrite(events, sizeof(AutomationEvent), eventCount, raeFile); + fclose(raeFile); + */ + + // Export events as text + // TODO: Save to memory buffer and SaveFileText() + char *txtData = (char *)RL_CALLOC(256*list.count + 2048, sizeof(char)); // 256 characters per line plus some header + + int byteCount = 0; + byteCount += sprintf(txtData + byteCount, "#\n"); + byteCount += sprintf(txtData + byteCount, "# Automation events exporter v1.0 - raylib automation events list\n"); + byteCount += sprintf(txtData + byteCount, "#\n"); + byteCount += sprintf(txtData + byteCount, "# c \n"); + byteCount += sprintf(txtData + byteCount, "# e // \n"); + byteCount += sprintf(txtData + byteCount, "#\n"); + byteCount += sprintf(txtData + byteCount, "# more info and bugs-report: github.com/raysan5/raylib\n"); + byteCount += sprintf(txtData + byteCount, "# feedback and support: ray[at]raylib.com\n"); + byteCount += sprintf(txtData + byteCount, "#\n"); + byteCount += sprintf(txtData + byteCount, "# Copyright (c) 2023-2024 Ramon Santamaria (@raysan5)\n"); + byteCount += sprintf(txtData + byteCount, "#\n\n"); + + // Add events data + byteCount += sprintf(txtData + byteCount, "c %i\n", list.count); + for (int i = 0; i < list.count; i++) + { + byteCount += sprintf(txtData + byteCount, "e %i %i %i %i %i %i // Event: %s\n", list.events[i].frame, list.events[i].type, + list.events[i].params[0], list.events[i].params[1], list.events[i].params[2], list.events[i].params[3], autoEventTypeName[list.events[i].type]); + } + + // NOTE: Text data size exported is determined by '\0' (NULL) character + success = SaveFileText(fileName, txtData); + + RL_FREE(txtData); +#endif + + return success; +} + +// Setup automation event list to record to +void SetAutomationEventList(AutomationEventList *list) +{ +#if defined(SUPPORT_AUTOMATION_EVENTS) + currentEventList = list; +#endif +} + +// Start recording automation events (AutomationEventList must be set) +void StartAutomationEventRecording(void) +{ +#if defined(SUPPORT_AUTOMATION_EVENTS) + automationEventRecording = true; +#endif +} + +// Stop recording automation events +void StopAutomationEventRecording(void) +{ +#if defined(SUPPORT_AUTOMATION_EVENTS) + automationEventRecording = false; +#endif +} + +// Play a recorded automation event +void PlayAutomationEvent(AutomationEvent event) +{ +#if defined(SUPPORT_AUTOMATION_EVENTS) + // WARNING: When should event be played? After/before/replace PollInputEvents()? -> Up to the user! + + if (!automationEventRecording) // TODO: Allow recording events while playing? + { + switch (event.type) + { + // Input event + case INPUT_KEY_UP: CORE.Input.Keyboard.currentKeyState[event.params[0]] = false; break; // param[0]: key + case INPUT_KEY_DOWN: CORE.Input.Keyboard.currentKeyState[event.params[0]] = true; break; // param[0]: key + case INPUT_MOUSE_BUTTON_UP: CORE.Input.Mouse.currentButtonState[event.params[0]] = false; break; // param[0]: key + case INPUT_MOUSE_BUTTON_DOWN: CORE.Input.Mouse.currentButtonState[event.params[0]] = true; break; // param[0]: key + case INPUT_MOUSE_POSITION: // param[0]: x, param[1]: y + { + CORE.Input.Mouse.currentPosition.x = (float)event.params[0]; + CORE.Input.Mouse.currentPosition.y = (float)event.params[1]; + } break; + case INPUT_MOUSE_WHEEL_MOTION: // param[0]: x delta, param[1]: y delta + { + CORE.Input.Mouse.currentWheelMove.x = (float)event.params[0]; break; + CORE.Input.Mouse.currentWheelMove.y = (float)event.params[1]; break; + } break; + case INPUT_TOUCH_UP: CORE.Input.Touch.currentTouchState[event.params[0]] = false; break; // param[0]: id + case INPUT_TOUCH_DOWN: CORE.Input.Touch.currentTouchState[event.params[0]] = true; break; // param[0]: id + case INPUT_TOUCH_POSITION: // param[0]: id, param[1]: x, param[2]: y + { + CORE.Input.Touch.position[event.params[0]].x = (float)event.params[1]; + CORE.Input.Touch.position[event.params[0]].y = (float)event.params[2]; + } break; + case INPUT_GAMEPAD_CONNECT: CORE.Input.Gamepad.ready[event.params[0]] = true; break; // param[0]: gamepad + case INPUT_GAMEPAD_DISCONNECT: CORE.Input.Gamepad.ready[event.params[0]] = false; break; // param[0]: gamepad + case INPUT_GAMEPAD_BUTTON_UP: CORE.Input.Gamepad.currentButtonState[event.params[0]][event.params[1]] = false; break; // param[0]: gamepad, param[1]: button + case INPUT_GAMEPAD_BUTTON_DOWN: CORE.Input.Gamepad.currentButtonState[event.params[0]][event.params[1]] = true; break; // param[0]: gamepad, param[1]: button + case INPUT_GAMEPAD_AXIS_MOTION: // param[0]: gamepad, param[1]: axis, param[2]: delta + { + CORE.Input.Gamepad.axisState[event.params[0]][event.params[1]] = ((float)event.params[2]/32768.0f); + } break; + case INPUT_GESTURE: GESTURES.current = event.params[0]; break; // param[0]: gesture (enum Gesture) -> rgestures.h: GESTURES.current + + // Window event + case WINDOW_CLOSE: CORE.Window.shouldClose = true; break; + case WINDOW_MAXIMIZE: MaximizeWindow(); break; + case WINDOW_MINIMIZE: MinimizeWindow(); break; + case WINDOW_RESIZE: SetWindowSize(event.params[0], event.params[1]); break; + + // Custom event + case ACTION_TAKE_SCREENSHOT: + { + TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); + screenshotCounter++; + } break; + case ACTION_SETTARGETFPS: SetTargetFPS(event.params[0]); break; + default: break; + } + } +#endif +} + //---------------------------------------------------------------------------------- // Module Functions Definition: Input Handling: Keyboard //---------------------------------------------------------------------------------- @@ -2793,233 +2979,180 @@ static void ScanDirectoryFilesRecursively(const char *basePath, FilePathList *fi else TRACELOG(LOG_WARNING, "FILEIO: Directory cannot be opened (%s)", basePath); } -#if defined(SUPPORT_EVENTS_AUTOMATION) -// NOTE: Loading happens over AutomationEvent *events -// TODO: This system should probably be redesigned -static void LoadAutomationEvents(const char *fileName) +#if defined(SUPPORT_AUTOMATION_EVENTS) +// Automation event recording +// NOTE: Recording is by default done at EndDrawing(), after PollInputEvents() +static void RecordAutomationEvent(void) { - // Load events file (binary) - /* - FILE *repFile = fopen(fileName, "rb"); - unsigned char fileId[4] = { 0 }; + // Checking events in current frame and save them into currentEventList + // TODO: How important is the current frame? Could it be modified? + + if (currentEventList->count == currentEventList->capacity) return; // Security check - fread(fileId, 1, 4, repFile); - - if ((fileId[0] == 'r') && (fileId[1] == 'E') && (fileId[2] == 'P') && (fileId[1] == ' ')) - { - fread(&eventCount, sizeof(int), 1, repFile); - TRACELOG(LOG_WARNING, "Events loaded: %i\n", eventCount); - fread(events, sizeof(AutomationEvent), eventCount, repFile); - } - - fclose(repFile); - */ - - // Load events file (text) - FILE *repFile = fopen(fileName, "rt"); - - if (repFile != NULL) - { - unsigned int count = 0; - char buffer[256] = { 0 }; - - fgets(buffer, 256, repFile); - - while (!feof(repFile)) - { - if (buffer[0] == 'c') sscanf(buffer, "c %i", &eventCount); - else if (buffer[0] == 'e') - { - sscanf(buffer, "e %d %d %d %d %d", &events[count].frame, &events[count].type, - &events[count].params[0], &events[count].params[1], &events[count].params[2]); - - count++; - } - - fgets(buffer, 256, repFile); - } - - if (count != eventCount) TRACELOG(LOG_WARNING, "Events count provided is different than count"); - - fclose(repFile); - } - - TRACELOG(LOG_WARNING, "Events loaded: %i", eventCount); -} - -// Export recorded events into a file -static void ExportAutomationEvents(const char *fileName) -{ - unsigned char fileId[4] = "rEP "; - - // Save as binary - /* - FILE *repFile = fopen(fileName, "wb"); - fwrite(fileId, sizeof(unsigned char), 4, repFile); - fwrite(&eventCount, sizeof(int), 1, repFile); - fwrite(events, sizeof(AutomationEvent), eventCount, repFile); - fclose(repFile); - */ - - // Export events as text - FILE *repFile = fopen(fileName, "wt"); - - if (repFile != NULL) - { - fprintf(repFile, "# Automation events list\n"); - fprintf(repFile, "# c \n"); - fprintf(repFile, "# e // \n"); - - fprintf(repFile, "c %i\n", eventCount); - for (int i = 0; i < eventCount; i++) - { - fprintf(repFile, "e %i %i %i %i %i // %s\n", events[i].frame, events[i].type, - events[i].params[0], events[i].params[1], events[i].params[2], autoEventTypeName[events[i].type]); - } - - fclose(repFile); - } -} - -// EndDrawing() -> After PollInputEvents() -// Check event in current frame and save into the events[i] array -static void RecordAutomationEvent(unsigned int frame) -{ + // Keyboard input events recording + //------------------------------------------------------------------------------------- for (int key = 0; key < MAX_KEYBOARD_KEYS; key++) { - // INPUT_KEY_UP (only saved once) + // Event type: INPUT_KEY_UP (only saved once) if (CORE.Input.Keyboard.previousKeyState[key] && !CORE.Input.Keyboard.currentKeyState[key]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_KEY_UP; - events[eventCount].params[0] = key; - events[eventCount].params[1] = 0; - events[eventCount].params[2] = 0; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_KEY_UP; + currentEventList->events[currentEventList->count].params[0] = key; + currentEventList->events[currentEventList->count].params[1] = 0; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_KEY_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_KEY_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; } + + if (currentEventList->count == currentEventList->capacity) return; // Security check - // INPUT_KEY_DOWN + // Event type: INPUT_KEY_DOWN if (CORE.Input.Keyboard.currentKeyState[key]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_KEY_DOWN; - events[eventCount].params[0] = key; - events[eventCount].params[1] = 0; - events[eventCount].params[2] = 0; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_KEY_DOWN; + currentEventList->events[currentEventList->count].params[0] = key; + currentEventList->events[currentEventList->count].params[1] = 0; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_KEY_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_KEY_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; } + + if (currentEventList->count == currentEventList->capacity) return; // Security check } + //------------------------------------------------------------------------------------- + // Mouse input currentEventList->events recording + //------------------------------------------------------------------------------------- for (int button = 0; button < MAX_MOUSE_BUTTONS; button++) { - // INPUT_MOUSE_BUTTON_UP + // Event type: INPUT_MOUSE_BUTTON_UP if (CORE.Input.Mouse.previousButtonState[button] && !CORE.Input.Mouse.currentButtonState[button]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_MOUSE_BUTTON_UP; - events[eventCount].params[0] = button; - events[eventCount].params[1] = 0; - events[eventCount].params[2] = 0; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_MOUSE_BUTTON_UP; + currentEventList->events[currentEventList->count].params[0] = button; + currentEventList->events[currentEventList->count].params[1] = 0; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_BUTTON_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_BUTTON_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; } + + if (currentEventList->count == currentEventList->capacity) return; // Security check - // INPUT_MOUSE_BUTTON_DOWN + // Event type: INPUT_MOUSE_BUTTON_DOWN if (CORE.Input.Mouse.currentButtonState[button]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_MOUSE_BUTTON_DOWN; - events[eventCount].params[0] = button; - events[eventCount].params[1] = 0; - events[eventCount].params[2] = 0; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_MOUSE_BUTTON_DOWN; + currentEventList->events[currentEventList->count].params[0] = button; + currentEventList->events[currentEventList->count].params[1] = 0; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_BUTTON_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_BUTTON_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; } + + if (currentEventList->count == currentEventList->capacity) return; // Security check } - // INPUT_MOUSE_POSITION (only saved if changed) + // Event type: INPUT_MOUSE_POSITION (only saved if changed) if (((int)CORE.Input.Mouse.currentPosition.x != (int)CORE.Input.Mouse.previousPosition.x) || ((int)CORE.Input.Mouse.currentPosition.y != (int)CORE.Input.Mouse.previousPosition.y)) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_MOUSE_POSITION; - events[eventCount].params[0] = (int)CORE.Input.Mouse.currentPosition.x; - events[eventCount].params[1] = (int)CORE.Input.Mouse.currentPosition.y; - events[eventCount].params[2] = 0; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_MOUSE_POSITION; + currentEventList->events[currentEventList->count].params[0] = (int)CORE.Input.Mouse.currentPosition.x; + currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Mouse.currentPosition.y; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_POSITION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_POSITION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; + + if (currentEventList->count == currentEventList->capacity) return; // Security check } - // INPUT_MOUSE_WHEEL_MOTION + // Event type: INPUT_MOUSE_WHEEL_MOTION if (((int)CORE.Input.Mouse.currentWheelMove.x != (int)CORE.Input.Mouse.previousWheelMove.x) || ((int)CORE.Input.Mouse.currentWheelMove.y != (int)CORE.Input.Mouse.previousWheelMove.y)) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_MOUSE_WHEEL_MOTION; - events[eventCount].params[0] = (int)CORE.Input.Mouse.currentWheelMove.x; - events[eventCount].params[1] = (int)CORE.Input.Mouse.currentWheelMove.y;; - events[eventCount].params[2] = 0; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_MOUSE_WHEEL_MOTION; + currentEventList->events[currentEventList->count].params[0] = (int)CORE.Input.Mouse.currentWheelMove.x; + currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Mouse.currentWheelMove.y;; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_MOUSE_WHEEL_MOTION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_WHEEL_MOTION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; + + if (currentEventList->count == currentEventList->capacity) return; // Security check } + //------------------------------------------------------------------------------------- + // Touch input currentEventList->events recording + //------------------------------------------------------------------------------------- for (int id = 0; id < MAX_TOUCH_POINTS; id++) { - // INPUT_TOUCH_UP + // Event type: INPUT_TOUCH_UP if (CORE.Input.Touch.previousTouchState[id] && !CORE.Input.Touch.currentTouchState[id]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_TOUCH_UP; - events[eventCount].params[0] = id; - events[eventCount].params[1] = 0; - events[eventCount].params[2] = 0; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_TOUCH_UP; + currentEventList->events[currentEventList->count].params[0] = id; + currentEventList->events[currentEventList->count].params[1] = 0; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_TOUCH_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; } + + if (currentEventList->count == currentEventList->capacity) return; // Security check - // INPUT_TOUCH_DOWN + // Event type: INPUT_TOUCH_DOWN if (CORE.Input.Touch.currentTouchState[id]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_TOUCH_DOWN; - events[eventCount].params[0] = id; - events[eventCount].params[1] = 0; - events[eventCount].params[2] = 0; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_TOUCH_DOWN; + currentEventList->events[currentEventList->count].params[0] = id; + currentEventList->events[currentEventList->count].params[1] = 0; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_TOUCH_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; } + + if (currentEventList->count == currentEventList->capacity) return; // Security check - // INPUT_TOUCH_POSITION + // Event type: INPUT_TOUCH_POSITION // TODO: It requires the id! /* if (((int)CORE.Input.Touch.currentPosition[id].x != (int)CORE.Input.Touch.previousPosition[id].x) || ((int)CORE.Input.Touch.currentPosition[id].y != (int)CORE.Input.Touch.previousPosition[id].y)) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_TOUCH_POSITION; - events[eventCount].params[0] = id; - events[eventCount].params[1] = (int)CORE.Input.Touch.currentPosition[id].x; - events[eventCount].params[2] = (int)CORE.Input.Touch.currentPosition[id].y; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_TOUCH_POSITION; + currentEventList->events[currentEventList->count].params[0] = id; + currentEventList->events[currentEventList->count].params[1] = (int)CORE.Input.Touch.currentPosition[id].x; + currentEventList->events[currentEventList->count].params[2] = (int)CORE.Input.Touch.currentPosition[id].y; - TRACELOG(LOG_INFO, "[%i] INPUT_TOUCH_POSITION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_POSITION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; } */ + + if (currentEventList->count == currentEventList->capacity) return; // Security check } + //------------------------------------------------------------------------------------- + // Gamepad input currentEventList->events recording + //------------------------------------------------------------------------------------- for (int gamepad = 0; gamepad < MAX_GAMEPADS; gamepad++) { - // INPUT_GAMEPAD_CONNECT + // Event type: INPUT_GAMEPAD_CONNECT /* if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) && (CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to ready @@ -3028,7 +3161,7 @@ static void RecordAutomationEvent(unsigned int frame) } */ - // INPUT_GAMEPAD_DISCONNECT + // Event type: INPUT_GAMEPAD_DISCONNECT /* if ((CORE.Input.Gamepad.currentState[gamepad] != CORE.Input.Gamepad.previousState[gamepad]) && (!CORE.Input.Gamepad.currentState[gamepad])) // Check if changed to not-ready @@ -3039,122 +3172,84 @@ static void RecordAutomationEvent(unsigned int frame) for (int button = 0; button < MAX_GAMEPAD_BUTTONS; button++) { - // INPUT_GAMEPAD_BUTTON_UP + // Event type: INPUT_GAMEPAD_BUTTON_UP if (CORE.Input.Gamepad.previousButtonState[gamepad][button] && !CORE.Input.Gamepad.currentButtonState[gamepad][button]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_GAMEPAD_BUTTON_UP; - events[eventCount].params[0] = gamepad; - events[eventCount].params[1] = button; - events[eventCount].params[2] = 0; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_BUTTON_UP; + currentEventList->events[currentEventList->count].params[0] = gamepad; + currentEventList->events[currentEventList->count].params[1] = button; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_GAMEPAD_BUTTON_UP: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_BUTTON_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; } + + if (currentEventList->count == currentEventList->capacity) return; // Security check - // INPUT_GAMEPAD_BUTTON_DOWN + // Event type: INPUT_GAMEPAD_BUTTON_DOWN if (CORE.Input.Gamepad.currentButtonState[gamepad][button]) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_GAMEPAD_BUTTON_DOWN; - events[eventCount].params[0] = gamepad; - events[eventCount].params[1] = button; - events[eventCount].params[2] = 0; + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_BUTTON_DOWN; + currentEventList->events[currentEventList->count].params[0] = gamepad; + currentEventList->events[currentEventList->count].params[1] = button; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_GAMEPAD_BUTTON_DOWN: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_BUTTON_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; } + + if (currentEventList->count == currentEventList->capacity) return; // Security check } for (int axis = 0; axis < MAX_GAMEPAD_AXIS; axis++) { - // INPUT_GAMEPAD_AXIS_MOTION + // Event type: INPUT_GAMEPAD_AXIS_MOTION if (CORE.Input.Gamepad.axisState[gamepad][axis] > 0.1f) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_GAMEPAD_AXIS_MOTION; - events[eventCount].params[0] = gamepad; - events[eventCount].params[1] = axis; - events[eventCount].params[2] = (int)(CORE.Input.Gamepad.axisState[gamepad][axis]*32768.0f); + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_GAMEPAD_AXIS_MOTION; + currentEventList->events[currentEventList->count].params[0] = gamepad; + currentEventList->events[currentEventList->count].params[1] = axis; + currentEventList->events[currentEventList->count].params[2] = (int)(CORE.Input.Gamepad.axisState[gamepad][axis]*32768.0f); - TRACELOG(LOG_INFO, "[%i] INPUT_GAMEPAD_AXIS_MOTION: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_AXIS_MOTION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; } + + if (currentEventList->count == currentEventList->capacity) return; // Security check } } + //------------------------------------------------------------------------------------- - // INPUT_GESTURE + // Gestures input currentEventList->events recording + //------------------------------------------------------------------------------------- if (GESTURES.current != GESTURE_NONE) { - events[eventCount].frame = frame; - events[eventCount].type = INPUT_GESTURE; - events[eventCount].params[0] = GESTURES.current; - events[eventCount].params[1] = 0; - events[eventCount].params[2] = 0; + // Event type: INPUT_GESTURE + currentEventList->events[currentEventList->count].frame = CORE.Time.frameCounter; + currentEventList->events[currentEventList->count].type = INPUT_GESTURE; + currentEventList->events[currentEventList->count].params[0] = GESTURES.current; + currentEventList->events[currentEventList->count].params[1] = 0; + currentEventList->events[currentEventList->count].params[2] = 0; - TRACELOG(LOG_INFO, "[%i] INPUT_GESTURE: %i, %i, %i", events[eventCount].frame, events[eventCount].params[0], events[eventCount].params[1], events[eventCount].params[2]); - eventCount++; + TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GESTURE | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); + currentEventList->count++; + + if (currentEventList->count == currentEventList->capacity) return; // Security check } -} + //------------------------------------------------------------------------------------- -// Play automation event -static void PlayAutomationEvent(unsigned int frame) -{ - for (unsigned int i = 0; i < eventCount; i++) - { - if (events[i].frame == frame) - { - switch (events[i].type) - { - // Input events - case INPUT_KEY_UP: CORE.Input.Keyboard.currentKeyState[events[i].params[0]] = false; break; // param[0]: key - case INPUT_KEY_DOWN: CORE.Input.Keyboard.currentKeyState[events[i].params[0]] = true; break; // param[0]: key - case INPUT_MOUSE_BUTTON_UP: CORE.Input.Mouse.currentButtonState[events[i].params[0]] = false; break; // param[0]: key - case INPUT_MOUSE_BUTTON_DOWN: CORE.Input.Mouse.currentButtonState[events[i].params[0]] = true; break; // param[0]: key - case INPUT_MOUSE_POSITION: // param[0]: x, param[1]: y - { - CORE.Input.Mouse.currentPosition.x = (float)events[i].params[0]; - CORE.Input.Mouse.currentPosition.y = (float)events[i].params[1]; - } break; - case INPUT_MOUSE_WHEEL_MOTION: // param[0]: x delta, param[1]: y delta - { - CORE.Input.Mouse.currentWheelMove.x = (float)events[i].params[0]; break; - CORE.Input.Mouse.currentWheelMove.y = (float)events[i].params[1]; break; - } break; - case INPUT_TOUCH_UP: CORE.Input.Touch.currentTouchState[events[i].params[0]] = false; break; // param[0]: id - case INPUT_TOUCH_DOWN: CORE.Input.Touch.currentTouchState[events[i].params[0]] = true; break; // param[0]: id - case INPUT_TOUCH_POSITION: // param[0]: id, param[1]: x, param[2]: y - { - CORE.Input.Touch.position[events[i].params[0]].x = (float)events[i].params[1]; - CORE.Input.Touch.position[events[i].params[0]].y = (float)events[i].params[2]; - } break; - case INPUT_GAMEPAD_CONNECT: CORE.Input.Gamepad.ready[events[i].params[0]] = true; break; // param[0]: gamepad - case INPUT_GAMEPAD_DISCONNECT: CORE.Input.Gamepad.ready[events[i].params[0]] = false; break; // param[0]: gamepad - case INPUT_GAMEPAD_BUTTON_UP: CORE.Input.Gamepad.currentButtonState[events[i].params[0]][events[i].params[1]] = false; break; // param[0]: gamepad, param[1]: button - case INPUT_GAMEPAD_BUTTON_DOWN: CORE.Input.Gamepad.currentButtonState[events[i].params[0]][events[i].params[1]] = true; break; // param[0]: gamepad, param[1]: button - case INPUT_GAMEPAD_AXIS_MOTION: // param[0]: gamepad, param[1]: axis, param[2]: delta - { - CORE.Input.Gamepad.axisState[events[i].params[0]][events[i].params[1]] = ((float)events[i].params[2]/32768.0f); - } break; - case INPUT_GESTURE: GESTURES.current = events[i].params[0]; break; // param[0]: gesture (enum Gesture) -> rgestures.h: GESTURES.current + // Window events recording + //------------------------------------------------------------------------------------- + // TODO. + //------------------------------------------------------------------------------------- - // Window events - case WINDOW_CLOSE: CORE.Window.shouldClose = true; break; - case WINDOW_MAXIMIZE: MaximizeWindow(); break; - case WINDOW_MINIMIZE: MinimizeWindow(); break; - case WINDOW_RESIZE: SetWindowSize(events[i].params[0], events[i].params[1]); break; - - // Custom events - case ACTION_TAKE_SCREENSHOT: - { - TakeScreenshot(TextFormat("screenshot%03i.png", screenshotCounter)); - screenshotCounter++; - } break; - case ACTION_SETTARGETFPS: SetTargetFPS(events[i].params[0]); break; - default: break; - } - } - } + // Custom actions events recording + //------------------------------------------------------------------------------------- + // TODO. + //------------------------------------------------------------------------------------- } #endif diff --git a/src/rcore.h b/src/rcore.h index 1127585a0..a6955f8cc 100644 --- a/src/rcore.h +++ b/src/rcore.h @@ -89,6 +89,10 @@ #define MAX_DECOMPRESSION_SIZE 64 // Maximum size allocated for decompression in MB #endif +#ifndef MAX_AUTOMATION_EVENTS + #define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record +#endif + // Flags operation macros #define FLAG_SET(n, f) ((n) |= (f)) #define FLAG_CLEAR(n, f) ((n) &= ~(f)) From 654b4e62579aea3dd7dc6ae0018271e2df1c2b55 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 27 Oct 2023 00:45:00 +0200 Subject: [PATCH 0425/1350] Update core_automation_events.c --- examples/core/core_automation_events.c | 85 ++++++++++++++++++-------- 1 file changed, 60 insertions(+), 25 deletions(-) diff --git a/examples/core/core_automation_events.c b/examples/core/core_automation_events.c index d011d6304..c35ce485a 100644 --- a/examples/core/core_automation_events.c +++ b/examples/core/core_automation_events.c @@ -77,7 +77,7 @@ int main(void) int frameCounter = 0; int playFrameCounter = 0; - int currentFrame = 0; + int currentPlayFrame = 0; SetTargetFPS(60); //-------------------------------------------------------------------------------------- @@ -87,7 +87,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - float deltaTime = GetFrameTime(); + float deltaTime = 0.015f;//GetFrameTime(); // Dropped files logic //---------------------------------------------------------------------------------- @@ -106,6 +106,7 @@ int main(void) // Reset scene state to play eventPlaying = true; playFrameCounter = 0; + currentPlayFrame = 0; player.position = (Vector2){ 400, 280 }; player.speed = 0; @@ -163,8 +164,15 @@ int main(void) if (IsKeyPressed(KEY_R)) { - camera.zoom = 1.0f; + // Reset game state player.position = (Vector2){ 400, 280 }; + player.speed = 0; + player.canJump = false; + + camera.target = player.position; + camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f }; + camera.rotation = 0.0f; + camera.zoom = 1.0f; } //---------------------------------------------------------------------------------- @@ -193,7 +201,7 @@ int main(void) //---------------------------------------------------------------------------------- // Toggle events recording - if (IsKeyPressed(KEY_ONE)) + if (IsKeyPressed(KEY_F2)) { if (!eventPlaying) { @@ -211,22 +219,41 @@ int main(void) } } } + else if (IsKeyPressed(KEY_F3)) + { + if (!eventRecording && (aelist.count > 0)) + { + // Reset scene state to play + eventPlaying = true; + playFrameCounter = 0; + currentPlayFrame = 0; + + player.position = (Vector2){ 400, 280 }; + player.speed = 0; + player.canJump = false; + + camera.target = player.position; + camera.offset = (Vector2){ screenWidth/2.0f, screenHeight/2.0f }; + camera.rotation = 0.0f; + camera.zoom = 1.0f; + } + } if (eventPlaying) { - if (playFrameCounter == aelist.events[currentFrame].frame) + if (playFrameCounter >= aelist.events[currentPlayFrame].frame) { - PlayAutomationEvent(aelist.events[currentFrame]); - currentFrame++; + PlayAutomationEvent(aelist.events[currentPlayFrame]); + currentPlayFrame++; - if (currentFrame == aelist.count) + if (currentPlayFrame == aelist.count) { eventPlaying = false; - currentFrame = 0; + currentPlayFrame = 0; playFrameCounter = 0; } } - + playFrameCounter++; } @@ -253,28 +280,36 @@ int main(void) EndMode2D(); + // Draw game controls + DrawRectangle(10, 10, 290, 145, Fade(SKYBLUE, 0.5f)); + DrawRectangleLines(10, 10, 290, 145, Fade(BLUE, 0.8f)); + + DrawText("Controls:", 20, 20, 10, BLACK); + DrawText("- RIGHT | LEFT: Player movement", 30, 40, 10, DARKGRAY); + DrawText("- SPACE: Player jump", 30, 60, 10, DARKGRAY); + DrawText("- R: Reset game state", 30, 80, 10, DARKGRAY); + + DrawText("- F2: START/STOP RECORDING INPUT EVENTS", 30, 110, 10, BLACK); + DrawText("- F3: REPLAY LAST RECORDED INPUT EVENTS", 30, 130, 10, BLACK); + // Draw automation events recording indicator if (eventRecording) { - if (((frameCounter/15)%2) == 1) - { - DrawCircle(GetScreenWidth() - 200, 20, 10, MAROON); - DrawText(TextFormat("RECORDING EVENTS... [%i]", aelist.count), GetScreenWidth() - 180, 15, 10, RED); - } + DrawRectangle(10, 160, 290, 30, Fade(RED, 0.3f)); + DrawRectangleLines(10, 160, 290, 30, Fade(MAROON, 0.8f)); + DrawCircle(30, 175, 10, MAROON); + + if (((frameCounter/15)%2) == 1) DrawText(TextFormat("RECORDING EVENTS... [%i]", aelist.count), 50, 170, 10, MAROON); } else if (eventPlaying) { - if (((frameCounter/15)%2) == 1) - { - DrawTriangle((Vector2){ GetScreenWidth() - 200, 10 }, (Vector2){ GetScreenWidth() - 200, 30 }, (Vector2){ GetScreenWidth() - 200 + 20, 20 }, DARKGREEN); - DrawText(TextFormat("PLAYING EVENTS... [%i]", currentFrame), GetScreenWidth() - 170, 15, 10, LIME); - } - } + DrawRectangle(10, 160, 290, 30, Fade(LIME, 0.3f)); + DrawRectangleLines(10, 160, 290, 30, Fade(DARKGREEN, 0.8f)); + DrawTriangle((Vector2){ 20, 155 + 10 }, (Vector2){ 20, 155 + 30 }, (Vector2){ 40, 155 + 20 }, DARKGREEN); - DrawText("Controls:", 20, 20, 10, BLACK); - DrawText("- Right/Left to move", 30, 40, 10, DARKGRAY); - DrawText("- Space to jump", 30, 60, 10, DARKGRAY); - DrawText("- Mouse Wheel to Zoom in-out, R to reset zoom", 30, 80, 10, DARKGRAY); + if (((frameCounter/15)%2) == 1) DrawText(TextFormat("PLAYING RECORDED EVENTS... [%i]", currentPlayFrame), 50, 170, 10, DARKGREEN); + } + EndDrawing(); //---------------------------------------------------------------------------------- From 98fcbe3fe2bc2f6a20f8c2415251e9164af8661f Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 27 Oct 2023 00:50:02 +0200 Subject: [PATCH 0426/1350] Update core_automation_events.c --- examples/core/core_automation_events.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/core/core_automation_events.c b/examples/core/core_automation_events.c index c35ce485a..adea0e88b 100644 --- a/examples/core/core_automation_events.c +++ b/examples/core/core_automation_events.c @@ -241,7 +241,8 @@ int main(void) if (eventPlaying) { - if (playFrameCounter >= aelist.events[currentPlayFrame].frame) + // NOTE: Multiple events could be executed in a single frame + while (playFrameCounter == aelist.events[currentPlayFrame].frame) { PlayAutomationEvent(aelist.events[currentPlayFrame]); currentPlayFrame++; From f721429f2584b2d2769c7031eb8823f4d768d979 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 27 Oct 2023 01:19:10 +0200 Subject: [PATCH 0427/1350] ADDED: `SetAutomationEventBaseFrame(int frame)` --- examples/core/core_automation_events.c | 1 + src/raylib.h | 7 ++++--- src/rcore.c | 6 ++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/examples/core/core_automation_events.c b/examples/core/core_automation_events.c index adea0e88b..27711b39d 100644 --- a/examples/core/core_automation_events.c +++ b/examples/core/core_automation_events.c @@ -214,6 +214,7 @@ int main(void) } else { + SetAutomationEventBaseFrame(180); StartAutomationEventRecording(); eventRecording = true; } diff --git a/src/raylib.h b/src/raylib.h index 7b3eddaee..d8bf1e07c 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -508,9 +508,9 @@ typedef struct FilePathList { // Automation event (opaque struct) typedef struct AutomationEvent { - unsigned int frame; // Event frame - unsigned int type; // Event type (AutomationEventType) - int params[4]; // Event parameters (if required) + unsigned int frame; // Event frame + unsigned int type; // Event type (AutomationEventType) + int params[4]; // Event parameters (if required) } AutomationEvent; // Automation event list @@ -1133,6 +1133,7 @@ RLAPI AutomationEventList LoadAutomationEventList(const char *fileName); RLAPI void UnloadAutomationEventList(AutomationEventList *list); // Unload automation events list from file RLAPI bool ExportAutomationEventList(AutomationEventList list, const char *fileName); // Export automation events list as text file RLAPI void SetAutomationEventList(AutomationEventList *list); // Set automation event list to record to +RLAPI void SetAutomationEventBaseFrame(int frame); // Set automation event internal base frame to start recording RLAPI void StartAutomationEventRecording(void); // Start recording automation events (AutomationEventList must be set) RLAPI void StopAutomationEventRecording(void); // Stop recording automation events RLAPI void PlayAutomationEvent(AutomationEvent event); // Play a recorded automation event diff --git a/src/rcore.c b/src/rcore.c index 67c79efa9..c3b69ae88 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2289,6 +2289,12 @@ void SetAutomationEventList(AutomationEventList *list) #endif } +// Set automation event internal base frame to start recording +void SetAutomationEventBaseFrame(int frame) +{ + CORE.Time.frameCounter = frame; +} + // Start recording automation events (AutomationEventList must be set) void StartAutomationEventRecording(void) { From 3afd0a55b9f243d1a3f0fb6be026deb085828c9d Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 27 Oct 2023 16:55:27 +0200 Subject: [PATCH 0428/1350] Update miniaudio to latest dev #3471 --- src/external/miniaudio.h | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h index 518e3c43a..ac2da690d 100644 --- a/src/external/miniaudio.h +++ b/src/external/miniaudio.h @@ -2675,9 +2675,16 @@ outputting any audio data. To output audio data, use `ma_encoder_write_pcm_frame example below: ```c - framesWritten = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite); + ma_uint64 framesWritten; + result = ma_encoder_write_pcm_frames(&encoder, pPCMFramesToWrite, framesToWrite, &framesWritten); + if (result != MA_SUCCESS) { + ... handle error ... + } ``` +The `framesWritten` variable will contain the number of PCM frames that were actually written. This +is optionally and you can pass in `NULL` if you need this. + Encoders must be uninitialized with `ma_encoder_uninit()`. @@ -40902,6 +40909,11 @@ static ma_thread_result MA_THREADCALL ma_worker_thread(void* pData) ma_device__on_notification_stopped(pDevice); } + /* If we stopped because the device has been uninitialized, abort now. */ + if (ma_device_get_state(pDevice) == ma_device_state_uninitialized) { + break; + } + /* A function somewhere is waiting for the device to have stopped for real so we need to signal an event to allow it to continue. */ ma_device__set_state(pDevice, ma_device_state_stopped); ma_event_signal(&pDevice->stopEvent); @@ -76804,6 +76816,7 @@ MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* seekTarget = ma_atomic_load_64(&pSound->seekTarget); if (seekTarget != MA_SEEK_TARGET_NONE) { *pCursor = seekTarget; + return MA_SUCCESS; } else { return ma_data_source_get_cursor_in_pcm_frames(pSound->pDataSource, pCursor); } From 2db7c727b653fc526b7da07fd337de02b7156e37 Mon Sep 17 00:00:00 2001 From: Alexandre Almeida Date: Fri, 27 Oct 2023 12:01:05 -0300 Subject: [PATCH 0429/1350] GetCurrentMonitor() - use closest monitor (#3472) --- src/platforms/rcore_desktop.c | 55 ++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index 1114181a4..f60a0e56c 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -751,15 +751,19 @@ int GetCurrentMonitor(void) } else { - int x = 0; - int y = 0; + int closestDist = 0x7FFFFFFF; - glfwGetWindowPos(platform.handle, &x, &y); - x += (int)CORE.Window.screen.width / 2; - y += (int)CORE.Window.screen.height / 2; + // Window center position + int wcx = 0; + int wcy = 0; + + glfwGetWindowPos(platform.handle, &wcx, &wcy); + wcx += (int)CORE.Window.screen.width / 2; + wcy += (int)CORE.Window.screen.height / 2; for (int i = 0; i < monitorCount; i++) { + // Monitor top-left position int mx = 0; int my = 0; @@ -769,17 +773,46 @@ int GetCurrentMonitor(void) if (mode) { - const int width = mode->width; - const int height = mode->height; + const int right = mx + mode->width - 1; + const int bottom = my + mode->height - 1; - if ((x >= mx) && - (x < (mx + width)) && - (y >= my) && - (y < (my + height))) + if ((wcx >= mx) && + (wcx <= right) && + (wcy >= my) && + (wcy <= bottom)) { index = i; break; } + + int xclosest = wcx; + if (wcx < mx) + { + xclosest = mx; + } + else if (wcx > right) + { + xclosest = right; + } + + int yclosest = wcy; + if (wcy < my) + { + yclosest = my; + } + else if (wcy > bottom) + { + yclosest = bottom; + } + + int dx = wcx - xclosest; + int dy = wcy - yclosest; + int dist = (dx * dx) + (dy * dy); + if (dist < closestDist) + { + index = i; + closestDist = dist; + } } else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); } From b46505b13d4a85e26d1d5b6f9fc2a4264bf8b02f Mon Sep 17 00:00:00 2001 From: SuperUserNameMan Date: Fri, 27 Oct 2023 17:13:10 +0200 Subject: [PATCH 0430/1350] Update tinyobj_loader_c.h (#3474) temporary quickfix for issue #3473 --- src/external/tinyobj_loader_c.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/external/tinyobj_loader_c.h b/src/external/tinyobj_loader_c.h index 502a55a7e..55d595a69 100644 --- a/src/external/tinyobj_loader_c.h +++ b/src/external/tinyobj_loader_c.h @@ -1269,6 +1269,11 @@ int tinyobj_parse_obj(tinyobj_attrib_t *attrib, tinyobj_shape_t **shapes, if (is_line_ending(buf, i, end_idx)) { line_infos[line_no].pos = prev_pos; line_infos[line_no].len = i - prev_pos; + +// ---- QUICK BUG FIX : https://github.com/raysan5/raylib/issues/3473 + if ( i > 0 && buf[i-1] == '\r' ) line_infos[line_no].len--; +// -------- + prev_pos = i + 1; line_no++; } From effa3ee249ed393fb7b283092c4023e919be3061 Mon Sep 17 00:00:00 2001 From: Khalid Abdullah Date: Sun, 29 Oct 2023 21:22:59 +0600 Subject: [PATCH 0431/1350] [Typo fixed] in CHANGELOG (#3477) --- CHANGELOG | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 1da04cc73..244d329da 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1871,9 +1871,9 @@ other changes: [models] Added function DrawCubeTexture() [models] Added function DrawQuad() [models] Added function DrawRay() -[models] Simplified funtion DrawPlane() +[models] Simplified function DrawPlane() [models] Removed function DrawPlaneEx() -[models] Simplified funtion DrawGizmo() +[models] Simplified function DrawGizmo() [models] Removed function DrawGizmoEx() [models] Added function LoadModelEx() [models] Review of function LoadCubicMap() From 01c264123d7bb12d166069a7f5c7403e98e51cfe Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sun, 29 Oct 2023 12:23:38 -0300 Subject: [PATCH 0432/1350] Remove rcore.h include from SDL (#3475) --- src/platforms/rcore_desktop_sdl.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 8543c751c..566d75d8c 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -49,8 +49,6 @@ * **********************************************************************************************/ -#include "rcore.h" - #include "SDL.h" // SDL base library (window/rendered, input, timming... functionality) #include "SDL_opengl.h" // SDL OpenGL functionality (if required, instead of internal renderer) @@ -998,7 +996,7 @@ void PollInputEvents(void) switch (event.type) { case SDL_QUIT: CORE.Window.shouldClose = true; break; - + case SDL_DROPFILE: // Dropped file { if (CORE.Window.dropFileCount == 0) @@ -1019,7 +1017,7 @@ void PollInputEvents(void) CORE.Window.dropFilepaths[CORE.Window.dropFileCount] = (char *)RL_CALLOC(MAX_FILEPATH_LENGTH, sizeof(char)); strcpy(CORE.Window.dropFilepaths[CORE.Window.dropFileCount], event.drop.file); SDL_free(event.drop.file); - + CORE.Window.dropFileCount++; } else TRACELOG(LOG_WARNING, "FILE: Maximum drag and drop files at once is limited to 1024 files!"); @@ -1274,7 +1272,7 @@ int InitPlatform(void) SDL_Joystick *gamepad = SDL_JoystickOpen(0); //if (SDL_Joystick *gamepad == NULL) SDL_Log("WARNING: Unable to open game controller! SDL Error: %s\n", SDL_GetError()); } - + SDL_EventState(SDL_DROPFILE, SDL_ENABLE); //---------------------------------------------------------------------------- From b4865588f84288b0c52f143a91c6e12908d1237f Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Oct 2023 16:36:46 +0100 Subject: [PATCH 0433/1350] REVIEWED: `GetCurrentMonitor()` #3472 --- src/platforms/rcore_desktop.c | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index f60a0e56c..08b329e85 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -751,6 +751,11 @@ int GetCurrentMonitor(void) } else { + // In case the window is between two monitors, we use below logic + // to try to detect the "current monitor" for that window, note that + // this is probably an overengineered solution for a very side case + // trying to match SDL behaviour + int closestDist = 0x7FFFFFFF; // Window center position @@ -758,8 +763,8 @@ int GetCurrentMonitor(void) int wcy = 0; glfwGetWindowPos(platform.handle, &wcx, &wcy); - wcx += (int)CORE.Window.screen.width / 2; - wcy += (int)CORE.Window.screen.height / 2; + wcx += (int)CORE.Window.screen.width/2; + wcy += (int)CORE.Window.screen.height/2; for (int i = 0; i < monitorCount; i++) { @@ -786,28 +791,16 @@ int GetCurrentMonitor(void) } int xclosest = wcx; - if (wcx < mx) - { - xclosest = mx; - } - else if (wcx > right) - { - xclosest = right; - } + if (wcx < mx) xclosest = mx; + else if (wcx > right) xclosest = right; int yclosest = wcy; - if (wcy < my) - { - yclosest = my; - } - else if (wcy > bottom) - { - yclosest = bottom; - } + if (wcy < my) yclosest = my; + else if (wcy > bottom) yclosest = bottom; int dx = wcx - xclosest; int dy = wcy - yclosest; - int dist = (dx * dx) + (dy * dy); + int dist = (dx*dx) + (dy*dy); if (dist < closestDist) { index = i; From 975d4154e6739fc7fcbe6e3406d4974aa5c4705e Mon Sep 17 00:00:00 2001 From: Josh Colclough Date: Sun, 29 Oct 2023 15:41:02 +0000 Subject: [PATCH 0434/1350] Fix the Julia set shader example (#3467) * Simplify POI selection * Improve mouse logic * Add colour cycles to the shader to show finer details. Works well with high iteration numbers * Testing things... * Actually fix zoom. Also allow user to reset camera with 'R' * Reset max iterations * Tidying & comments * Revert to original if statement * Make mouse logic more readable * Style conventions * Coding conventions - f postifx on floating points * Missed a few f postfixes --- .../resources/shaders/glsl100/julia_set.fs | 32 +++--- .../resources/shaders/glsl330/julia_set.fs | 34 +++--- examples/shaders/shaders_julia_set.c | 102 +++++++++--------- 3 files changed, 89 insertions(+), 79 deletions(-) diff --git a/examples/shaders/resources/shaders/glsl100/julia_set.fs b/examples/shaders/resources/shaders/glsl100/julia_set.fs index 44d083459..82d0a75ab 100644 --- a/examples/shaders/resources/shaders/glsl100/julia_set.fs +++ b/examples/shaders/resources/shaders/glsl100/julia_set.fs @@ -6,30 +6,30 @@ precision mediump float; varying vec2 fragTexCoord; varying vec4 fragColor; -uniform vec2 screenDims; // Dimensions of the screen uniform vec2 c; // c.x = real, c.y = imaginary component. Equation done is z^2 + c uniform vec2 offset; // Offset of the scale. uniform float zoom; // Zoom of the scale. // NOTE: Maximum number of shader for-loop iterations depend on GPU, // for example, on RasperryPi for this examply only supports up to 60 -const int MAX_ITERATIONS = 48; // Max iterations to do +const int maxIterations = 48; // Max iterations to do. +const float colorCycles = 1.0f; // Number of times the color palette repeats. // Square a complex number vec2 ComplexSquare(vec2 z) { return vec2( - z.x * z.x - z.y * z.y, - z.x * z.y * 2.0 + z.x*z.x - z.y*z.y, + z.x*z.y*2.0f ); } // Convert Hue Saturation Value (HSV) color into RGB vec3 Hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); + vec4 K = vec4(1.0f, 2.0f/3.0f, 1.0f/3.0f, 3.0f); + vec3 p = abs(fract(c.xxx + K.xyz)*6.0f - K.www); + return c.z*mix(K.xxx, clamp(p - K.xxx, 0.0f, 1.0f), c.y); } void main() @@ -45,8 +45,8 @@ void main() If the number is below 2, we keep iterating. But when do we stop iterating if the number is always below 2 (it converges)? - That is what MAX_ITERATIONS is for. - Then we can divide the iterations by the MAX_ITERATIONS value to get a normalized value that we can + That is what maxIterations is for. + Then we can divide the iterations by the maxIterations value to get a normalized value that we can then map to a color. We use dot product (z.x * z.x + z.y * z.y) to determine the magnitude (length) squared. @@ -55,13 +55,15 @@ void main() // The pixel coordinates are scaled so they are on the mandelbrot scale // NOTE: fragTexCoord already comes as normalized screen coordinates but offset must be normalized before scaling and zoom - vec2 z = vec2((fragTexCoord.x + offset.x/screenDims.x)*2.5/zoom, (fragTexCoord.y + offset.y/screenDims.y)*1.5/zoom); + vec2 z = vec2((fragTexCoord.x - 0.5f)*2.5f, (fragTexCoord.y - 0.5f)*1.5f)/zoom; + z.x += offset.x; + z.y += offset.y; int iter = 0; for (int iterations = 0; iterations < 60; iterations++) { z = ComplexSquare(z) + c; // Iterate function - if (dot(z, z) > 4.0) break; + if (dot(z, z) > 4.0f) break; iter = iterations; } @@ -72,12 +74,12 @@ void main() z = ComplexSquare(z) + c; // This last part smooths the color (again see link above). - float smoothVal = float(iter) + 1.0 - (log(log(length(z)))/log(2.0)); + float smoothVal = float(iter) + 1.0f - (log(log(length(z)))/log(2.0f)); // Normalize the value so it is between 0 and 1. - float norm = smoothVal/float(MAX_ITERATIONS); + float norm = smoothVal/float(maxIterations); // If in set, color black. 0.999 allows for some float accuracy error. - if (norm > 0.999) gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); - else gl_FragColor = vec4(Hsv2rgb(vec3(norm, 1.0, 1.0)), 1.0); + if (norm > 0.999f) gl_FragColor = vec4(0.0f, 0.0f, 0.0f, 1.0f); + else gl_FragColor = vec4(Hsv2rgb(vec3(norm*colorCycles, 1.0f, 1.0f)), 1.0f); } diff --git a/examples/shaders/resources/shaders/glsl330/julia_set.fs b/examples/shaders/resources/shaders/glsl330/julia_set.fs index c5ee0da60..7a6f069c8 100644 --- a/examples/shaders/resources/shaders/glsl330/julia_set.fs +++ b/examples/shaders/resources/shaders/glsl330/julia_set.fs @@ -7,28 +7,28 @@ in vec4 fragColor; // Output fragment color out vec4 finalColor; -uniform vec2 screenDims; // Dimensions of the screen uniform vec2 c; // c.x = real, c.y = imaginary component. Equation done is z^2 + c uniform vec2 offset; // Offset of the scale. uniform float zoom; // Zoom of the scale. -const int MAX_ITERATIONS = 255; // Max iterations to do. +const int maxIterations = 255; // Max iterations to do. +const float colorCycles = 2.0f; // Number of times the color palette repeats. Can show higher detail for higher iteration numbers. // Square a complex number vec2 ComplexSquare(vec2 z) { return vec2( - z.x * z.x - z.y * z.y, - z.x * z.y * 2.0 + z.x*z.x - z.y*z.y, + z.x*z.y*2.0f ); } // Convert Hue Saturation Value (HSV) color into RGB vec3 Hsv2rgb(vec3 c) { - vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); - vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); - return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); + vec4 K = vec4(1.0f, 2.0f/3.0f, 1.0f/3.0f, 3.0f); + vec3 p = abs(fract(c.xxx + K.xyz)*6.0f - K.www); + return c.z*mix(K.xxx, clamp(p - K.xxx, 0.0f, 1.0f), c.y); } void main() @@ -44,8 +44,8 @@ void main() If the number is below 2, we keep iterating. But when do we stop iterating if the number is always below 2 (it converges)? - That is what MAX_ITERATIONS is for. - Then we can divide the iterations by the MAX_ITERATIONS value to get a normalized value that we can + That is what maxIterations is for. + Then we can divide the iterations by the maxIterations value to get a normalized value that we can then map to a color. We use dot product (z.x * z.x + z.y * z.y) to determine the magnitude (length) squared. @@ -54,14 +54,16 @@ void main() // The pixel coordinates are scaled so they are on the mandelbrot scale // NOTE: fragTexCoord already comes as normalized screen coordinates but offset must be normalized before scaling and zoom - vec2 z = vec2((fragTexCoord.x + offset.x/screenDims.x)*2.5/zoom, (fragTexCoord.y + offset.y/screenDims.y)*1.5/zoom); + vec2 z = vec2((fragTexCoord.x - 0.5f)*2.5f, (fragTexCoord.y - 0.5f)*1.5f)/zoom; + z.x += offset.x; + z.y += offset.y; int iterations = 0; - for (iterations = 0; iterations < MAX_ITERATIONS; iterations++) + for (iterations = 0; iterations < maxIterations; iterations++) { z = ComplexSquare(z) + c; // Iterate function - if (dot(z, z) > 4.0) break; + if (dot(z, z) > 4.0f) break; } // Another few iterations decreases errors in the smoothing calculation. @@ -70,12 +72,12 @@ void main() z = ComplexSquare(z) + c; // This last part smooths the color (again see link above). - float smoothVal = float(iterations) + 1.0 - (log(log(length(z)))/log(2.0)); + float smoothVal = float(iterations) + 1.0f - (log(log(length(z)))/log(2.0f)); // Normalize the value so it is between 0 and 1. - float norm = smoothVal/float(MAX_ITERATIONS); + float norm = smoothVal/float(maxIterations); // If in set, color black. 0.999 allows for some float accuracy error. - if (norm > 0.999) finalColor = vec4(0.0, 0.0, 0.0, 1.0); - else finalColor = vec4(Hsv2rgb(vec3(norm, 1.0, 1.0)), 1.0); + if (norm > 0.999f) finalColor = vec4(0.0f, 0.0f, 0.0f, 1.0f); + else finalColor = vec4(Hsv2rgb(vec3(norm*colorCycles, 1.0f, 1.0f)), 1.0f); } diff --git a/examples/shaders/shaders_julia_set.c b/examples/shaders/shaders_julia_set.c index aebb287a1..608d3b52b 100644 --- a/examples/shaders/shaders_julia_set.c +++ b/examples/shaders/shaders_julia_set.c @@ -9,12 +9,12 @@ * * Example originally created with raylib 2.5, last time updated with raylib 4.0 * -* Example contributed by eggmund (@eggmund) and reviewed by Ramon Santamaria (@raysan5) +* Example contributed by Josh Colclough (@joshcol9232) and reviewed by Ramon Santamaria (@raysan5) * * Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, * BSD-like license that allows static linking with closed source software * -* Copyright (c) 2019-2023 eggmund (@eggmund) and Ramon Santamaria (@raysan5) +* Copyright (c) 2019-2023 Josh Colclough (@joshcol9232) and Ramon Santamaria (@raysan5) * ********************************************************************************************/ @@ -37,6 +37,13 @@ const float pointsOfInterest[6][2] = { -0.70176f, -0.3842f }, }; +const int screenWidth = 800; +const int screenHeight = 450; +const float zoomSpeed = 1.01f; +const float offsetSpeedMul = 2.0f; + +const float startingZoom = 0.75f; + //------------------------------------------------------------------------------------ // Program main entry point //------------------------------------------------------------------------------------ @@ -44,10 +51,6 @@ int main(void) { // Initialization //-------------------------------------------------------------------------------------- - const int screenWidth = 800; - const int screenHeight = 450; - - //SetConfigFlags(FLAG_WINDOW_HIGHDPI); InitWindow(screenWidth, screenHeight, "raylib [shaders] example - julia sets"); // Load julia set shader @@ -61,10 +64,8 @@ int main(void) float c[2] = { pointsOfInterest[0][0], pointsOfInterest[0][1] }; // Offset and zoom to draw the julia set at. (centered on screen and default size) - float offset[2] = { -(float)GetScreenWidth()/2, -(float)GetScreenHeight()/2 }; - float zoom = 1.0f; - - Vector2 offsetSpeed = { 0.0f, 0.0f }; + float offset[2] = { 0.0f, 0.0f }; + float zoom = startingZoom; // Get variable (uniform) locations on the shader to connect with the program // NOTE: If uniform variable could not be found in the shader, function returns -1 @@ -72,17 +73,13 @@ int main(void) int zoomLoc = GetShaderLocation(shader, "zoom"); int offsetLoc = GetShaderLocation(shader, "offset"); - // Tell the shader what the screen dimensions, zoom, offset and c are - float screenDims[2] = { (float)GetScreenWidth(), (float)GetScreenHeight() }; - SetShaderValue(shader, GetShaderLocation(shader, "screenDims"), screenDims, SHADER_UNIFORM_VEC2); - + // Upload the shader uniform values! SetShaderValue(shader, cLoc, c, SHADER_UNIFORM_VEC2); SetShaderValue(shader, zoomLoc, &zoom, SHADER_UNIFORM_FLOAT); SetShaderValue(shader, offsetLoc, offset, SHADER_UNIFORM_VEC2); int incrementSpeed = 0; // Multiplier of speed to change c value bool showControls = true; // Show controls - bool pause = false; // Pause animation SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -110,42 +107,50 @@ int main(void) SetShaderValue(shader, cLoc, c, SHADER_UNIFORM_VEC2); } - if (IsKeyPressed(KEY_SPACE)) pause = !pause; // Pause animation (c change) - if (IsKeyPressed(KEY_F1)) showControls = !showControls; // Toggle whether or not to show controls - - if (!pause) + // If "R" is pressed, reset zoom and offset. + if (IsKeyPressed(KEY_R)) { - if (IsKeyPressed(KEY_RIGHT)) incrementSpeed++; - else if (IsKeyPressed(KEY_LEFT)) incrementSpeed--; - - // TODO: The idea is to zoom and move around with mouse - // Probably offset movement should be proportional to zoom level - if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) || IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) - { - if (IsMouseButtonDown(MOUSE_BUTTON_LEFT)) zoom += zoom*0.003f; - if (IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) zoom -= zoom*0.003f; - - Vector2 mousePos = GetMousePosition(); - - offsetSpeed.x = mousePos.x -(float)screenWidth/2; - offsetSpeed.y = mousePos.y -(float)screenHeight/2; - - // Slowly move camera to targetOffset - offset[0] += GetFrameTime()*offsetSpeed.x*0.8f; - offset[1] += GetFrameTime()*offsetSpeed.y*0.8f; - } - else offsetSpeed = (Vector2){ 0.0f, 0.0f }; - + zoom = startingZoom; + offset[0] = 0.0f; + offset[1] = 0.0f; SetShaderValue(shader, zoomLoc, &zoom, SHADER_UNIFORM_FLOAT); SetShaderValue(shader, offsetLoc, offset, SHADER_UNIFORM_VEC2); - - // Increment c value with time - float amount = GetFrameTime()*incrementSpeed*0.0005f; - c[0] += amount; - c[1] += amount; - - SetShaderValue(shader, cLoc, c, SHADER_UNIFORM_VEC2); } + + if (IsKeyPressed(KEY_SPACE)) incrementSpeed = 0; // Pause animation (c change) + if (IsKeyPressed(KEY_F1)) showControls = !showControls; // Toggle whether or not to show controls + + if (IsKeyPressed(KEY_RIGHT)) incrementSpeed++; + else if (IsKeyPressed(KEY_LEFT)) incrementSpeed--; + + // If either left or right button is pressed, zoom in/out. + if (IsMouseButtonDown(MOUSE_BUTTON_LEFT) || IsMouseButtonDown(MOUSE_BUTTON_RIGHT)) + { + // Change zoom. If Mouse left -> zoom in. Mouse right -> zoom out. + zoom *= IsMouseButtonDown(MOUSE_BUTTON_LEFT)? zoomSpeed : 1.0f/zoomSpeed; + + const Vector2 mousePos = GetMousePosition(); + Vector2 offsetVelocity; + // Find the velocity at which to change the camera. Take the distance of the mouse + // from the center of the screen as the direction, and adjust magnitude based on + // the current zoom. + offsetVelocity.x = (mousePos.x/(float)screenWidth - 0.5f)*offsetSpeedMul/zoom; + offsetVelocity.y = (mousePos.y/(float)screenHeight - 0.5f)*offsetSpeedMul/zoom; + + // Apply move velocity to camera + offset[0] += GetFrameTime()*offsetVelocity.x; + offset[1] += GetFrameTime()*offsetVelocity.y; + + // Update the shader uniform values! + SetShaderValue(shader, zoomLoc, &zoom, SHADER_UNIFORM_FLOAT); + SetShaderValue(shader, offsetLoc, offset, SHADER_UNIFORM_VEC2); + } + + // Increment c value with time + const float dc = GetFrameTime()*(float)incrementSpeed*0.0005f; + c[0] += dc; + c[1] += dc; + SetShaderValue(shader, cLoc, c, SHADER_UNIFORM_VEC2); //---------------------------------------------------------------------------------- // Draw @@ -178,7 +183,8 @@ int main(void) DrawText("Press KEY_F1 to toggle these controls", 10, 30, 10, RAYWHITE); DrawText("Press KEYS [1 - 6] to change point of interest", 10, 45, 10, RAYWHITE); DrawText("Press KEY_LEFT | KEY_RIGHT to change speed", 10, 60, 10, RAYWHITE); - DrawText("Press KEY_SPACE to pause movement animation", 10, 75, 10, RAYWHITE); + DrawText("Press KEY_SPACE to stop movement animation", 10, 75, 10, RAYWHITE); + DrawText("Press KEY_R to recenter the camera", 10, 90, 10, RAYWHITE); } EndDrawing(); //---------------------------------------------------------------------------------- From e3363e9ad0904f87cf038d7f7eeb379d988678c3 Mon Sep 17 00:00:00 2001 From: Khalid Abdullah Date: Mon, 30 Oct 2023 00:34:50 +0600 Subject: [PATCH 0435/1350] Typo fixed in HISTORY.md (#3481) * [Typo fixed] in CHANGELOG * [Typo fixed] in HISTORY.md --- HISTORY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 0a0b1ce2b..10f0dfb77 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -140,7 +140,7 @@ notes on raylib 1.7 On May 2017, around 6 month after raylib 1.6, comes another raylib instalment, raylib 1.7. This time library has been improved a lot in terms of consistency and cleanness. As stated in [this patreon article](https://www.patreon.com/posts/raylib-future-7501034), this new raylib version has focused efforts in becoming more simple and easy-to-use to learn videogames programming. Some highlights of this new version are: - - More than 30 new functions added to the library, functions to control Window, utils to work with filenames and extensions, functions to draw lines with custom thick, mesh loading, functions for 3d ray collisions detailed detection, funtions for VR simulation and much more... Just check [CHANGELOG](CHANGELOG) for a detailed list of additions! + - More than 30 new functions added to the library, functions to control Window, utils to work with filenames and extensions, functions to draw lines with custom thick, mesh loading, functions for 3d ray collisions detailed detection, functions for VR simulation and much more... Just check [CHANGELOG](CHANGELOG) for a detailed list of additions! - Support of [configuration flags](https://github.com/raysan5/raylib/issues/200) on every raylib module. Advance users can customize raylib just choosing desired features, defining some configuration flags on modules compilation. That way users can control library size and available functionality. From 2da8cc383cba42af830f251912263db6eb9298fd Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Oct 2023 19:46:34 +0100 Subject: [PATCH 0436/1350] Update Makefile.Web --- examples/Makefile.Web | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 3512f7407..9c2cadc37 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -138,7 +138,7 @@ endif # Define default make program: MAKE #------------------------------------------------------------------------------------------------ -MAKE ?= make +MAKE ?= emmake make ifeq ($(PLATFORM),PLATFORM_DESKTOP) ifeq ($(PLATFORM_OS),WINDOWS) From 12f3bc10c2def80abfc76c8158785ac91c0ca1d1 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sun, 29 Oct 2023 16:20:19 -0300 Subject: [PATCH 0437/1350] [core] Move `rcore.h` content to inside `rcore.c` (#3479) * Move rcore.h content inside rcore.c * Remove extern CoreData CORE --- src/rcore.c | 200 +++++++++++++++++++++++++++++++++++++++++++----- src/rcore.h | 213 ---------------------------------------------------- 2 files changed, 181 insertions(+), 232 deletions(-) delete mode 100644 src/rcore.h diff --git a/src/rcore.c b/src/rcore.c index c3b69ae88..b2eb78675 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -85,12 +85,19 @@ #include "config.h" // Defines module configuration flags #endif -#include "rcore.h" // Defines types and globals +#include "utils.h" // Required for: TRACELOG() macros + +#include // Required for: srand(), rand(), atexit() +#include // Required for: sprintf() [Used in OpenURL()] +#include // Required for: strrchr(), strcmp(), strlen(), memset() +#include // Required for: time() [Used in InitTimer()] +#include // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()] #define RLGL_IMPLEMENTATION #include "rlgl.h" // OpenGL abstraction layer to OpenGL 1.1, 3.3+ or ES2 -#include "raymath.h" // Vector3, Quaternion and Matrix functionality +#define RAYMATH_IMPLEMENTATION +#include "raymath.h" // Vector2, Vector3, Quaternion and Matrix functionality #if defined(SUPPORT_GESTURES_SYSTEM) #define RGESTURES_IMPLEMENTATION @@ -166,6 +173,161 @@ __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigne #define CHDIR chdir #endif +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +#ifndef MAX_FILEPATH_CAPACITY + #define MAX_FILEPATH_CAPACITY 8192 // Maximum capacity for filepath +#endif +#ifndef MAX_FILEPATH_LENGTH + #define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value) +#endif + +#ifndef MAX_KEYBOARD_KEYS + #define MAX_KEYBOARD_KEYS 512 // Maximum number of keyboard keys supported +#endif +#ifndef MAX_MOUSE_BUTTONS + #define MAX_MOUSE_BUTTONS 8 // Maximum number of mouse buttons supported +#endif +#ifndef MAX_GAMEPADS + #define MAX_GAMEPADS 4 // Maximum number of gamepads supported +#endif +#ifndef MAX_GAMEPAD_AXIS + #define MAX_GAMEPAD_AXIS 8 // Maximum number of axis supported (per gamepad) +#endif +#ifndef MAX_GAMEPAD_BUTTONS + #define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad) +#endif +#ifndef MAX_TOUCH_POINTS + #define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported +#endif +#ifndef MAX_KEY_PRESSED_QUEUE + #define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue +#endif +#ifndef MAX_CHAR_PRESSED_QUEUE + #define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue +#endif + +#ifndef MAX_DECOMPRESSION_SIZE + #define MAX_DECOMPRESSION_SIZE 64 // Maximum size allocated for decompression in MB +#endif + +#ifndef MAX_AUTOMATION_EVENTS + #define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record +#endif + +// Flags operation macros +#define FLAG_SET(n, f) ((n) |= (f)) +#define FLAG_CLEAR(n, f) ((n) &= ~(f)) +#define FLAG_TOGGLE(n, f) ((n) ^= (f)) +#define FLAG_CHECK(n, f) ((n) & (f)) + +#if (defined(__linux__) || defined(PLATFORM_WEB)) && (_POSIX_C_SOURCE < 199309L) + #undef _POSIX_C_SOURCE + #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext. +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +typedef struct { int x; int y; } Point; +typedef struct { unsigned int width; unsigned int height; } Size; + +// Core global state context data +typedef struct CoreData { + struct { + const char *title; // Window text title const pointer + unsigned int flags; // Configuration flags (bit based), keeps window state + bool ready; // Check if window has been initialized successfully + bool fullscreen; // Check if fullscreen mode is enabled + bool shouldClose; // Check if window set for closing + bool resizedLastFrame; // Check if window has been resized last frame + bool eventWaiting; // Wait for events before ending frame + + Point position; // Window position (required on fullscreen toggle) + Point previousPosition; // Window previous position (required on borderless windowed toggle) + Size display; // Display width and height (monitor, device-screen, LCD, ...) + Size screen; // Screen width and height (used render area) + Size previousScreen; // Screen previous width and height (required on borderless windowed toggle) + Size currentFbo; // Current render width and height (depends on active fbo) + Size render; // Framebuffer width and height (render area, including black bars if required) + Point renderOffset; // Offset from render area (must be divided by 2) + Size screenMin; // Screen minimum width and height (for resizable window) + Size screenMax; // Screen maximum width and height (for resizable window) + Matrix screenScale; // Matrix to scale screen (framebuffer rendering) + + char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) + unsigned int dropFileCount; // Count dropped files strings + + } Window; + struct { + const char *basePath; // Base path for data storage + + } Storage; + struct { + struct { + int exitKey; // Default exit key + char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state + char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state + + // NOTE: Since key press logic involves comparing prev vs cur key state, we need to handle key repeats specially + char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame. + + int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue + int keyPressedQueueCount; // Input keys queue count + + int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode) + int charPressedQueueCount; // Input characters queue count + + } Keyboard; + struct { + Vector2 offset; // Mouse offset + Vector2 scale; // Mouse scaling + Vector2 currentPosition; // Mouse position on screen + Vector2 previousPosition; // Previous mouse position + + int cursor; // Tracks current mouse cursor + bool cursorHidden; // Track if cursor is hidden + bool cursorOnScreen; // Tracks if cursor is inside client area + + char currentButtonState[MAX_MOUSE_BUTTONS]; // Registers current mouse button state + char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state + Vector2 currentWheelMove; // Registers current mouse wheel variation + Vector2 previousWheelMove; // Registers previous mouse wheel variation + + } Mouse; + struct { + int pointCount; // Number of touch points active + int pointId[MAX_TOUCH_POINTS]; // Point identifiers + Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen + char currentTouchState[MAX_TOUCH_POINTS]; // Registers current touch state + char previousTouchState[MAX_TOUCH_POINTS]; // Registers previous touch state + + } Touch; + struct { + int lastButtonPressed; // Register last gamepad button pressed + int axisCount[MAX_GAMEPADS]; // Register number of available gamepad axis + bool ready[MAX_GAMEPADS]; // Flag to know if gamepad is ready + char name[MAX_GAMEPADS][64]; // Gamepad name holder + char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state + char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state + float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state + + } Gamepad; + } Input; + struct { + double current; // Current time measure + double previous; // Previous time measure + double update; // Time measure for frame update + double draw; // Time measure for frame draw + double frame; // Time measure for one frame + double target; // Desired time for one frame, if 0 not applied + unsigned long long int base; // Base time measure for hi-res timer (PLATFORM_ANDROID, PLATFORM_DRM) + unsigned int frameCounter; // Frame counter + + } Time; +} CoreData; + //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- @@ -2156,7 +2318,7 @@ AutomationEventList LoadAutomationEventList(const char *fileName) /* //int dataSize = 0; //unsigned char *data = LoadFileData(fileName, &dataSize); - + FILE *raeFile = fopen(fileName, "rb"); unsigned char fileId[4] = { 0 }; @@ -2202,7 +2364,7 @@ AutomationEventList LoadAutomationEventList(const char *fileName) fgets(buffer, 256, raeFile); } - if (counter != list.count) + if (counter != list.count) { TRACELOG(LOG_WARNING, "AUTOMATION: Events read from file [%i] do not mach event count specified [%i]", counter, list.count); list.count = counter; @@ -2234,7 +2396,7 @@ void UnloadAutomationEventList(AutomationEventList *list) bool ExportAutomationEventList(AutomationEventList list, const char *fileName) { bool success = false; - + #if defined(SUPPORT_AUTOMATION_EVENTS) // Export events as binary file // TODO: Save to memory buffer and SaveFileData() @@ -2992,7 +3154,7 @@ static void RecordAutomationEvent(void) { // Checking events in current frame and save them into currentEventList // TODO: How important is the current frame? Could it be modified? - + if (currentEventList->count == currentEventList->capacity) return; // Security check // Keyboard input events recording @@ -3011,7 +3173,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_KEY_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; } - + if (currentEventList->count == currentEventList->capacity) return; // Security check // Event type: INPUT_KEY_DOWN @@ -3026,7 +3188,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_KEY_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; } - + if (currentEventList->count == currentEventList->capacity) return; // Security check } //------------------------------------------------------------------------------------- @@ -3047,7 +3209,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_BUTTON_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; } - + if (currentEventList->count == currentEventList->capacity) return; // Security check // Event type: INPUT_MOUSE_BUTTON_DOWN @@ -3062,7 +3224,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_BUTTON_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; } - + if (currentEventList->count == currentEventList->capacity) return; // Security check } @@ -3078,7 +3240,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_POSITION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; - + if (currentEventList->count == currentEventList->capacity) return; // Security check } @@ -3094,7 +3256,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_MOUSE_WHEEL_MOTION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; - + if (currentEventList->count == currentEventList->capacity) return; // Security check } //------------------------------------------------------------------------------------- @@ -3115,7 +3277,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; } - + if (currentEventList->count == currentEventList->capacity) return; // Security check // Event type: INPUT_TOUCH_DOWN @@ -3130,7 +3292,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_TOUCH_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; } - + if (currentEventList->count == currentEventList->capacity) return; // Security check // Event type: INPUT_TOUCH_POSITION @@ -3149,7 +3311,7 @@ static void RecordAutomationEvent(void) currentEventList->count++; } */ - + if (currentEventList->count == currentEventList->capacity) return; // Security check } //------------------------------------------------------------------------------------- @@ -3190,7 +3352,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_BUTTON_UP | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; } - + if (currentEventList->count == currentEventList->capacity) return; // Security check // Event type: INPUT_GAMEPAD_BUTTON_DOWN @@ -3205,7 +3367,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_BUTTON_DOWN | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; } - + if (currentEventList->count == currentEventList->capacity) return; // Security check } @@ -3223,7 +3385,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GAMEPAD_AXIS_MOTION | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; } - + if (currentEventList->count == currentEventList->capacity) return; // Security check } } @@ -3242,7 +3404,7 @@ static void RecordAutomationEvent(void) TRACELOG(LOG_INFO, "AUTOMATION: Frame: %i | Event type: INPUT_GESTURE | Event parameters: %i, %i, %i", currentEventList->events[currentEventList->count].frame, currentEventList->events[currentEventList->count].params[0], currentEventList->events[currentEventList->count].params[1], currentEventList->events[currentEventList->count].params[2]); currentEventList->count++; - + if (currentEventList->count == currentEventList->capacity) return; // Security check } //------------------------------------------------------------------------------------- diff --git a/src/rcore.h b/src/rcore.h deleted file mode 100644 index a6955f8cc..000000000 --- a/src/rcore.h +++ /dev/null @@ -1,213 +0,0 @@ -/********************************************************************************************** -* -* rcore - Common types and globals (all platforms) -* -* LIMITATIONS: -* - Limitation 01 -* - Limitation 02 -* -* POSSIBLE IMPROVEMENTS: -* - Improvement 01 -* - Improvement 02 -* -* -* LICENSE: zlib/libpng -* -* Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) and contributors -* -* This software is provided "as-is", without any express or implied warranty. In no event -* will the authors be held liable for any damages arising from the use of this software. -* -* Permission is granted to anyone to use this software for any purpose, including commercial -* applications, and to alter it and redistribute it freely, subject to the following restrictions: -* -* 1. The origin of this software must not be misrepresented; you must not claim that you -* wrote the original software. If you use this software in a product, an acknowledgment -* in the product documentation would be appreciated but is not required. -* -* 2. Altered source versions must be plainly marked as such, and must not be misrepresented -* as being the original software. -* -* 3. This notice may not be removed or altered from any source distribution. -* -**********************************************************************************************/ - -#ifndef RCORE_H -#define RCORE_H - -#include "raylib.h" - -#include "utils.h" // Required for: TRACELOG() macros - -#include "rlgl.h" // Required for: graphics layer functionality - -#define RAYMATH_IMPLEMENTATION -#include "raymath.h" // Required for: Vector2/Vector3/Matrix functionality - -#include // Required for: srand(), rand(), atexit() -#include // Required for: sprintf() [Used in OpenURL()] -#include // Required for: strrchr(), strcmp(), strlen(), memset() -#include // Required for: time() [Used in InitTimer()] -#include // Required for: tan() [Used in BeginMode3D()], atan2f() [Used in LoadVrStereoConfig()] - -//---------------------------------------------------------------------------------- -// Defines and Macros -//---------------------------------------------------------------------------------- -#ifndef MAX_FILEPATH_CAPACITY - #define MAX_FILEPATH_CAPACITY 8192 // Maximum capacity for filepath -#endif -#ifndef MAX_FILEPATH_LENGTH - #define MAX_FILEPATH_LENGTH 4096 // Maximum length for filepaths (Linux PATH_MAX default value) -#endif - -#ifndef MAX_KEYBOARD_KEYS - #define MAX_KEYBOARD_KEYS 512 // Maximum number of keyboard keys supported -#endif -#ifndef MAX_MOUSE_BUTTONS - #define MAX_MOUSE_BUTTONS 8 // Maximum number of mouse buttons supported -#endif -#ifndef MAX_GAMEPADS - #define MAX_GAMEPADS 4 // Maximum number of gamepads supported -#endif -#ifndef MAX_GAMEPAD_AXIS - #define MAX_GAMEPAD_AXIS 8 // Maximum number of axis supported (per gamepad) -#endif -#ifndef MAX_GAMEPAD_BUTTONS - #define MAX_GAMEPAD_BUTTONS 32 // Maximum number of buttons supported (per gamepad) -#endif -#ifndef MAX_TOUCH_POINTS - #define MAX_TOUCH_POINTS 8 // Maximum number of touch points supported -#endif -#ifndef MAX_KEY_PRESSED_QUEUE - #define MAX_KEY_PRESSED_QUEUE 16 // Maximum number of keys in the key input queue -#endif -#ifndef MAX_CHAR_PRESSED_QUEUE - #define MAX_CHAR_PRESSED_QUEUE 16 // Maximum number of characters in the char input queue -#endif - -#ifndef MAX_DECOMPRESSION_SIZE - #define MAX_DECOMPRESSION_SIZE 64 // Maximum size allocated for decompression in MB -#endif - -#ifndef MAX_AUTOMATION_EVENTS - #define MAX_AUTOMATION_EVENTS 16384 // Maximum number of automation events to record -#endif - -// Flags operation macros -#define FLAG_SET(n, f) ((n) |= (f)) -#define FLAG_CLEAR(n, f) ((n) &= ~(f)) -#define FLAG_TOGGLE(n, f) ((n) ^= (f)) -#define FLAG_CHECK(n, f) ((n) & (f)) - -#if (defined(__linux__) || defined(PLATFORM_WEB)) && (_POSIX_C_SOURCE < 199309L) - #undef _POSIX_C_SOURCE - #define _POSIX_C_SOURCE 199309L // Required for: CLOCK_MONOTONIC if compiled with c99 without gnu ext. -#endif - -//---------------------------------------------------------------------------------- -// Types and Structures Definition -//---------------------------------------------------------------------------------- -typedef struct { int x; int y; } Point; -typedef struct { unsigned int width; unsigned int height; } Size; - -// Core global state context data -typedef struct CoreData { - struct { - const char *title; // Window text title const pointer - unsigned int flags; // Configuration flags (bit based), keeps window state - bool ready; // Check if window has been initialized successfully - bool fullscreen; // Check if fullscreen mode is enabled - bool shouldClose; // Check if window set for closing - bool resizedLastFrame; // Check if window has been resized last frame - bool eventWaiting; // Wait for events before ending frame - - Point position; // Window position (required on fullscreen toggle) - Point previousPosition; // Window previous position (required on borderless windowed toggle) - Size display; // Display width and height (monitor, device-screen, LCD, ...) - Size screen; // Screen width and height (used render area) - Size previousScreen; // Screen previous width and height (required on borderless windowed toggle) - Size currentFbo; // Current render width and height (depends on active fbo) - Size render; // Framebuffer width and height (render area, including black bars if required) - Point renderOffset; // Offset from render area (must be divided by 2) - Size screenMin; // Screen minimum width and height (for resizable window) - Size screenMax; // Screen maximum width and height (for resizable window) - Matrix screenScale; // Matrix to scale screen (framebuffer rendering) - - char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) - unsigned int dropFileCount; // Count dropped files strings - - } Window; - struct { - const char *basePath; // Base path for data storage - - } Storage; - struct { - struct { - int exitKey; // Default exit key - char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state - char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state - - // NOTE: Since key press logic involves comparing prev vs cur key state, we need to handle key repeats specially - char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame. - - int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue - int keyPressedQueueCount; // Input keys queue count - - int charPressedQueue[MAX_CHAR_PRESSED_QUEUE]; // Input characters queue (unicode) - int charPressedQueueCount; // Input characters queue count - - } Keyboard; - struct { - Vector2 offset; // Mouse offset - Vector2 scale; // Mouse scaling - Vector2 currentPosition; // Mouse position on screen - Vector2 previousPosition; // Previous mouse position - - int cursor; // Tracks current mouse cursor - bool cursorHidden; // Track if cursor is hidden - bool cursorOnScreen; // Tracks if cursor is inside client area - - char currentButtonState[MAX_MOUSE_BUTTONS]; // Registers current mouse button state - char previousButtonState[MAX_MOUSE_BUTTONS]; // Registers previous mouse button state - Vector2 currentWheelMove; // Registers current mouse wheel variation - Vector2 previousWheelMove; // Registers previous mouse wheel variation - - } Mouse; - struct { - int pointCount; // Number of touch points active - int pointId[MAX_TOUCH_POINTS]; // Point identifiers - Vector2 position[MAX_TOUCH_POINTS]; // Touch position on screen - char currentTouchState[MAX_TOUCH_POINTS]; // Registers current touch state - char previousTouchState[MAX_TOUCH_POINTS]; // Registers previous touch state - - } Touch; - struct { - int lastButtonPressed; // Register last gamepad button pressed - int axisCount[MAX_GAMEPADS]; // Register number of available gamepad axis - bool ready[MAX_GAMEPADS]; // Flag to know if gamepad is ready - char name[MAX_GAMEPADS][64]; // Gamepad name holder - char currentButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Current gamepad buttons state - char previousButtonState[MAX_GAMEPADS][MAX_GAMEPAD_BUTTONS]; // Previous gamepad buttons state - float axisState[MAX_GAMEPADS][MAX_GAMEPAD_AXIS]; // Gamepad axis state - - } Gamepad; - } Input; - struct { - double current; // Current time measure - double previous; // Previous time measure - double update; // Time measure for frame update - double draw; // Time measure for frame draw - double frame; // Time measure for one frame - double target; // Desired time for one frame, if 0 not applied - unsigned long long int base; // Base time measure for hi-res timer (PLATFORM_ANDROID, PLATFORM_DRM) - unsigned int frameCounter; // Frame counter - - } Time; -} CoreData; - -//---------------------------------------------------------------------------------- -// Global Variables Definition -//---------------------------------------------------------------------------------- -extern CoreData CORE; - -#endif From 1fd61a00e4423c68f2b32c46148847b27a6eb94f Mon Sep 17 00:00:00 2001 From: JaanDev <67502867+JaanDev@users.noreply.github.com> Date: Sun, 29 Oct 2023 22:21:00 +0300 Subject: [PATCH 0438/1350] Fix compressed DDS texture loading issues (#3483) --- src/external/rl_gputex.h | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index fa39fe29c..fb7b5e8de 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -261,11 +261,9 @@ void *rl_load_dds_from_memory(const unsigned char *file_data, unsigned int file_ } else if (((header->ddspf.flags == 0x04) || (header->ddspf.flags == 0x05)) && (header->ddspf.fourcc > 0)) // Compressed { - int data_size = 0; - - // Calculate data size, including all mipmaps - if (header->mipmap_count > 1) data_size = header->pitch_or_linear_size*2; - else data_size = header->pitch_or_linear_size; + // NOTE: This forces only 1 mipmap to be loaded which is not really correct but it works + int data_size = (header->pitch_or_linear_size < file_size - 0x80) ? header->pitch_or_linear_size : file_size - 0x80; + *mips = 1; image_data = RL_MALLOC(data_size*sizeof(unsigned char)); From 21243c82344a132c23fc7fe24094a2f4933ab92a Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Oct 2023 20:42:29 +0100 Subject: [PATCH 0439/1350] Update rcore_desktop_sdl.c --- src/platforms/rcore_desktop_sdl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 566d75d8c..8793e6c77 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -1269,8 +1269,8 @@ int InitPlatform(void) //---------------------------------------------------------------------------- if (SDL_NumJoysticks() >= 1) { - SDL_Joystick *gamepad = SDL_JoystickOpen(0); - //if (SDL_Joystick *gamepad == NULL) SDL_Log("WARNING: Unable to open game controller! SDL Error: %s\n", SDL_GetError()); + platform.gamepad = SDL_JoystickOpen(0); + //if (platform.gamepadgamepad == NULL) TRACELOG(LOG_WARNING, "PLATFORM: Unable to open game controller [ERROR: %s]", SDL_GetError()); } SDL_EventState(SDL_DROPFILE, SDL_ENABLE); From 601e391b068c9bc043b801486e02586012d58f20 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Oct 2023 20:43:52 +0100 Subject: [PATCH 0440/1350] Remove physac library from raylib building At this moment, physac is an external unmaintained library, better move out of raylib. --- src/Makefile | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/Makefile b/src/Makefile index c9094f096..d5726f5ed 100644 --- a/src/Makefile +++ b/src/Makefile @@ -78,13 +78,10 @@ RAYLIB_CONFIG_FLAGS ?= NONE RAYLIB_MODULE_AUDIO ?= TRUE RAYLIB_MODULE_MODELS ?= TRUE RAYLIB_MODULE_RAYGUI ?= FALSE -RAYLIB_MODULE_PHYSAC ?= FALSE # NOTE: Additional libraries have been moved to their own repos: # raygui: https://github.com/raysan5/raygui -# physac: https://github.com/raysan5/physac RAYLIB_MODULE_RAYGUI_PATH ?= $(RAYLIB_SRC_PATH)/../../raygui/src -RAYLIB_MODULE_PHYSAC_PATH ?= $(RAYLIB_SRC_PATH)/../../physac/src # Use external GLFW library instead of rglfw module USE_EXTERNAL_GLFW ?= FALSE @@ -553,9 +550,6 @@ endif ifeq ($(RAYLIB_MODULE_RAYGUI),TRUE) OBJS += raygui.o endif -ifeq ($(RAYLIB_MODULE_PHYSAC),TRUE) - OBJS += physac.o -endif ifeq ($(PLATFORM),PLATFORM_ANDROID) OBJS += android_native_app_glue.o @@ -680,23 +674,10 @@ else @echo "#include \"$(RAYLIB_MODULE_RAYGUI_PATH)/raygui.h\"" >> raygui.c endif -# Compile physac module -# NOTE: physac header should be distributed with raylib.h -physac.o : physac.c - $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) -physac.c: -ifeq ($(PLATFORM_SHELL), cmd) - @echo #define PHYSAC_IMPLEMENTATION > physac.c - @echo #include "$(RAYLIB_MODULE_PHYSAC_PATH)/physac.h" >> physac.c -else - @echo "#define PHYSAC_IMPLEMENTATION" > physac.c - @echo "#include \"$(RAYLIB_MODULE_PHYSAC_PATH)/physac.h\"" >> physac.c -endif # Compile android_native_app_glue module android_native_app_glue.o : $(NATIVE_APP_GLUE)/android_native_app_glue.c $(CC) -c $< $(CFLAGS) $(INCLUDE_PATHS) - # Install generated and needed files to desired directories. # On GNU/Linux and BSDs, there are some standard directories that contain extra # libraries and header files. These directories (often /usr/local/lib and @@ -780,7 +761,7 @@ clean: clean_shell_$(PLATFORM_SHELL) @echo "removed all generated files!" clean_shell_sh: - rm -fv *.o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).bc $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so* raygui.c physac.c + rm -fv *.o $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).a $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).bc $(RAYLIB_RELEASE_PATH)/lib$(RAYLIB_LIB_NAME).so* raygui.c ifeq ($(PLATFORM),PLATFORM_ANDROID) rm -fv $(NATIVE_APP_GLUE)/android_native_app_glue.o endif @@ -794,4 +775,3 @@ clean_shell_cmd: del lib$(RAYLIB_LIB_NAME)dll.a /s & \ del $(RAYLIB_LIB_NAME).dll /s & \ del raygui.c /s & \ - del physac.c /s From 4625c414319fbcb06fc5f1b633b8cf31c563522c Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Oct 2023 20:44:18 +0100 Subject: [PATCH 0441/1350] ADDED: Support for SDL building on Makefile --- examples/Makefile | 74 ++++++++++++++++++++++++++++++++++++++--- src/Makefile | 84 ++++++++++++++++++++++++++++++++--------------- 2 files changed, 128 insertions(+), 30 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index 136a0ab47..d10a35be4 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,6 +1,25 @@ #************************************************************************************************** # -# raylib makefile for Desktop platforms, Raspberry Pi, Android and HTML5 +# raylib makefile for multiple platforms +# +# This file supports building raylib examples for the following platforms: +# +# - PLATFORM_DESKTOP (GLFW backend): +# > Windows (Win32, Win64) +# > Linux (X11/Wayland desktop mode) +# > macOS/OSX (x64, arm64) +# > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +# - PLATFORM_DESKTOP_SDL (SDL backend): +# > Windows (Win32, Win64) +# > Linux (X11/Wayland desktop mode) +# > Others (not tested) +# - PLATFORM_WEB: +# > HTML5 (WebAssembly) +# - PLATFORM_DRM: +# > Raspberry Pi 0-5 (no X11/Wayland) +# > Linux native mode (KMS driver) +# - PLATFORM_ANDROID: +# > Android (ARM, ARM64) # # Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) # @@ -25,7 +44,7 @@ # Define required environment variables #------------------------------------------------------------------------------------------------ -# Define target platform: PLATFORM_DESKTOP, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB +# Define target platform: PLATFORM_DESKTOP, PLATFORM_DESKTOP_SDL, PLATFORM_DRM, PLATFORM_ANDROID, PLATFORM_WEB PLATFORM ?= PLATFORM_DESKTOP # Define required raylib variables @@ -47,6 +66,11 @@ BUILD_MODE ?= RELEASE # Use external GLFW library instead of rglfw module USE_EXTERNAL_GLFW ?= FALSE +# PLATFORM_DESKTOP_SDL: It requires SDL library to be provided externally +# WARNING: Library is not included in raylib, it MUST be configured by users +SDL_INCLUDE_PATH ?= $(RAYLIB_SRC_PATH)/external/SDL2-2.28.4/include +SDL_LIBRARY_PATH ?= $(RAYLIB_SRC_PATH)/external/SDL2-2.28.4/lib/x64 + # Use Wayland display server protocol on Linux desktop (by default it uses X11 windowing system) # NOTE: This variable is only used for PLATFORM_OS: LINUX USE_WAYLAND_DISPLAY ?= FALSE @@ -58,8 +82,8 @@ BUILD_WEB_HEAP_SIZE ?= 134217728 BUILD_WEB_RESOURCES ?= TRUE BUILD_WEB_RESOURCES_PATH ?= $(dir $<)resources@resources -# Determine PLATFORM_OS in case PLATFORM_DESKTOP or PLATFORM_WEB selected -ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_WEB)) +# Determine PLATFORM_OS when required +ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL PLATFORM_WEB)) # No uname.exe on MinGW!, but OS=Windows_NT on Windows! # ifeq ($(UNAME),Msys) -> Windows ifeq ($(OS),Windows_NT) @@ -224,6 +248,9 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) INCLUDE_PATHS += -I$(RAYLIB_INCLUDE_PATH) endif endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) + INCLUDE_PATHS += -I$(SDL_INCLUDE_PATH) +endif ifeq ($(PLATFORM),PLATFORM_DRM) INCLUDE_PATHS += -I$(RAYLIB_INCLUDE_PATH) INCLUDE_PATHS += -I/usr/include/libdrm @@ -254,6 +281,17 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDFLAGS += -Lsrc -L$(RAYLIB_LIB_PATH) endif endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) + ifeq ($(PLATFORM_OS),WINDOWS) + # NOTE: The resource .rc file contains windows executable icon and properties + LDFLAGS += $(RAYLIB_PATH)/src/raylib.rc.data + # -Wl,--subsystem,windows hides the console window + ifeq ($(BUILD_MODE), RELEASE) + LDFLAGS += -Wl,--subsystem,windows + endif + endif + LDFLAGS += -L$(SDL_LIBRARY_PATH) +endif ifeq ($(PLATFORM),PLATFORM_WEB) # -Os # size optimization # -O2 # optimization level 2, if used, also set --memory-init-file 0 @@ -346,6 +384,34 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDLIBS += -lglfw endif endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) + ifeq ($(PLATFORM_OS),WINDOWS) + # Libraries for Windows desktop compilation + LDLIBS = -lraylib -lSDL2 -lSDL2main -lopengl32 -lgdi32 + endif + ifeq ($(PLATFORM_OS),LINUX) + # Libraries for Debian GNU/Linux desktop compiling + # NOTE: Required packages: libegl1-mesa-dev + LDLIBS = -lraylib -lSDL2 -lSDL2main -lGL -lm -lpthread -ldl -lrt + + # On X11 requires also below libraries + LDLIBS += -lX11 + # NOTE: It seems additional libraries are not required any more, latest GLFW just dlopen them + #LDLIBS += -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor + + # On Wayland windowing system, additional libraries requires + ifeq ($(USE_WAYLAND_DISPLAY),TRUE) + LDLIBS += -lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon + endif + # Explicit link to libc + ifeq ($(RAYLIB_LIBTYPE),SHARED) + LDLIBS += -lc + endif + + # NOTE: On ARM 32bit arch, miniaudio requires atomics library + LDLIBS += -latomic + endif +endif ifeq ($(PLATFORM),PLATFORM_DRM) # Libraries for DRM compiling # NOTE: Required packages: libasound2-dev (ALSA) diff --git a/src/Makefile b/src/Makefile index d5726f5ed..2406588b8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,18 +1,28 @@ #****************************************************************************** # -# raylib makefile +# raylib makefile # -# Platforms supported: -# PLATFORM_DESKTOP: Windows (Win32, Win64) -# PLATFORM_DESKTOP: Linux (arm64, i386, x64) -# PLATFORM_DESKTOP: OSX/macOS (arm64, x86_64) -# PLATFORM_DESKTOP: FreeBSD, OpenBSD, NetBSD, DragonFly -# PLATFORM_ANDROID: Android (arm, i686, arm64, x86_64) -# PLATFORM_DRM: Linux native mode, including Raspberry Pi (RPI OS Bullseye) -# PLATFORM_WEB: HTML5 (Chrome, Firefox) +# This file supports building raylib library for the following platforms: # -# Many thanks to Milan Nikolic (@gen2brain) for implementing Android platform pipeline. -# Many thanks to Emanuele Petriglia for his contribution on GNU/Linux pipeline. +# - PLATFORM_DESKTOP (GLFW backend): +# > Windows (Win32, Win64) +# > Linux (X11/Wayland desktop mode) +# > macOS/OSX (x64, arm64) +# > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +# - PLATFORM_DESKTOP_SDL (SDL backend): +# > Windows (Win32, Win64) +# > Linux (X11/Wayland desktop mode) +# > Others (not tested) +# - PLATFORM_WEB: +# > HTML5 (WebAssembly) +# - PLATFORM_DRM: +# > Raspberry Pi 0-5 (no X11/Wayland) +# > Linux native mode (KMS driver) +# - PLATFORM_ANDROID: +# > Android (ARM, ARM64) +# +# Many thanks to Milan Nikolic (@gen2brain) for implementing Android platform pipeline. +# Many thanks to Emanuele Petriglia for his contribution on GNU/Linux pipeline. # # Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) # @@ -86,6 +96,11 @@ RAYLIB_MODULE_RAYGUI_PATH ?= $(RAYLIB_SRC_PATH)/../../raygui/src # Use external GLFW library instead of rglfw module USE_EXTERNAL_GLFW ?= FALSE +# PLATFORM_DESKTOP_SDL: It requires SDL library to be provided externally +# WARNING: Library is not included in raylib, it MUST be configured by users +SDL_INCLUDE_PATH ?= $(RAYLIB_SRC_PATH)/external/SDL2-2.28.4/include +SDL_LIBRARY_PATH ?= $(RAYLIB_SRC_PATH)/external/SDL2-2.28.4/lib/x64 + # Use Wayland display server protocol on Linux desktop (by default it uses X11 windowing system) # NOTE: This variable is only used for PLATFORM_OS: LINUX USE_WAYLAND_DISPLAY ?= FALSE @@ -98,8 +113,8 @@ ROOT = $(shell whoami) HOST_PLATFORM_OS ?= WINDOWS PLATFORM_OS ?= WINDOWS -# Determine PLATFORM_OS in case PLATFORM_DESKTOP selected -ifeq ($(PLATFORM),PLATFORM_DESKTOP) +# Determine PLATFORM_OS when required +ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL PLATFORM_WEB)) # No uname.exe on MinGW!, but OS=Windows_NT on Windows! # ifeq ($(UNAME),Msys) -> Windows ifeq ($(OS),Windows_NT) @@ -142,16 +157,7 @@ ifeq ($(PLATFORM),PLATFORM_DRM) endif endif ifeq ($(PLATFORM),PLATFORM_WEB) - ifeq ($(OS),Windows_NT) - PLATFORM_OS = WINDOWS - ifndef PLATFORM_SHELL - PLATFORM_SHELL = cmd - endif - else - UNAMEOS = $(shell uname) - ifeq ($(UNAMEOS),Linux) - PLATFORM_OS = LINUX - endif + ifeq ($(PLATFORM_OS),LINUX) ifndef PLATFORM_SHELL PLATFORM_SHELL = sh endif @@ -214,7 +220,10 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) #GRAPHICS = GRAPHICS_API_OPENGL_43 # Uncomment to use OpenGL 4.3 #GRAPHICS = GRAPHICS_API_OPENGL_ES2 # Uncomment to use OpenGL ES 2.0 (ANGLE) endif - +ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) + # By default use OpenGL 3.3 on desktop platform with SDL backend + GRAPHICS ?= GRAPHICS_API_OPENGL_33 +endif ifeq ($(PLATFORM),PLATFORM_DRM) # On DRM OpenGL ES 2.0 must be used GRAPHICS = GRAPHICS_API_OPENGL_ES2 @@ -413,14 +422,21 @@ endif # Define include paths for required headers: INCLUDE_PATHS # NOTE: Several external required libraries (stb and others) #------------------------------------------------------------------------------------------------ -INCLUDE_PATHS = -I. -Iexternal/glfw/include -Iexternal/glfw/deps/mingw +INCLUDE_PATHS = -I. # Define additional directories containing required header files ifeq ($(PLATFORM),PLATFORM_DESKTOP) + INCLUDE_PATHS += -Iexternal/glfw/include -Iexternal/glfw/deps/mingw ifeq ($(PLATFORM_OS),BSD) INCLUDE_PATHS += -I/usr/local/include endif endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) + INCLUDE_PATHS += -I$(SDL_INCLUDE_PATH) +endif +ifeq ($(PLATFORM),PLATFORM_WEB) + INCLUDE_PATHS += -Iexternal/glfw/include -Iexternal/glfw/deps/mingw +endif ifeq ($(PLATFORM),PLATFORM_DRM) INCLUDE_PATHS += -I/usr/include/libdrm ifeq ($(USE_RPI_CROSSCOMPILER), TRUE) @@ -470,10 +486,14 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).$(RAYLIB_API_VERSION).so -Lsrc -L/usr/local/lib endif endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) + LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) + LDFLAGS += -L$(SDL_LIBRARY_PATH) +endif ifeq ($(PLATFORM),PLATFORM_DRM) LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) ifeq ($(USE_RPI_CROSSCOMPILER), TRUE) - INCLUDE_PATHS += -L$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/lib -L$(RPI_TOOLCHAIN_SYSROOT)/usr/lib + LDFLAGS += -L$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/lib -L$(RPI_TOOLCHAIN_SYSROOT)/usr/lib endif endif ifeq ($(PLATFORM),PLATFORM_ANDROID) @@ -518,6 +538,18 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) LDLIBS = -lglfw endif endif +ifeq ($(PLATFORM),PLATFORM_DESKTOP_SDL) + ifeq ($(PLATFORM_OS),WINDOWS) + LDLIBS = -static-libgcc -lopengl32 -lgdi32 + endif + ifeq ($(PLATFORM_OS),LINUX) + LDLIBS = -lGL -lc -lm -lpthread -ldl -lrt + ifeq ($(USE_WAYLAND_DISPLAY),FALSE) + LDLIBS += -lX11 + endif + endif + LDLIBS += -lSDL2 -lSDL2main +endif ifeq ($(PLATFORM),PLATFORM_DRM) LDLIBS = -lGLESv2 -lEGL -ldrm -lgbm -lpthread -lrt -lm -ldl ifeq ($(RAYLIB_MODULE_AUDIO),TRUE) From 09075d515af4fcf3caca8c494e7bcf71d64060da Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Oct 2023 20:44:32 +0100 Subject: [PATCH 0442/1350] Some notes and comments --- src/rcore.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index b2eb78675..1a680ee9a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3,11 +3,15 @@ * rcore - Window/display management, Graphic device/context management and input management * * PLATFORMS SUPPORTED: -* - PLATFORM_DESKTOP: +* - PLATFORM_DESKTOP (GLFW backend): * > Windows (Win32, Win64) * > Linux (X11/Wayland desktop mode) * > macOS/OSX (x64, arm64) * > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +* - PLATFORM_DESKTOP_SDL (SDL backend): +* > Windows (Win32, Win64) +* > Linux (X11/Wayland desktop mode) +* > Others (not tested) * - PLATFORM_WEB: * > HTML5 (WebAssembly) * - PLATFORM_DRM: @@ -446,8 +450,8 @@ extern void UnloadFontDefault(void); // [Module: text] Unloads default font f extern int InitPlatform(void); // Initialize platform (graphics, inputs and more) extern void ClosePlatform(void); // Close platform -static void InitTimer(void); // Initialize timer (hi-resolution if available) -static void SetupFramebuffer(int width, int height); // Setup main framebuffer +static void InitTimer(void); // Initialize timer, hi-resolution if available (required by InitPlatform()) +static void SetupFramebuffer(int width, int height); // Setup main framebuffer (required by InitPlatform()) static void SetupViewport(int width, int height); // Set viewport for a provided width and height static void ScanDirectoryFiles(const char *basePath, FilePathList *list, const char *filter); // Scan all files and directories in a base path @@ -2933,7 +2937,7 @@ void InitTimer(void) // However, it can also reduce overall system performance, because the thread scheduler switches tasks more often. // High resolutions can also prevent the CPU power management system from entering power-saving modes. // Setting a higher resolution does not improve the accuracy of the high-resolution performance counter. -#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) +#if defined(_WIN32) && defined(SUPPORT_WINMM_HIGHRES_TIMER) && !defined(SUPPORT_BUSY_WAIT_LOOP) && !defined(PLATFORM_DESKTOP_SDL) timeBeginPeriod(1); // Setup high-resolution timer to 1ms (granularity of 1-2 ms) #endif From 049a6d475ddb10b81ccd896c05c7c6b878d499d8 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sun, 29 Oct 2023 16:55:02 -0300 Subject: [PATCH 0443/1350] Fix drm hang up on exit and mouse input issues (#3484) --- src/platforms/rcore_drm.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 6f459b1af..82701e310 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -906,6 +906,12 @@ int InitPlatform(void) rlLoadExtensions(eglGetProcAddress); //---------------------------------------------------------------------------- + // Initialize timming system + //---------------------------------------------------------------------------- + // NOTE: timming system must be initialized before the input events system + InitTimer(); + //---------------------------------------------------------------------------- + // Initialize input events system //---------------------------------------------------------------------------- InitEvdevInput(); // Evdev inputs initialization @@ -913,11 +919,6 @@ int InitPlatform(void) InitKeyboard(); // Keyboard init (stdin) //---------------------------------------------------------------------------- - // Initialize timming system - //---------------------------------------------------------------------------- - InitTimer(); - //---------------------------------------------------------------------------- - // Initialize storage system //---------------------------------------------------------------------------- CORE.Storage.basePath = GetWorkingDirectory(); From fc7dcff4a72e3ce9ea970ad4e03cfabed026fbad Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 29 Oct 2023 21:11:30 +0100 Subject: [PATCH 0444/1350] ADDED: Pseudo-random numbers generator! --- CMakeOptions.txt | 1 + src/config.h | 2 + src/external/rprand.h | 306 ++++++++++++++++++++++++++++++++++++++++++ src/raylib.h | 3 +- src/rcore.c | 41 ++++-- 5 files changed, 340 insertions(+), 13 deletions(-) create mode 100644 src/external/rprand.h diff --git a/CMakeOptions.txt b/CMakeOptions.txt index 799530951..823128b0f 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -37,6 +37,7 @@ cmake_dependent_option(SUPPORT_MODULE_RAUDIO "Include module: raudio" ON CUSTOMI # rcore.c cmake_dependent_option(SUPPORT_CAMERA_SYSTEM "Provide camera module (rcamera.h) with multiple predefined cameras: free, 1st/3rd person, orbital" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_GESTURES_SYSTEM "Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_RPRAND_GENERATOR "Include pseudo-random numbers generator (rprand.h), based on Xoshiro128** and SplitMix64" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_MOUSE_GESTURES "Mouse gestures are directly mapped like touches and processed by gestures system" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_SSH_KEYBOARD_RPI "Reconfigure standard input to receive key inputs, works with SSH connection" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_DEFAULT_FONT "Default font is loaded on window initialization to be available for the user to render simple text. If enabled, uses external module functions to load default raylib font (module: text)" ON CUSTOMIZE_BUILD ON) diff --git a/src/config.h b/src/config.h index 96fdd065f..c6d553a5e 100644 --- a/src/config.h +++ b/src/config.h @@ -45,6 +45,8 @@ #define SUPPORT_CAMERA_SYSTEM 1 // Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag #define SUPPORT_GESTURES_SYSTEM 1 +// Include pseudo-random numbers generator (rprand.h), based on Xoshiro128** and SplitMix64 +#define SUPPORT_RPRAND_GENERATOR 1 // Mouse gestures are directly mapped like touches and processed by gestures system #define SUPPORT_MOUSE_GESTURES 1 // Reconfigure standard input to receive key inputs, works with SSH connection. diff --git a/src/external/rprand.h b/src/external/rprand.h new file mode 100644 index 000000000..a9b3a6eaa --- /dev/null +++ b/src/external/rprand.h @@ -0,0 +1,306 @@ +/********************************************************************************************** +* +* rprand v1.0 - A simple and easy-to-use pseudo-random numbers generator (PRNG) +* +* FEATURES: +* - Pseudo-random values generation, 32 bits: [0..4294967295] +* - Sequence generation avoiding duplicate values +* - Using standard and proven prng algorithm (Xoshiro128**) +* - State initialized with a separate generator (SplitMix64) +* +* LIMITATIONS: +* - No negative numbers, up to the user to manage them +* +* POSSIBLE IMPROVEMENTS: +* - Support 64 bits generation +* +* ADDITIONAL NOTES: +* This library implements two pseudo-random number generation algorithms: +* +* - Xoshiro128** : https://prng.di.unimi.it/xoshiro128starstar.c +* - SplitMix64 : https://prng.di.unimi.it/splitmix64.c +* +* SplitMix64 is used to initialize the Xoshiro128** state, from a provided seed +* +* It's suggested to use SplitMix64 to initialize the state of the generators starting from +* a 64-bit seed, as research has shown that initialization must be performed with a generator +* radically different in nature from the one initialized to avoid correlation on similar seeds. +* +* CONFIGURATION: +* #define RPRAND_IMPLEMENTATION +* Generates the implementation of the library into the included file. +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation. +* +* DEPENDENCIES: none +* +* VERSIONS HISTORY: +* 1.0 (01-Jun-2023) First version +* +* +* LICENSE: zlib/libpng +* +* Copyright (c) 2023 Ramon Santamaria (@raysan5) +* +* This software is provided "as-is", without any express or implied warranty. In no event +* will the authors be held liable for any damages arising from the use of this software. +* +* Permission is granted to anyone to use this software for any purpose, including commercial +* applications, and to alter it and redistribute it freely, subject to the following restrictions: +* +* 1. The origin of this software must not be misrepresented; you must not claim that you +* wrote the original software. If you use this software in a product, an acknowledgment +* in the product documentation would be appreciated but is not required. +* +* 2. Altered source versions must be plainly marked as such, and must not be misrepresented +* as being the original software. +* +* 3. This notice may not be removed or altered from any source distribution. +* +**********************************************************************************************/ + +#ifndef RPRAND_H +#define RPRAND_H + +#define RPRAND_VERSION "1.0" + +// Function specifiers in case library is build/used as a shared library (Windows) +// NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll +#if defined(_WIN32) + #if defined(BUILD_LIBTYPE_SHARED) + #define RPRAND __declspec(dllexport) // We are building the library as a Win32 shared library (.dll) + #elif defined(USE_LIBTYPE_SHARED) + #define RPRAND __declspec(dllimport) // We are using the library as a Win32 shared library (.dll) + #endif +#endif + +// Function specifiers definition +#ifndef RPRANDAPI + #define RPRANDAPI // Functions defined as 'extern' by default (implicit specifiers) +#endif + +//---------------------------------------------------------------------------------- +// Defines and Macros +//---------------------------------------------------------------------------------- +// Allow custom memory allocators +#ifndef RPRAND_CALLOC + #define RPRAND_CALLOC(ptr,sz) calloc(ptr,sz) +#endif +#ifndef RPRAND_FREE + #define RPRAND_FREE(ptr) free(ptr) +#endif + +// Simple log system to avoid RPNG_LOG() calls if required +// NOTE: Avoiding those calls, also avoids const strings memory usage +#define RPRAND_SHOW_LOG_INFO +#if defined(RPNG_SHOW_LOG_INFO) + #define RPRAND_LOG(...) printf(__VA_ARGS__) +#else + #define RPRAND_LOG(...) +#endif + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +//... + +#ifdef __cplusplus +extern "C" { // Prevents name mangling of functions +#endif + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +//... + +//---------------------------------------------------------------------------------- +// Module Functions Declaration +//---------------------------------------------------------------------------------- +RPRANDAPI void rprand_set_seed(unsigned long long seed); // Set rprand_state for Xoshiro128**, seed is 64bit +RPRANDAPI unsigned int rprand_get_value(int min, int max); // Get random value within a range, min and max included + +RPRANDAPI unsigned int *rprand_load_sequence(unsigned int count, int min, int max); // Load pseudo-random numbers sequence with no duplicates +RPRANDAPI void rprand_unload_sequence(unsigned int *sequence); // Unload pseudo-random numbers sequence + +#ifdef __cplusplus +} +#endif + +#endif // RPRAND_H + +/*********************************************************************************** +* +* RPRAND IMPLEMENTATION +* +************************************************************************************/ + +#if defined(RPRAND_IMPLEMENTATION) + +#include // Required for: calloc(), free() +#include // Required for data types: uint32_t, uint64_t + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +// ... + +//---------------------------------------------------------------------------------- +// Global Variables Definition +//---------------------------------------------------------------------------------- +static uint64_t rprand_seed = 0; // SplitMix64 actual seed +static uint32_t rprand_state[4] = { 0 }; // Xoshiro128** state, nitialized by SplitMix64 + +//---------------------------------------------------------------------------------- +// Module internal functions declaration +//---------------------------------------------------------------------------------- +static uint32_t rprand_xoshiro(void); // Xoshiro128** generator (uses global rprand_state) +static uint64_t rprand_splitmix64(void); // SplitMix64 generator (uses seed to generate rprand_state) + +//---------------------------------------------------------------------------------- +// Module functions definition +//---------------------------------------------------------------------------------- +// Set rprand_state for Xoshiro128** +// NOTE: We use a custom generation algorithm using SplitMix64 +void rprand_set_seed(unsigned long long seed) +{ + rprand_seed = (uint64_t)seed; // Set SplitMix64 seed for further use + + // To generate the Xoshiro128** state, we use SplitMix64 generator first + // We generate 4 pseudo-random 64bit numbers that we combine using their LSB|MSB + rprand_state[0] = (uint32_t)(rprand_splitmix64() & 0xffffffff); + rprand_state[1] = (uint32_t)((rprand_splitmix64() & 0xffffffff00000000) >> 32); + rprand_state[2] = (uint32_t)(rprand_splitmix64() & 0xffffffff); + rprand_state[3] = (uint32_t)((rprand_splitmix64() & 0xffffffff00000000) >> 32); +} + +// Get random value within a range, min and max included +unsigned int rprand_get_value(int min, int max) +{ + unsigned int value = rprand_xoshiro()%(max - min) + min; + + return value; +} + +// Load pseudo-random numbers sequence with no duplicates +unsigned int *rprand_load_sequence(unsigned int count, int min, int max) +{ + unsigned int *sequence = NULL; + + if (count > (max - min)) + { + RPRAND_LOG("WARNING: Sequence count required is greater than range provided\n"); + //count = (max - min); + return sequence; + } + + sequence = (unsigned int *)RPRAND_CALLOC(count, sizeof(unsigned int)); + + uint32_t value = 0; + int value_count = 0; + bool value_is_dup = false; + + for (int i = 0; value_count < count; i++) + { + value = rprand_xoshiro()%(max - min) + min; + value_is_dup = false; + + for (int j = 0; j < value_count; j++) + { + if (sequence[j] == value) + { + value_is_dup = true; + break; + } + } + + if (!value_is_dup) + { + sequence[value_count] = value; + value_count++; + } + } + + return sequence; +} + +// Unload pseudo-random numbers sequence +void rprand_unload_sequence(unsigned int *sequence) +{ + RPRAND_FREE(sequence); + sequence = NULL; +} + +//---------------------------------------------------------------------------------- +// Module internal functions definition +//---------------------------------------------------------------------------------- +static inline uint32_t rprand_rotate_left(const uint32_t x, int k) +{ + return (x << k) | (x >> (32 - k)); +} + +// Xoshiro128** generator info: +// +// Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org) +// +// To the extent possible under law, the author has dedicated all copyright +// and related and neighboring rights to this software to the public domain +// worldwide. This software is distributed without any warranty. +// +// See . +// +// This is xoshiro128** 1.1, one of our 32-bit all-purpose, rock-solid +// generators. It has excellent speed, a state size (128 bits) that is +// large enough for mild parallelism, and it passes all tests we are aware +// of. +// +// Note that version 1.0 had mistakenly s[0] instead of s[1] as state +// word passed to the scrambler. +// +// For generating just single-precision (i.e., 32-bit) floating-point +// numbers, xoshiro128+ is even faster. +// +// The state must be seeded so that it is not everywhere zero. +// +uint32_t rprand_xoshiro(void) +{ + const uint32_t result = rprand_rotate_left(rprand_state[1]*5, 7)*9; + const uint32_t t = rprand_state[1] << 9; + + rprand_state[2] ^= rprand_state[0]; + rprand_state[3] ^= rprand_state[1]; + rprand_state[1] ^= rprand_state[2]; + rprand_state[0] ^= rprand_state[3]; + + rprand_state[2] ^= t; + + rprand_state[3] = rprand_rotate_left(rprand_state[3], 11); + + return result; +} + +// SplitMix64 generator info: +// +// Written in 2015 by Sebastiano Vigna (vigna@acm.org) +// +// To the extent possible under law, the author has dedicated all copyright +// and related and neighboring rights to this software to the public domain +// worldwide. This software is distributed without any warranty. +// +// See . +// +// +// This is a fixed-increment version of Java 8's SplittableRandom generator +// See http://dx.doi.org/10.1145/2714064.2660195 and +// http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html +// +// It is a very fast generator passing BigCrush, and it can be useful if +// for some reason you absolutely want 64 bits of state. +uint64_t rprand_splitmix64() +{ + uint64_t z = (rprand_seed += 0x9e3779b97f4a7c15); + z = (z ^ (z >> 30))*0xbf58476d1ce4e5b9; + z = (z ^ (z >> 27))*0x94d049bb133111eb; + return z ^ (z >> 31); +} + +#endif // RPRAND_IMPLEMENTATION \ No newline at end of file diff --git a/src/raylib.h b/src/raylib.h index d8bf1e07c..115e2be99 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1068,8 +1068,9 @@ RLAPI void PollInputEvents(void); // Register al RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution) // Misc. functions -RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included) RLAPI void SetRandomSeed(unsigned int seed); // Set the seed for the random number generator +RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included) + RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (filename extension defines format) RLAPI void SetConfigFlags(unsigned int flags); // Setup init configuration flags (view FLAGS) RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available) diff --git a/src/rcore.c b/src/rcore.c index 1a680ee9a..902f84c65 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -131,6 +131,11 @@ #include "external/sdefl.h" // Deflate (RFC 1951) compressor #endif +#if defined(SUPPORT_RPRAND_GENERATOR) + #define RPRAND_IMPLEMENTATION + #include "external/rprand.h" +#endif + #if defined(__linux__) && !defined(_GNU_SOURCE) #define _GNU_SOURCE #endif @@ -1647,31 +1652,43 @@ void WaitTime(double seconds) // NOTE: Functions with a platform-specific implementation on rcore_.c //void OpenURL(const char *url) + +// Set the seed for the random number generator +void SetRandomSeed(unsigned int seed) +{ +#if defined(SUPPORT_RPRAND_GENERATOR) + rprand_set_seed(seed); +#else + srand(seed); +#endif +} + // Get a random value between min and max (both included) -// WARNING: Ranges higher than RAND_MAX will return invalid results -// More specifically, if (max - min) > INT_MAX there will be an overflow, -// and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold int GetRandomValue(int min, int max) { + int value = 0; + if (min > max) { int tmp = max; max = min; min = tmp; } - + +#if defined(SUPPORT_RPRAND_GENERATOR) + value = rprand_get_value(min, max); +#else + // WARNING: Ranges higher than RAND_MAX will return invalid results + // More specifically, if (max - min) > INT_MAX there will be an overflow, + // and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold if ((unsigned int)(max - min) > (unsigned int)RAND_MAX) { TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); } - - return (rand()%(abs(max - min) + 1) + min); -} - -// Set the seed for the random number generator -void SetRandomSeed(unsigned int seed) -{ - srand(seed); + + value = (rand()%(abs(max - min) + 1) + min); +#endif + return value; } // Takes a screenshot of current screen (saved a .png) From 15632876f7d27857888d5eeab5caf0e322a5b791 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Mon, 30 Oct 2023 08:02:35 -0300 Subject: [PATCH 0445/1350] Fix examples Makefile for SDL (#3486) --- examples/Makefile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/Makefile b/examples/Makefile index d10a35be4..5af5a5590 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -52,6 +52,9 @@ PROJECT_NAME ?= raylib_examples RAYLIB_VERSION ?= 5.0.0 RAYLIB_PATH ?= .. +# Define raylib source code path +RAYLIB_SRC_PATH ?= ../src + # Locations of raylib.h and libraylib.a/libraylib.so # NOTE: Those variables are only used for PLATFORM_OS: LINUX, BSD RAYLIB_INCLUDE_PATH ?= /usr/local/include From 9642fffbbbdda0d31bedae6126e7b3b9073ec407 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 30 Oct 2023 13:13:31 +0100 Subject: [PATCH 0446/1350] REVIEWED: `GetRender*()` issue on macOS highDPI #3367 --- src/rcore.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 902f84c65..415fecfda 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -748,13 +748,27 @@ int GetScreenHeight(void) // Get current render width which is equal to screen width*dpi scale int GetRenderWidth(void) { - return CORE.Window.render.width; + int width = 0; +#if defined(__APPLE__) + Vector2 scale = GetWindowScaleDPI(); + width = (int)((float)CORE.Window.render.width*scale.x); +#else + width = CORE.Window.render.width; +#endif + return width; } // Get current screen height which is equal to screen height*dpi scale int GetRenderHeight(void) { - return CORE.Window.render.height; + int height = 0; +#if defined(__APPLE__) + Vector2 scale = GetWindowScaleDPI(); + height = (int)((float)CORE.Window.render.width*scale.y); +#else + height = CORE.Window.render.height; +#endif + return height; } // Enable waiting for events on EndDrawing(), no automatic event polling From abdebc244d41508c246a3a604f9f8b94d5758704 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 30 Oct 2023 13:14:15 +0100 Subject: [PATCH 0447/1350] Update rcore.c --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 415fecfda..fdc521207 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -764,7 +764,7 @@ int GetRenderHeight(void) int height = 0; #if defined(__APPLE__) Vector2 scale = GetWindowScaleDPI(); - height = (int)((float)CORE.Window.render.width*scale.y); + height = (int)((float)CORE.Window.render.height*scale.y); #else height = CORE.Window.render.height; #endif From b8fce54c0fc443c76dcac7c7e1adba9fd7e2a4df Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 30 Oct 2023 13:29:14 +0100 Subject: [PATCH 0448/1350] Minor tweaks --- src/raylib.h | 2 +- src/rcore.c | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 115e2be99..3f6328ada 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -506,7 +506,7 @@ typedef struct FilePathList { char **paths; // Filepaths entries } FilePathList; -// Automation event (opaque struct) +// Automation event typedef struct AutomationEvent { unsigned int frame; // Event frame unsigned int type; // Event type (AutomationEventType) diff --git a/src/rcore.c b/src/rcore.c index fdc521207..333fa4bfa 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2995,8 +2995,6 @@ void SetupViewport(int width, int height) // NOTE: We consider render size (scaled) and offset in case black bars are required and // render area does not match full display area (this situation is only applicable on fullscreen mode) #if defined(__APPLE__) - //float xScale = 1.0f, yScale = 1.0f; - //glfwGetWindowContentScale(CORE.Window.handle, &xScale, &yScale); Vector2 scale = GetWindowScaleDPI(); rlViewport(CORE.Window.renderOffset.x/2*scale.x, CORE.Window.renderOffset.y/2*scale.y, (CORE.Window.render.width)*scale.x, (CORE.Window.render.height)*scale.y); #else From 7677e4b92842a317f883c91ce9ad0cd6963d9341 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 30 Oct 2023 20:41:33 +0100 Subject: [PATCH 0449/1350] REVIEWED: `GetModelBoundingBox()` #3485 --- src/rmodels.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rmodels.c b/src/rmodels.c index f9018eaaf..0a997f858 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1170,6 +1170,12 @@ BoundingBox GetModelBoundingBox(Model model) bounds.max = temp; } } + + // Apply model.transform to bounding box + // WARNING: Current BoundingBox structure design does not support rotation transformations, + // in those cases is up to the user to calculate the proper box bounds (8 vertices transformed) + bounds.min = Vector3Transform(bounds.min, model.transform); + bounds.max = Vector3Transform(bounds.max, model.transform); return bounds; } From ff04d52f12c95b0b25faaffc4e68abed9ba2b474 Mon Sep 17 00:00:00 2001 From: Jett <30197659+JettMonstersGoBoom@users.noreply.github.com> Date: Tue, 31 Oct 2023 03:43:32 -0400 Subject: [PATCH 0450/1350] Added rlEnablePointMode (#3490) for rendering meshes with points. similar to wire mode. (NOTE) they still backface cull, so disable that if you want to show the entire mesh. --- src/rlgl.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/rlgl.h b/src/rlgl.h index 7c93bf929..707555dd5 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -632,7 +632,8 @@ RLAPI void rlEnableScissorTest(void); // Enable scissor test RLAPI void rlDisableScissorTest(void); // Disable scissor test RLAPI void rlScissor(int x, int y, int width, int height); // Scissor test RLAPI void rlEnableWireMode(void); // Enable wire mode -RLAPI void rlDisableWireMode(void); // Disable wire mode +RLAPI void rlEnablePointMode(void); // Enable point mode +RLAPI void rlDisableWireMode(void); // Disable wire mode ( and point ) maybe rename RLAPI void rlSetLineWidth(float width); // Set the line drawing width RLAPI float rlGetLineWidth(void); // Get the line drawing width RLAPI void rlEnableSmoothLines(void); // Enable line aliasing @@ -1817,6 +1818,14 @@ void rlEnableWireMode(void) #endif } +void rlEnablePointMode(void) +{ +#if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) + // NOTE: glPolygonMode() not available on OpenGL ES + glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); + glEnable(GL_PROGRAM_POINT_SIZE); +#endif +} // Disable wire mode void rlDisableWireMode(void) { From d8acceca14da2ee5f8e03cd23347b28e438334bd Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Tue, 31 Oct 2023 06:10:43 -0300 Subject: [PATCH 0451/1350] Fix example core_3d_camera_free (#3488) --- examples/core/core_3d_camera_free.c | 10 ++++------ examples/core/core_3d_camera_free.png | Bin 25317 -> 25956 bytes 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/examples/core/core_3d_camera_free.c b/examples/core/core_3d_camera_free.c index 78200a642..ec849758a 100644 --- a/examples/core/core_3d_camera_free.c +++ b/examples/core/core_3d_camera_free.c @@ -65,15 +65,13 @@ int main(void) EndMode3D(); - DrawRectangle( 10, 10, 320, 133, Fade(SKYBLUE, 0.5f)); - DrawRectangleLines( 10, 10, 320, 133, BLUE); + DrawRectangle( 10, 10, 320, 93, Fade(SKYBLUE, 0.5f)); + DrawRectangleLines( 10, 10, 320, 93, BLUE); DrawText("Free camera default controls:", 20, 20, 10, BLACK); DrawText("- Mouse Wheel to Zoom in-out", 40, 40, 10, DARKGRAY); DrawText("- Mouse Wheel Pressed to Pan", 40, 60, 10, DARKGRAY); - DrawText("- Alt + Mouse Wheel Pressed to Rotate", 40, 80, 10, DARKGRAY); - //DrawText("- Alt + Ctrl + Mouse Wheel Pressed for Smooth Zoom", 40, 100, 10, DARKGRAY); - DrawText("- Z to zoom to (0, 0, 0)", 40, 120, 10, DARKGRAY); + DrawText("- Z to zoom to (0, 0, 0)", 40, 80, 10, DARKGRAY); EndDrawing(); //---------------------------------------------------------------------------------- @@ -85,4 +83,4 @@ int main(void) //-------------------------------------------------------------------------------------- return 0; -} \ No newline at end of file +} diff --git a/examples/core/core_3d_camera_free.png b/examples/core/core_3d_camera_free.png index 7874eedcfd3a820bb10af294058e688e313481a0..71dfc1c80a307772d2c1a30c7039a428e4450308 100644 GIT binary patch literal 25956 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 Date: Tue, 31 Oct 2023 06:16:12 -0300 Subject: [PATCH 0452/1350] Fix relative mouse mode for DRM (#3492) --- src/platforms/rcore_drm.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 82701e310..9212d7e0c 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -121,6 +121,7 @@ typedef struct { Vector2 eventWheelMove; // Registers the event mouse wheel variation // NOTE: currentButtonState[] can't be written directly due to multithreading, app could miss the update char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab + bool cursorRelative; // Relative cursor mode // Gamepad data pthread_t gamepadThreadId; // Gamepad reading thread id @@ -400,6 +401,7 @@ void EnableCursor(void) // Set cursor position in the middle SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + platform.cursorRelative = false; CORE.Input.Mouse.cursorHidden = false; } @@ -409,6 +411,7 @@ void DisableCursor(void) // Set cursor position in the middle SetMousePosition(CORE.Window.screen.width/2, CORE.Window.screen.height/2); + platform.cursorRelative = true; CORE.Input.Mouse.cursorHidden = true; } @@ -522,6 +525,13 @@ void PollInputEvents(void) PollKeyboardEvents(); + // Register previous mouse position + if (platform.cursorRelative) + { + CORE.Input.Mouse.previousPosition = CORE.Input.Mouse.currentPosition; + CORE.Input.Mouse.currentPosition = (Vector2){ 0.0f, 0.0f }; + } + // Register previous mouse states CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; CORE.Input.Mouse.currentWheelMove = platform.eventWheelMove; @@ -1535,8 +1545,16 @@ static void *EventThread(void *arg) { if (event.code == REL_X) { - CORE.Input.Mouse.currentPosition.x += event.value; - CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x; + if (platform.cursorRelative) + { + CORE.Input.Mouse.currentPosition.x -= event.value; + CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x; + } + else + { + CORE.Input.Mouse.currentPosition.x += event.value; + CORE.Input.Touch.position[0].x = CORE.Input.Mouse.currentPosition.x; + } touchAction = 2; // TOUCH_ACTION_MOVE gestureUpdate = true; @@ -1544,8 +1562,16 @@ static void *EventThread(void *arg) if (event.code == REL_Y) { - CORE.Input.Mouse.currentPosition.y += event.value; - CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y; + if (platform.cursorRelative) + { + CORE.Input.Mouse.currentPosition.y -= event.value; + CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y; + } + else + { + CORE.Input.Mouse.currentPosition.y += event.value; + CORE.Input.Touch.position[0].y = CORE.Input.Mouse.currentPosition.y; + } touchAction = 2; // TOUCH_ACTION_MOVE gestureUpdate = true; From 0d186a0557a3c74d34ddce1daa7a66ae0fc1e699 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 11:32:07 +0100 Subject: [PATCH 0453/1350] REVIEWED: `LoadModel()`, removed cube fallback mechanism #3459 --- src/rmodels.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 0a997f858..b0120932a 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1052,26 +1052,16 @@ Model LoadModel(const char *fileName) // Make sure model transform is set to identity matrix! model.transform = MatrixIdentity(); - if (model.meshCount == 0) + if ((model.meshCount != 0) && (model.meshes != NULL)) { - model.meshCount = 1; - model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); -#if defined(SUPPORT_MESH_GENERATION) - TRACELOG(LOG_WARNING, "MESH: [%s] Failed to load mesh data, default to cube mesh", fileName); - model.meshes[0] = GenMeshCube(1.0f, 1.0f, 1.0f); -#else - TRACELOG(LOG_WARNING, "MESH: [%s] Failed to load mesh data", fileName); -#endif - } - else - { - // Upload vertex data to GPU (static mesh) + // Upload vertex data to GPU (static meshes) for (int i = 0; i < model.meshCount; i++) UploadMesh(&model.meshes[i], false); } + else TRACELOG(LOG_WARNING, "MESH: [%s] Failed to load model mesh(es) data", fileName); if (model.materialCount == 0) { - TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to load material data, default to white material", fileName); + TRACELOG(LOG_WARNING, "MATERIAL: [%s] Failed to load model material data, default to white material", fileName); model.materialCount = 1; model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); From 68420127485990746eab874a4f9d7b08e5acfd13 Mon Sep 17 00:00:00 2001 From: veins1 <19636663+veins1@users.noreply.github.com> Date: Tue, 31 Oct 2023 19:48:24 +0500 Subject: [PATCH 0454/1350] Fix QOA seeking (#3494) --- src/raudio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/raudio.c b/src/raudio.c index dcc9f706a..476676049 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1797,7 +1797,9 @@ void SeekMusicStream(Music music, float position) case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, positionInFrames); break; #endif #if defined(SUPPORT_FILEFORMAT_QOA) - case MUSIC_AUDIO_QOA: qoaplay_seek_frame((qoaplay_desc *)music.ctxData, positionInFrames); break; + //qoaplay_seek_frame seeks to QOA frame, not PCM frame, therefore we need to compute QOA frame number and change positionInFrames + case MUSIC_AUDIO_QOA: { int qoaFrame = positionInFrames/QOA_FRAME_LEN; qoaplay_seek_frame((qoaplay_desc*)music.ctxData, qoaFrame); + positionInFrames = ((qoaplay_desc*)music.ctxData)->sample_position; break; } #endif #if defined(SUPPORT_FILEFORMAT_FLAC) case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, positionInFrames); break; From 0a3567439d90bb1a4955b13441f157ec9c956998 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 15:49:42 +0100 Subject: [PATCH 0455/1350] Comments tweaks --- src/platforms/rcore_android.c | 4 ++-- src/platforms/rcore_desktop.c | 5 ++--- src/platforms/rcore_desktop_sdl.c | 4 ++-- src/platforms/rcore_drm.c | 5 +++-- src/platforms/rcore_template.c | 4 ++-- src/platforms/rcore_web.c | 4 ++-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/platforms/rcore_android.c b/src/platforms/rcore_android.c index 83450bb0a..5d6f7d5ba 100644 --- a/src/platforms/rcore_android.c +++ b/src/platforms/rcore_android.c @@ -21,8 +21,8 @@ * Custom flag for rcore on target platform -not used- * * DEPENDENCIES: -* Android NDK - Provides C API to access Android functionality -* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* - Android NDK: Provides C API to access Android functionality +* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * * LICENSE: zlib/libpng diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index 08b329e85..267262c99 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -24,8 +24,8 @@ * Custom flag for rcore on target platform -not used- * * DEPENDENCIES: -* rglfw - Manage graphic device, OpenGL context and inputs (Windows, Linux, OSX, FreeBSD...) -* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* - rglfw: Manage graphic device, OpenGL context and inputs (Windows, Linux, OSX, FreeBSD...) +* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * * LICENSE: zlib/libpng @@ -1766,7 +1766,6 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int // Gesture data is sent to gestures-system for processing ProcessGestureEvent(gestureEvent); - #endif } diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 8793e6c77..7245b2d5c 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -24,8 +24,8 @@ * Custom flag for rcore on target platform -not used- * * DEPENDENCIES: -* - SDL 2 (main library) -* - Dependency 02 +* - SDL 2 (main library): Windowing and inputs management +* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * * LICENSE: zlib/libpng diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 9212d7e0c..632686767 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -3,7 +3,7 @@ * rcore_drm - Functions to manage window, graphics device and inputs * * PLATFORM: DRM -* - Raspberry Pi 0-5 +* - Raspberry Pi 0-5 (native mode) * - Linux native mode (KMS driver) * * LIMITATIONS: @@ -23,7 +23,8 @@ * running processes orblocking the device if not restored properly. Use with care. * * DEPENDENCIES: -* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* - DRM and GLM: System libraries for display initialization and configuration +* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * * LICENSE: zlib/libpng diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index 1cebfa798..9a815ec4e 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -21,8 +21,8 @@ * Custom flag for rcore on target platform -not used- * * DEPENDENCIES: -* - Dependency 01 -* - Dependency 02 +* - +* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * * LICENSE: zlib/libpng diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index d797d99d7..959f60332 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -20,8 +20,8 @@ * Custom flag for rcore on target platform -not used- * * DEPENDENCIES: -* emscripten - Allow interaction between browser API and C -* gestures - Gestures system for touch-ready devices (or simulated from mouse inputs) +* - emscripten: Allow interaction between browser API and C +* - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * * LICENSE: zlib/libpng From f88604e6d5e0b17280b05081450a0e9b31d8621d Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 15:54:38 +0100 Subject: [PATCH 0456/1350] Reviewed QOA seek PR --- src/external/qoaplay.c | 4 ++-- src/raudio.c | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/external/qoaplay.c b/src/external/qoaplay.c index 7f937f4fa..039e27974 100644 --- a/src/external/qoaplay.c +++ b/src/external/qoaplay.c @@ -36,7 +36,7 @@ // QOA streaming data descriptor typedef struct { qoa_desc info; // QOA descriptor data - + FILE *file; // QOA file to read, if NULL, using memory buffer -> file_data unsigned char *file_data; // QOA file data on memory unsigned int file_data_size; // QOA file data on memory size @@ -107,7 +107,7 @@ qoaplay_desc *qoaplay_open(const char *path) unsigned int sample_data_size = qoa.channels*QOA_FRAME_LEN*sizeof(short)*2; qoaplay_desc *qoa_ctx = QOA_MALLOC(sizeof(qoaplay_desc) + buffer_size + sample_data_size); memset(qoa_ctx, 0, sizeof(qoaplay_desc)); - + qoa_ctx->file = file; qoa_ctx->file_data = NULL; qoa_ctx->file_data_size = 0; diff --git a/src/raudio.c b/src/raudio.c index 476676049..e38e51e6c 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -1797,9 +1797,14 @@ void SeekMusicStream(Music music, float position) case MUSIC_AUDIO_MP3: drmp3_seek_to_pcm_frame((drmp3 *)music.ctxData, positionInFrames); break; #endif #if defined(SUPPORT_FILEFORMAT_QOA) - //qoaplay_seek_frame seeks to QOA frame, not PCM frame, therefore we need to compute QOA frame number and change positionInFrames - case MUSIC_AUDIO_QOA: { int qoaFrame = positionInFrames/QOA_FRAME_LEN; qoaplay_seek_frame((qoaplay_desc*)music.ctxData, qoaFrame); - positionInFrames = ((qoaplay_desc*)music.ctxData)->sample_position; break; } + case MUSIC_AUDIO_QOA: + { + int qoaFrame = positionInFrames/QOA_FRAME_LEN; + qoaplay_seek_frame((qoaplay_desc *)music.ctxData, qoaFrame); // Seeks to QOA frame, not PCM frame + + // We need to compute QOA frame number and update positionInFrames + positionInFrames = ((qoaplay_desc *)music.ctxData)->sample_position; + } break; #endif #if defined(SUPPORT_FILEFORMAT_FLAC) case MUSIC_AUDIO_FLAC: drflac_seek_to_pcm_frame((drflac *)music.ctxData, positionInFrames); break; From de7beef05d56a23c4ab06202013965e3da014ef3 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 15:54:52 +0100 Subject: [PATCH 0457/1350] Remove trailing spaces --- src/platforms/rcore_desktop.c | 2 +- src/platforms/rcore_template.c | 2 +- src/rcore.c | 8 ++++---- src/rlgl.h | 4 ++-- src/rmodels.c | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index 267262c99..3d38dc898 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -755,7 +755,7 @@ int GetCurrentMonitor(void) // to try to detect the "current monitor" for that window, note that // this is probably an overengineered solution for a very side case // trying to match SDL behaviour - + int closestDist = 0x7FFFFFFF; // Window center position diff --git a/src/platforms/rcore_template.c b/src/platforms/rcore_template.c index 9a815ec4e..5d4721c84 100644 --- a/src/platforms/rcore_template.c +++ b/src/platforms/rcore_template.c @@ -21,7 +21,7 @@ * Custom flag for rcore on target platform -not used- * * DEPENDENCIES: -* - +* - * - gestures: Gestures system for touch-ready devices (or simulated from mouse inputs) * * diff --git a/src/rcore.c b/src/rcore.c index 333fa4bfa..175d68611 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1681,17 +1681,17 @@ void SetRandomSeed(unsigned int seed) int GetRandomValue(int min, int max) { int value = 0; - + if (min > max) { int tmp = max; max = min; min = tmp; } - + #if defined(SUPPORT_RPRAND_GENERATOR) value = rprand_get_value(min, max); -#else +#else // WARNING: Ranges higher than RAND_MAX will return invalid results // More specifically, if (max - min) > INT_MAX there will be an overflow, // and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold @@ -1699,7 +1699,7 @@ int GetRandomValue(int min, int max) { TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); } - + value = (rand()%(abs(max - min) + 1) + min); #endif return value; diff --git a/src/rlgl.h b/src/rlgl.h index 707555dd5..fcb8feeb6 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -633,7 +633,7 @@ RLAPI void rlDisableScissorTest(void); // Disable scissor test RLAPI void rlScissor(int x, int y, int width, int height); // Scissor test RLAPI void rlEnableWireMode(void); // Enable wire mode RLAPI void rlEnablePointMode(void); // Enable point mode -RLAPI void rlDisableWireMode(void); // Disable wire mode ( and point ) maybe rename +RLAPI void rlDisableWireMode(void); // Disable wire mode ( and point ) maybe rename RLAPI void rlSetLineWidth(float width); // Set the line drawing width RLAPI float rlGetLineWidth(void); // Get the line drawing width RLAPI void rlEnableSmoothLines(void); // Enable line aliasing @@ -1823,7 +1823,7 @@ void rlEnablePointMode(void) #if defined(GRAPHICS_API_OPENGL_11) || defined(GRAPHICS_API_OPENGL_33) // NOTE: glPolygonMode() not available on OpenGL ES glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); - glEnable(GL_PROGRAM_POINT_SIZE); + glEnable(GL_PROGRAM_POINT_SIZE); #endif } // Disable wire mode diff --git a/src/rmodels.c b/src/rmodels.c index b0120932a..68c2d75bc 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1160,7 +1160,7 @@ BoundingBox GetModelBoundingBox(Model model) bounds.max = temp; } } - + // Apply model.transform to bounding box // WARNING: Current BoundingBox structure design does not support rotation transformations, // in those cases is up to the user to calculate the proper box bounds (8 vertices transformed) From 3645244f9fc1874e72252138ca4ed0ab849a2fd9 Mon Sep 17 00:00:00 2001 From: Justin <72092018+27justin@users.noreply.github.com> Date: Tue, 31 Oct 2023 20:13:12 +0100 Subject: [PATCH 0458/1350] examples/shaders: Add an example for deferred shading (#3496) * add example for deferred rendering/shading * adapt convention --------- Co-authored-by: 27justin --- examples/Makefile | 3 +- examples/Makefile.Web | 3 +- examples/README.md | 9 +- .../shaders/glsl330/deferred_shading.fs | 55 +++ .../shaders/glsl330/deferred_shading.vs | 11 + .../resources/shaders/glsl330/gbuffer.fs | 22 ++ .../resources/shaders/glsl330/gbuffer.vs | 24 ++ examples/shaders/shaders_deferred_render.c | 321 ++++++++++++++++++ examples/shaders/shaders_deferred_render.png | Bin 0 -> 90692 bytes 9 files changed, 442 insertions(+), 6 deletions(-) create mode 100644 examples/shaders/resources/shaders/glsl330/deferred_shading.fs create mode 100644 examples/shaders/resources/shaders/glsl330/deferred_shading.vs create mode 100644 examples/shaders/resources/shaders/glsl330/gbuffer.fs create mode 100644 examples/shaders/resources/shaders/glsl330/gbuffer.vs create mode 100644 examples/shaders/shaders_deferred_render.c create mode 100644 examples/shaders/shaders_deferred_render.png diff --git a/examples/Makefile b/examples/Makefile index 5af5a5590..fe0ee9fbd 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -562,7 +562,8 @@ SHADERS = \ shaders/shaders_mesh_instancing \ shaders/shaders_multi_sample2d \ shaders/shaders_write_depth \ - shaders/shaders_hybrid_render + shaders/shaders_hybrid_render \ + shaders/shaders_deferred_render AUDIO = \ audio/audio_module_playing \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 9c2cadc37..c57453ab3 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -468,7 +468,8 @@ SHADERS = \ shaders/shaders_mesh_instancing \ shaders/shaders_multi_sample2d \ shaders/shaders_write_depth \ - shaders/shaders_hybrid_render + shaders/shaders_hybrid_render \ + shaders/shaders_deferred_render AUDIO = \ audio/audio_module_playing \ diff --git a/examples/README.md b/examples/README.md index 82e4b7825..0fed8acd0 100644 --- a/examples/README.md +++ b/examples/README.md @@ -176,6 +176,7 @@ Examples using raylib shaders functionality, including shaders loading, paramete | 114 | [shaders_mesh_instancing](shaders/shaders_mesh_instancing.c) | shaders_mesh_instancing | ⭐️⭐️⭐️⭐️ | 3.7 | **4.2** | [seanpringle](https://github.com/seanpringle) | | 115 | [shaders_multi_sample2d](shaders/shaders_multi_sample2d.c) | shaders_multi_sample2d | ⭐️⭐️☆☆ | 3.5 | 3.5 | [Ray](https://github.com/raysan5) | | 116 | [shaders_spotlight](shaders/shaders_spotlight.c) | shaders_spotlight | ⭐️⭐️☆☆ | 2.5 | 3.7 | [Chris Camacho](https://github.com/codifies) | +| 117 | [shaders_deferred_render](shaders/shaders_deferred_render.c) | shaders_deferred_render | ⭐️⭐️⭐️⭐️ | 4.5 | 4.5 | [Justin Andreas Lacoste](https://github.com/27justin) | ### category: audio @@ -183,10 +184,10 @@ Examples using raylib audio functionality, including sound/music loading and pla | ## | example | image | difficulty
    level | version
    created | last version
    updated | original
    developer | |----|----------|--------|:-------------------:|:------------------:|:------------------:|:----------| -| 117 | [audio_module_playing](audio/audio_module_playing.c) | audio_module_playing | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) | -| 118 | [audio_music_stream](audio/audio_music_stream.c) | audio_music_stream | ⭐️☆☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | -| 119 | [audio_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐️⭐️⭐️☆ | 1.6 | **4.2** | [Ray](https://github.com/raysan5) | -| 120 | [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | +| 118 | [audio_module_playing](audio/audio_module_playing.c) | audio_module_playing | ⭐️☆☆☆ | 1.5 | 3.5 | [Ray](https://github.com/raysan5) | +| 119 | [audio_music_stream](audio/audio_music_stream.c) | audio_music_stream | ⭐️☆☆☆ | 1.3 | **4.2** | [Ray](https://github.com/raysan5) | +| 120 | [audio_raw_stream](audio/audio_raw_stream.c) | audio_raw_stream | ⭐️⭐️⭐️☆ | 1.6 | **4.2** | [Ray](https://github.com/raysan5) | +| 121 | [audio_sound_loading](audio/audio_sound_loading.c) | audio_sound_loading | ⭐️☆☆☆ | 1.1 | 3.5 | [Ray](https://github.com/raysan5) | ### category: others diff --git a/examples/shaders/resources/shaders/glsl330/deferred_shading.fs b/examples/shaders/resources/shaders/glsl330/deferred_shading.fs new file mode 100644 index 000000000..c9c6a313f --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/deferred_shading.fs @@ -0,0 +1,55 @@ +#version 330 core +out vec4 finalColor; + +in vec2 texCoord; +in vec2 texCoord2; + +uniform sampler2D gPosition; +uniform sampler2D gNormal; +uniform sampler2D gAlbedoSpec; + +struct Light { + int enabled; + int type; // Unused in this demo. + vec3 position; + vec3 target; // Unused in this demo. + vec4 color; +}; + +const int NR_LIGHTS = 4; +uniform Light lights[NR_LIGHTS]; +uniform vec3 viewPosition; + +const float QUADRATIC = 0.032; +const float LINEAR = 0.09; + +void main() { + vec3 fragPosition = texture(gPosition, texCoord).rgb; + vec3 normal = texture(gNormal, texCoord).rgb; + vec3 albedo = texture(gAlbedoSpec, texCoord).rgb; + float specular = texture(gAlbedoSpec, texCoord).a; + + vec3 ambient = albedo * vec3(0.1f); + vec3 viewDirection = normalize(viewPosition - fragPosition); + + for(int i = 0; i < NR_LIGHTS; ++i) + { + if(lights[i].enabled == 0) continue; + vec3 lightDirection = lights[i].position - fragPosition; + vec3 diffuse = max(dot(normal, lightDirection), 0.0) * albedo * lights[i].color.xyz; + + vec3 halfwayDirection = normalize(lightDirection + viewDirection); + float spec = pow(max(dot(normal, halfwayDirection), 0.0), 32.0); + vec3 specular = specular * spec * lights[i].color.xyz; + + // Attenuation + float distance = length(lights[i].position - fragPosition); + float attenuation = 1.0 / (1.0 + LINEAR * distance + QUADRATIC * distance * distance); + diffuse *= attenuation; + specular *= attenuation; + ambient += diffuse + specular; + } + + finalColor = vec4(ambient, 1.0); +} + diff --git a/examples/shaders/resources/shaders/glsl330/deferred_shading.vs b/examples/shaders/resources/shaders/glsl330/deferred_shading.vs new file mode 100644 index 000000000..f2b1bd7c4 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/deferred_shading.vs @@ -0,0 +1,11 @@ +#version 330 core + +layout (location = 0) in vec3 vertexPosition; +layout (location = 1) in vec2 vertexTexCoord; + +out vec2 texCoord; + +void main() { + gl_Position = vec4(vertexPosition, 1.0); + texCoord = vertexTexCoord; +} diff --git a/examples/shaders/resources/shaders/glsl330/gbuffer.fs b/examples/shaders/resources/shaders/glsl330/gbuffer.fs new file mode 100644 index 000000000..c86e20a9e --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/gbuffer.fs @@ -0,0 +1,22 @@ +#version 330 core +layout (location = 0) out vec3 gPosition; +layout (location = 1) out vec3 gNormal; +layout (location = 2) out vec4 gAlbedoSpec; + +in vec3 fragPosition; +in vec2 fragTexCoord; +in vec3 fragNormal; + +uniform sampler2D diffuseTexture; +uniform sampler2D specularTexture; + +void main() { + // store the fragment position vector in the first gbuffer texture + gPosition = fragPosition; + // also store the per-fragment normals into the gbuffer + gNormal = normalize(fragNormal); + // and the diffuse per-fragment color + gAlbedoSpec.rgb = texture(diffuseTexture, fragTexCoord).rgb; + // store specular intensity in gAlbedoSpec's alpha component + gAlbedoSpec.a = texture(specularTexture, fragTexCoord).r; +} diff --git a/examples/shaders/resources/shaders/glsl330/gbuffer.vs b/examples/shaders/resources/shaders/glsl330/gbuffer.vs new file mode 100644 index 000000000..7d264ba64 --- /dev/null +++ b/examples/shaders/resources/shaders/glsl330/gbuffer.vs @@ -0,0 +1,24 @@ +#version 330 core +layout (location = 0) in vec3 vertexPosition; +layout (location = 1) in vec2 vertexTexCoord; +layout (location = 2) in vec3 vertexNormal; + +out vec3 fragPosition; +out vec2 fragTexCoord; +out vec3 fragNormal; + +uniform mat4 matModel; +uniform mat4 matView; +uniform mat4 matProjection; + +void main() +{ + vec4 worldPos = matModel * vec4(vertexPosition, 1.0); + fragPosition = worldPos.xyz; + fragTexCoord = vertexTexCoord; + + mat3 normalMatrix = transpose(inverse(mat3(matModel))); + fragNormal = normalMatrix * vertexNormal; + + gl_Position = matProjection * matView * worldPos; +} diff --git a/examples/shaders/shaders_deferred_render.c b/examples/shaders/shaders_deferred_render.c new file mode 100644 index 000000000..58d4b1dd9 --- /dev/null +++ b/examples/shaders/shaders_deferred_render.c @@ -0,0 +1,321 @@ +/******************************************************************************************* +* +* raylib [shaders] example - deferred rendering +* +* NOTE: This example requires raylib OpenGL 3.3 or ES 3 versions. +* +* Example originally created with raylib 4.5, last time updated with raylib 4.5 +* +* Example contributed by Justin Andreas Lacoste (@27justin) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2023 Justin Andreas Lacoste (@27justin) +* +********************************************************************************************/ + +#include +#include + +#include "raylib.h" +#include "rlgl.h" + +#include "raymath.h" + +#define RLIGHTS_IMPLEMENTATION +#include "rlights.h" + +#if defined(PLATFORM_DESKTOP) + #define GLSL_VERSION 330 +#else // PLATFORM_ANDROID, PLATFORM_WEB + #define GLSL_VERSION 100 +#endif + +typedef struct { + unsigned int framebuffer; + + unsigned int positionTexture; + unsigned int normalTexture; + unsigned int albedoSpecTexture; + + unsigned int depthRenderbuffer; +} GBuffer; + +int main(void) { + // Initialization + // ------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [shaders] example - deferred render"); + + Camera camera = { 0 }; + camera.position = (Vector3){ 5.0f, 4.0f, 5.0f }; // Camera position + camera.target = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera looking at point + camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target) + camera.fovy = 60.0f; // Camera field-of-view Y + camera.projection = CAMERA_PERSPECTIVE; // Camera projection type + + // Load plane model from a generated mesh + Model model = LoadModelFromMesh(GenMeshPlane(10.0f, 10.0f, 3, 3)); + Model cube = LoadModelFromMesh(GenMeshCube(2.0f, 2.0f, 2.0f)); + + // Load geometry buffer (G-buffer) shader and deferred shader + Shader gbufferShader = LoadShader("resources/shaders/glsl330/gbuffer.vs", + "resources/shaders/glsl330/gbuffer.fs"); + + Shader deferredShader = LoadShader("resources/shaders/glsl330/deferred_shading.vs", + "resources/shaders/glsl330/deferred_shading.fs"); + deferredShader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(deferredShader, "viewPosition"); + + // Initialize the G-buffer + GBuffer gBuffer = { 0 }; + gBuffer.framebuffer = rlLoadFramebuffer(screenWidth, screenHeight); + + if(!gBuffer.framebuffer) + { + TraceLog(LOG_WARNING, "Failed to create framebuffer"); + exit(1); + } + rlEnableFramebuffer(gBuffer.framebuffer); + + // Since we are storing position and normal data in these textures, + // we need to use a floating point format. + gBuffer.positionTexture = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32, 1); + + gBuffer.normalTexture = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32, 1); + // Albedo (diffuse color) and specular strength can be combined into one texture. + // The color in RGB, and the specular strength in the alpha channel. + gBuffer.albedoSpecTexture = rlLoadTexture(NULL, screenWidth, screenHeight, RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, 1); + + // Activate the draw buffers for our framebuffer + rlActiveDrawBuffers(3); + + // Now we attach our textures to the framebuffer. + rlFramebufferAttach(gBuffer.framebuffer, gBuffer.positionTexture, RL_ATTACHMENT_COLOR_CHANNEL0, RL_ATTACHMENT_TEXTURE2D, 0); + rlFramebufferAttach(gBuffer.framebuffer, gBuffer.normalTexture, RL_ATTACHMENT_COLOR_CHANNEL1, RL_ATTACHMENT_TEXTURE2D, 0); + rlFramebufferAttach(gBuffer.framebuffer, gBuffer.albedoSpecTexture, RL_ATTACHMENT_COLOR_CHANNEL2, RL_ATTACHMENT_TEXTURE2D, 0); + + // Finally we attach the depth buffer. + gBuffer.depthRenderbuffer = rlLoadTextureDepth(screenWidth, screenHeight, true); + rlFramebufferAttach(gBuffer.framebuffer, gBuffer.depthRenderbuffer, RL_ATTACHMENT_DEPTH, RL_ATTACHMENT_RENDERBUFFER, 0); + + // Make sure our framebuffer is complete. + // NOTE: rlFramebufferComplete() automatically unbinds the framebuffer, so we don't have + // to rlDisableFramebuffer() here. + if(rlFramebufferComplete(gBuffer.framebuffer) != true) + { + TraceLog(LOG_WARNING, "Framebuffer is not complete"); + exit(1); + } + + // Now we initialize the sampler2D uniform's in the deferred shader. + // We do this by setting the uniform's value to the color channel slot we earlier + // bound our textures to. + rlEnableShader(deferredShader.id); + + rlSetUniformSampler(rlGetLocationUniform(deferredShader.id, "gPosition"), 0); + rlSetUniformSampler(rlGetLocationUniform(deferredShader.id, "gNormal"), 1); + rlSetUniformSampler(rlGetLocationUniform(deferredShader.id, "gAlbedoSpec"), 2); + + rlDisableShader(); + + // Assign out lighting shader to model + model.materials[0].shader = gbufferShader; + cube.materials[0].shader = gbufferShader; + + // Create lights + //-------------------------------------------------------------------------------------- + Light lights[MAX_LIGHTS] = { 0 }; + lights[0] = CreateLight(LIGHT_POINT, (Vector3){ -2, 1, -2 }, Vector3Zero(), YELLOW, deferredShader); + lights[1] = CreateLight(LIGHT_POINT, (Vector3){ 2, 1, 2 }, Vector3Zero(), RED, deferredShader); + lights[2] = CreateLight(LIGHT_POINT, (Vector3){ -2, 1, 2 }, Vector3Zero(), GREEN, deferredShader); + lights[3] = CreateLight(LIGHT_POINT, (Vector3){ 2, 1, -2 }, Vector3Zero(), BLUE, deferredShader); + + const int MAX_CUBES = 30; + const float CUBE_SCALE = 0.25; + Vector3 cubePositions[MAX_CUBES]; + float cubeRotations[MAX_CUBES]; + for(int i = 0; i < MAX_CUBES; i++) + { + cubePositions[i] = (Vector3) { + .x = (float)(rand() % 10) - 5, + .y = (float)(rand() % 5), + .z = (float)(rand() % 10) - 5, + }; + cubeRotations[i] = (float)(rand() % 360); + } + + enum { + POSITION, + NORMAL, + ALBEDO, + DEFERRED_SHADING + } activeTexture = DEFERRED_SHADING; + + rlEnableDepthTest(); + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //--------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) + { + // Update + //---------------------------------------------------------------------------------- + UpdateCamera(&camera, CAMERA_ORBITAL); + + // Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f }) + float cameraPos[3] = { camera.position.x, camera.position.y, camera.position.z }; + SetShaderValue(deferredShader, deferredShader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3); + + // Check key inputs to enable/disable lights + if (IsKeyPressed(KEY_Y)) { lights[0].enabled = !lights[0].enabled; } + if (IsKeyPressed(KEY_R)) { lights[1].enabled = !lights[1].enabled; } + if (IsKeyPressed(KEY_G)) { lights[2].enabled = !lights[2].enabled; } + if (IsKeyPressed(KEY_B)) { lights[3].enabled = !lights[3].enabled; } + + // Check key inputs to switch between G-buffer textures + if(IsKeyPressed(KEY_ONE)) activeTexture = POSITION; + if(IsKeyPressed(KEY_TWO)) activeTexture = NORMAL; + if(IsKeyPressed(KEY_THREE)) activeTexture = ALBEDO; + if(IsKeyPressed(KEY_FOUR)) activeTexture = DEFERRED_SHADING; + + + // Update light values (actually, only enable/disable them) + for (int i = 0; i < MAX_LIGHTS; i++) UpdateLightValues(deferredShader, lights[i]); + //---------------------------------------------------------------------------------- + + // Draw + // --------------------------------------------------------------------------------- + BeginDrawing(); + // Draw to the geometry buffer by first activating it. + rlEnableFramebuffer(gBuffer.framebuffer); + rlClearScreenBuffers(); // Clear color & depth buffer + + rlDisableColorBlend(); + BeginMode3D(camera); + // NOTE: + // We have to use rlEnableShader here. `BeginShaderMode` or thus `rlSetShader` + // will not work, as they won't immediately load the shader program. + rlEnableShader(gbufferShader.id); + // When drawing a model here, make sure that the material's shaders + // are set to the gbuffer shader! + DrawModel(model, Vector3Zero(), 1.0f, WHITE); + DrawModel(cube, (Vector3) { 0.0, 1.0f, 0.0 }, 1.0f, WHITE); + + for(int i = 0; i < MAX_CUBES; i++) + { + Vector3 position = cubePositions[i]; + + DrawModelEx(cube, position, (Vector3) { 1, 1, 1 }, cubeRotations[i], (Vector3) { CUBE_SCALE, CUBE_SCALE, CUBE_SCALE }, WHITE); + } + + rlDisableShader(); + EndMode3D(); + rlEnableColorBlend(); + + // Go back to the default framebuffer (0) and draw our deferred shading. + rlDisableFramebuffer(); + rlClearScreenBuffers(); // Clear color & depth buffer + + switch(activeTexture) + { + case DEFERRED_SHADING: + BeginMode3D(camera); + rlDisableColorBlend(); + rlEnableShader(deferredShader.id); + // Activate our g-buffer textures + // These will now be bound to the sampler2D uniforms `gPosition`, `gNormal`, + // and `gAlbedoSpec` + rlActiveTextureSlot(0); + rlEnableTexture(gBuffer.positionTexture); + rlActiveTextureSlot(1); + rlEnableTexture(gBuffer.normalTexture); + rlActiveTextureSlot(2); + rlEnableTexture(gBuffer.albedoSpecTexture); + + // Finally, we draw a fullscreen quad to our default framebuffer + // This will now be shaded using our deferred shader + rlLoadDrawQuad(); + rlDisableShader(); + rlEnableColorBlend(); + EndMode3D(); + + // As a last step, we now copy over the depth buffer from our g-buffer to the + // default framebuffer. + glBindFramebuffer(GL_READ_FRAMEBUFFER, gBuffer.framebuffer); + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + glBlitFramebuffer(0, 0, screenWidth, screenHeight, 0, 0, screenWidth, screenHeight, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + rlDisableFramebuffer(); + + // Since our shader is now done and disabled, we can draw our lights in default + // forward rendering + BeginMode3D(camera); + rlEnableShader(rlGetShaderIdDefault()); + for(int i = 0; i < MAX_LIGHTS; i++) + { + if(lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, lights[i].color); + else DrawSphereWires(lights[i].position, 0.2f, 8, 8, ColorAlpha(lights[i].color, 0.3f)); + } + rlDisableShader(); + EndMode3D(); + DrawText("FINAL RESULT", 10, screenHeight - 30, 20, DARKGREEN); + break; + case POSITION: + DrawTextureRec((Texture2D) { + .id = gBuffer.positionTexture, + .width = screenWidth, + .height = screenHeight, + }, (Rectangle) { 0, 0, screenWidth, -screenHeight }, Vector2Zero(), RAYWHITE); + DrawText("POSITION TEXTURE", 10, screenHeight - 30, 20, DARKGREEN); + break; + case NORMAL: + DrawTextureRec((Texture2D) { + .id = gBuffer.normalTexture, + .width = screenWidth, + .height = screenHeight, + }, (Rectangle) { 0, 0, screenWidth, -screenHeight }, Vector2Zero(), RAYWHITE); + DrawText("NORMAL TEXTURE", 10, screenHeight - 30, 20, DARKGREEN); + break; + + case ALBEDO: + DrawTextureRec((Texture2D) { + .id = gBuffer.albedoSpecTexture, + .width = screenWidth, + .height = screenHeight, + }, (Rectangle) { 0, 0, screenWidth, -screenHeight }, Vector2Zero(), RAYWHITE); + DrawText("ALBEDO TEXTURE", 10, screenHeight - 30, 20, DARKGREEN); + break; + } + + DrawFPS(10, 10); + + DrawText("Use keys [Y][R][G][B] to toggle lights", 10, 40, 20, DARKGRAY); + DrawText("Use keys [1]-[4] to switch between G-buffer textures", 10, 70, 20, DARKGRAY); + EndDrawing(); + // ----------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + UnloadModel(model); // Unload the models + UnloadModel(cube); + + UnloadShader(deferredShader); // Unload shaders + UnloadShader(gbufferShader); + + // Unload geometry buffer and all attached textures + rlUnloadFramebuffer(gBuffer.framebuffer); + rlUnloadTexture(gBuffer.positionTexture); + rlUnloadTexture(gBuffer.normalTexture); + rlUnloadTexture(gBuffer.albedoSpecTexture); + rlUnloadTexture(gBuffer.depthRenderbuffer); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} + diff --git a/examples/shaders/shaders_deferred_render.png b/examples/shaders/shaders_deferred_render.png new file mode 100644 index 0000000000000000000000000000000000000000..44129f0ff0ecb67f96e04d841e6e23c220f21dc8 GIT binary patch literal 90692 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�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 Date: Tue, 31 Oct 2023 20:15:45 +0100 Subject: [PATCH 0459/1350] REDESIGNED: `LoadOBJ()`, fix #3398 - Now triangulated meshes are properly supported - Simplified code to consider issue situation - Removed mesh split per material, just define separate mesh if multiple materials are required --- src/rmodels.c | 155 +++++++++++++++++++++----------------------------- 1 file changed, 65 insertions(+), 90 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 68c2d75bc..0fc305ddc 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3931,9 +3931,10 @@ static Model LoadOBJ(const char *fileName) if (fileText != NULL) { unsigned int dataSize = (unsigned int)strlen(fileText); + char currentDir[1024] = { 0 }; - strcpy(currentDir, GetWorkingDirectory()); - const char *workingDir = GetDirectoryPath(fileName); + strcpy(currentDir, GetWorkingDirectory()); // Save current working directory + const char *workingDir = GetDirectoryPath(fileName); // Switch to OBJ directory for material path correctness if (CHDIR(workingDir) != 0) { TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", workingDir); @@ -3945,117 +3946,91 @@ static Model LoadOBJ(const char *fileName) if (ret != TINYOBJ_SUCCESS) TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to load OBJ data", fileName); else TRACELOG(LOG_INFO, "MODEL: [%s] OBJ data loaded successfully: %i meshes/%i materials", fileName, meshCount, materialCount); - model.meshCount = materialCount; + // WARNING: We are not splitting meshes by materials (previous implementation) + // Depending on the provided OBJ that was not the best option and it just crashed + // so, implementation was simplified to prioritize parsed meshes + model.meshCount = meshCount; - // Init model materials array - if (materialCount > 0) + // Set number of materials available + // NOTE: There could be more materials available than meshes but it will be resolved at + // model.meshMaterial, just assigning the right material to corresponding mesh + model.materialCount = materialCount; + if (model.materialCount == 0) { - model.materialCount = materialCount; - model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); - TRACELOG(LOG_INFO, "MODEL: model has %i material meshes", materialCount); - } - else - { - model.meshCount = 1; - TRACELOG(LOG_INFO, "MODEL: No materials, putting all meshes in a default material"); + model.materialCount = 1; + TRACELOG(LOG_INFO, "MODEL: No materials provided, setting one default material for all meshes"); } + // Init model meshes and materials model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); - model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); + model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); // Material index assigned to each mesh + model.materials = (Material *)RL_CALLOC(model.materialCount, sizeof(Material)); - // Count the faces for each material - int *matFaces = RL_CALLOC(model.meshCount, sizeof(int)); - - // if no materials are present use all faces on one mesh - if (materialCount > 0) + // Process each provided mesh + for (int i = 0; i < model.meshCount; i++) { - for (unsigned int fi = 0; fi < attrib.num_faces; fi++) + // WARNING: We need to calculate the mesh triangles manually using meshes[i].face_offset + // because in case of triangulated quads, meshes[i].length actually report quads, + // despite the triangulation that is efectively considered on attrib.num_faces + unsigned int tris = 0; + if (i == model.meshCount - 1) tris = attrib.num_faces - meshes[i].face_offset; + else tris = meshes[i + 1].face_offset; + + model.meshes[i].vertexCount = tris*3; + model.meshes[i].triangleCount = tris; // Face count (triangulated) + model.meshes[i].vertices = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); + model.meshes[i].texcoords = (float *)RL_CALLOC(model.meshes[i].vertexCount*2, sizeof(float)); + model.meshes[i].normals = (float *)RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); + model.meshMaterial[i] = 0; // By default, assign material 0 to each mesh + + // Process all mesh faces + for (unsigned int face = 0, f = meshes[i].face_offset, v = 0, vt = 0, vn = 0; face < tris; face++, f++, v += 3, vt += 2, vn += 3) { - //tinyobj_vertex_index_t face = attrib.faces[fi]; - int idx = attrib.material_ids[fi]; - matFaces[idx]++; - } + // Get indices for the face + tinyobj_vertex_index_t idx0 = attrib.faces[f*3 + 0]; + tinyobj_vertex_index_t idx1 = attrib.faces[f*3 + 1]; + tinyobj_vertex_index_t idx2 = attrib.faces[f*3 + 2]; - } - else - { - matFaces[0] = attrib.num_faces; - } + // Fill vertices buffer (float) using vertex index of the face + for (int n = 0; n < 3; n++) { model.meshes[i].vertices[v*3 + n] = attrib.vertices[idx0.v_idx*3 + n]; } + for (int n = 0; n < 3; n++) { model.meshes[i].vertices[(v + 1)*3 + n] = attrib.vertices[idx1.v_idx*3 + n]; } + for (int n = 0; n < 3; n++) { model.meshes[i].vertices[(v + 2)*3 + n] = attrib.vertices[idx2.v_idx*3 + n]; } - //-------------------------------------- - // Create the material meshes + if (attrib.num_texcoords > 0) + { + // Fill texcoords buffer (float) using vertex index of the face + // NOTE: Y-coordinate must be flipped upside-down + model.meshes[i].texcoords[vt*2 + 0] = attrib.texcoords[idx0.vt_idx*2 + 0]; + model.meshes[i].texcoords[vt*2 + 1] = 1.0f - attrib.texcoords[idx0.vt_idx*2 + 1]; - // Running counts/indexes for each material mesh as we are - // building them at the same time - int *vCount = RL_CALLOC(model.meshCount, sizeof(int)); - int *vtCount = RL_CALLOC(model.meshCount, sizeof(int)); - int *vnCount = RL_CALLOC(model.meshCount, sizeof(int)); - int *faceCount = RL_CALLOC(model.meshCount, sizeof(int)); + model.meshes[i].texcoords[(vt + 1)*2 + 0] = attrib.texcoords[idx1.vt_idx*2 + 0]; + model.meshes[i].texcoords[(vt + 1)*2 + 1] = 1.0f - attrib.texcoords[idx1.vt_idx*2 + 1]; - // Allocate space for each of the material meshes - for (int mi = 0; mi < model.meshCount; mi++) - { - model.meshes[mi].vertexCount = matFaces[mi]*3; - model.meshes[mi].triangleCount = matFaces[mi]; - model.meshes[mi].vertices = (float *)RL_CALLOC(model.meshes[mi].vertexCount*3, sizeof(float)); - model.meshes[mi].texcoords = (float *)RL_CALLOC(model.meshes[mi].vertexCount*2, sizeof(float)); - model.meshes[mi].normals = (float *)RL_CALLOC(model.meshes[mi].vertexCount*3, sizeof(float)); - model.meshMaterial[mi] = mi; - } + model.meshes[i].texcoords[(vt + 2)*2 + 0] = attrib.texcoords[idx2.vt_idx*2 + 0]; + model.meshes[i].texcoords[(vt + 2)*2 + 1] = 1.0f - attrib.texcoords[idx2.vt_idx*2 + 1]; + } - // Scan through the combined sub meshes and pick out each material mesh - for (unsigned int af = 0; af < attrib.num_faces; af++) - { - int mm = attrib.material_ids[af]; // mesh material for this face - if (mm == -1) { mm = 0; } // no material object.. - - // Get indices for the face - tinyobj_vertex_index_t idx0 = attrib.faces[3*af + 0]; - tinyobj_vertex_index_t idx1 = attrib.faces[3*af + 1]; - tinyobj_vertex_index_t idx2 = attrib.faces[3*af + 2]; - - // Fill vertices buffer (float) using vertex index of the face - for (int v = 0; v < 3; v++) { model.meshes[mm].vertices[vCount[mm] + v] = attrib.vertices[idx0.v_idx*3 + v]; } vCount[mm] +=3; - for (int v = 0; v < 3; v++) { model.meshes[mm].vertices[vCount[mm] + v] = attrib.vertices[idx1.v_idx*3 + v]; } vCount[mm] +=3; - for (int v = 0; v < 3; v++) { model.meshes[mm].vertices[vCount[mm] + v] = attrib.vertices[idx2.v_idx*3 + v]; } vCount[mm] +=3; - - if (attrib.num_texcoords > 0) - { - // Fill texcoords buffer (float) using vertex index of the face - // NOTE: Y-coordinate must be flipped upside-down to account for - // raylib's upside down textures... - model.meshes[mm].texcoords[vtCount[mm] + 0] = attrib.texcoords[idx0.vt_idx*2 + 0]; - model.meshes[mm].texcoords[vtCount[mm] + 1] = 1.0f - attrib.texcoords[idx0.vt_idx*2 + 1]; vtCount[mm] += 2; - model.meshes[mm].texcoords[vtCount[mm] + 0] = attrib.texcoords[idx1.vt_idx*2 + 0]; - model.meshes[mm].texcoords[vtCount[mm] + 1] = 1.0f - attrib.texcoords[idx1.vt_idx*2 + 1]; vtCount[mm] += 2; - model.meshes[mm].texcoords[vtCount[mm] + 0] = attrib.texcoords[idx2.vt_idx*2 + 0]; - model.meshes[mm].texcoords[vtCount[mm] + 1] = 1.0f - attrib.texcoords[idx2.vt_idx*2 + 1]; vtCount[mm] += 2; - } - - if (attrib.num_normals > 0) - { - // Fill normals buffer (float) using vertex index of the face - for (int v = 0; v < 3; v++) { model.meshes[mm].normals[vnCount[mm] + v] = attrib.normals[idx0.vn_idx*3 + v]; } vnCount[mm] +=3; - for (int v = 0; v < 3; v++) { model.meshes[mm].normals[vnCount[mm] + v] = attrib.normals[idx1.vn_idx*3 + v]; } vnCount[mm] +=3; - for (int v = 0; v < 3; v++) { model.meshes[mm].normals[vnCount[mm] + v] = attrib.normals[idx2.vn_idx*3 + v]; } vnCount[mm] +=3; + if (attrib.num_normals > 0) + { + // Fill normals buffer (float) using vertex index of the face + for (int n = 0; n < 3; n++) { model.meshes[i].normals[vn*3 + n] = attrib.normals[idx0.vn_idx*3 + n]; } + for (int n = 0; n < 3; n++) { model.meshes[i].normals[(vn + 1)*3 + n] = attrib.normals[idx1.vn_idx*3 + n]; } + for (int n = 0; n < 3; n++) { model.meshes[i].normals[(vn + 2)*3 + n] = attrib.normals[idx2.vn_idx*3 + n]; } + } } } // Init model materials - ProcessMaterialsOBJ(model.materials, materials, materialCount); + if (materialCount > 0) ProcessMaterialsOBJ(model.materials, materials, materialCount); + else model.materials[0] = LoadMaterialDefault(); // Set default material for the mesh tinyobj_attrib_free(&attrib); - tinyobj_shapes_free(meshes, meshCount); + tinyobj_shapes_free(meshes, model.meshCount); tinyobj_materials_free(materials, materialCount); UnloadFileText(fileText); - RL_FREE(matFaces); - RL_FREE(vCount); - RL_FREE(vtCount); - RL_FREE(vnCount); - RL_FREE(faceCount); - + // Restore current working directory if (CHDIR(currentDir) != 0) { TRACELOG(LOG_WARNING, "MODEL: [%s] Failed to change working directory", currentDir); From 15142a30f5e371b4a8246d9a8671ae260bb4c78f Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 20:20:11 +0100 Subject: [PATCH 0460/1350] Update rmodels.c --- src/rmodels.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index 0fc305ddc..3cae43d12 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3984,7 +3984,7 @@ static Model LoadOBJ(const char *fileName) model.meshMaterial[i] = 0; // By default, assign material 0 to each mesh // Process all mesh faces - for (unsigned int face = 0, f = meshes[i].face_offset, v = 0, vt = 0, vn = 0; face < tris; face++, f++, v += 3, vt += 2, vn += 3) + for (unsigned int face = 0, f = meshes[i].face_offset, v = 0, vt = 0, vn = 0; face < tris; face++, f++, v += 3, vt += 3, vn += 3) { // Get indices for the face tinyobj_vertex_index_t idx0 = attrib.faces[f*3 + 0]; From 1407f6eb4673dbf480ce9c7c33cfe54703bad599 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 20:46:06 +0100 Subject: [PATCH 0461/1350] ADDED: `rlBlitFramebuffer()`, required for deferred render --- src/rlgl.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/rlgl.h b/src/rlgl.h index fcb8feeb6..da6573d62 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -617,6 +617,7 @@ RLAPI void rlDisableShader(void); // Disable shader progra RLAPI void rlEnableFramebuffer(unsigned int id); // Enable render texture (fbo) RLAPI void rlDisableFramebuffer(void); // Disable render texture (fbo), return to default framebuffer RLAPI void rlActiveDrawBuffers(int count); // Activate multiple draw color buffers +RLAPI void rlBlitFramebuffer(int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY, int dstWidth, int dstHeight, int bufferMask); // Blit active framebuffer to main framebuffer // General render state RLAPI void rlEnableColorBlend(void); // Enable color blending @@ -1713,6 +1714,14 @@ void rlDisableFramebuffer(void) #endif } +// Blit active framebuffer to main framebuffer +void rlBlitFramebuffer(int srcX, int srcY, int srcWidth, int srcHeight, int dstX, int dstY, int dstWidth, int dstHeight, int bufferMask) +{ +#if (defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3)) && defined(RLGL_RENDER_TEXTURES_HINT) + glBlitFramebuffer(srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight, bufferMask, GL_NEAREST); +#endif +} + // Activate multiple draw color buffers // NOTE: One color buffer is always active by default void rlActiveDrawBuffers(int count) From aca854ccbf1fe9fac04f2067ffc90960e621e927 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 20:46:10 +0100 Subject: [PATCH 0462/1350] Update shaders_deferred_render.c --- examples/shaders/shaders_deferred_render.c | 117 ++++++++++++--------- 1 file changed, 65 insertions(+), 52 deletions(-) diff --git a/examples/shaders/shaders_deferred_render.c b/examples/shaders/shaders_deferred_render.c index 58d4b1dd9..5bb4db1d2 100644 --- a/examples/shaders/shaders_deferred_render.c +++ b/examples/shaders/shaders_deferred_render.c @@ -2,7 +2,7 @@ * * raylib [shaders] example - deferred rendering * -* NOTE: This example requires raylib OpenGL 3.3 or ES 3 versions. +* NOTE: This example requires raylib OpenGL 3.3 or OpenGL ES 3.0 * * Example originally created with raylib 4.5, last time updated with raylib 4.5 * @@ -15,12 +15,9 @@ * ********************************************************************************************/ -#include -#include - #include "raylib.h" -#include "rlgl.h" +#include "rlgl.h" #include "raymath.h" #define RLIGHTS_IMPLEMENTATION @@ -32,7 +29,9 @@ #define GLSL_VERSION 100 #endif -typedef struct { +#include // Required for: NULL + +typedef struct GBuffer { unsigned int framebuffer; unsigned int positionTexture; @@ -42,7 +41,18 @@ typedef struct { unsigned int depthRenderbuffer; } GBuffer; -int main(void) { +typedef enum { + DEFERRED_POSITION, + DEFERRED_NORMAL, + DEFERRED_ALBEDO, + DEFERRED_SHADING +} DeferredMode; + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ // Initialization // ------------------------------------------------------------------------------------- const int screenWidth = 800; @@ -73,11 +83,12 @@ int main(void) { GBuffer gBuffer = { 0 }; gBuffer.framebuffer = rlLoadFramebuffer(screenWidth, screenHeight); - if(!gBuffer.framebuffer) + if (!gBuffer.framebuffer) { TraceLog(LOG_WARNING, "Failed to create framebuffer"); exit(1); } + rlEnableFramebuffer(gBuffer.framebuffer); // Since we are storing position and normal data in these textures, @@ -104,7 +115,7 @@ int main(void) { // Make sure our framebuffer is complete. // NOTE: rlFramebufferComplete() automatically unbinds the framebuffer, so we don't have // to rlDisableFramebuffer() here. - if(rlFramebufferComplete(gBuffer.framebuffer) != true) + if (!rlFramebufferComplete(gBuffer.framebuffer)) { TraceLog(LOG_WARNING, "Framebuffer is not complete"); exit(1); @@ -137,22 +148,18 @@ int main(void) { const float CUBE_SCALE = 0.25; Vector3 cubePositions[MAX_CUBES]; float cubeRotations[MAX_CUBES]; + for(int i = 0; i < MAX_CUBES; i++) { - cubePositions[i] = (Vector3) { - .x = (float)(rand() % 10) - 5, - .y = (float)(rand() % 5), - .z = (float)(rand() % 10) - 5, + cubePositions[i] = (Vector3){ + .x = (float)(rand()%10) - 5, + .y = (float)(rand()%5), + .z = (float)(rand()%10) - 5, }; - cubeRotations[i] = (float)(rand() % 360); + cubeRotations[i] = (float)(rand()%360); } - enum { - POSITION, - NORMAL, - ALBEDO, - DEFERRED_SHADING - } activeTexture = DEFERRED_SHADING; + DeferredMode mode = DEFERRED_SHADING; rlEnableDepthTest(); @@ -177,12 +184,11 @@ int main(void) { if (IsKeyPressed(KEY_B)) { lights[3].enabled = !lights[3].enabled; } // Check key inputs to switch between G-buffer textures - if(IsKeyPressed(KEY_ONE)) activeTexture = POSITION; - if(IsKeyPressed(KEY_TWO)) activeTexture = NORMAL; - if(IsKeyPressed(KEY_THREE)) activeTexture = ALBEDO; - if(IsKeyPressed(KEY_FOUR)) activeTexture = DEFERRED_SHADING; + if (IsKeyPressed(KEY_ONE)) mode = DEFERRED_POSITION; + if (IsKeyPressed(KEY_TWO)) mode = DEFERRED_NORMAL; + if (IsKeyPressed(KEY_THREE)) mode = DEFERRED_ALBEDO; + if (IsKeyPressed(KEY_FOUR)) mode = DEFERRED_SHADING; - // Update light values (actually, only enable/disable them) for (int i = 0; i < MAX_LIGHTS; i++) UpdateLightValues(deferredShader, lights[i]); //---------------------------------------------------------------------------------- @@ -190,14 +196,16 @@ int main(void) { // Draw // --------------------------------------------------------------------------------- BeginDrawing(); - // Draw to the geometry buffer by first activating it. + + ClearBackground(RAYWHITE); + + // Draw to the geometry buffer by first activating it rlEnableFramebuffer(gBuffer.framebuffer); - rlClearScreenBuffers(); // Clear color & depth buffer - + rlClearScreenBuffers(); // Clear color and depth buffer + rlDisableColorBlend(); BeginMode3D(camera); - // NOTE: - // We have to use rlEnableShader here. `BeginShaderMode` or thus `rlSetShader` + // NOTE: We have to use rlEnableShader here. `BeginShaderMode` or thus `rlSetShader` // will not work, as they won't immediately load the shader program. rlEnableShader(gbufferShader.id); // When drawing a model here, make sure that the material's shaders @@ -205,10 +213,9 @@ int main(void) { DrawModel(model, Vector3Zero(), 1.0f, WHITE); DrawModel(cube, (Vector3) { 0.0, 1.0f, 0.0 }, 1.0f, WHITE); - for(int i = 0; i < MAX_CUBES; i++) + for (int i = 0; i < MAX_CUBES; i++) { Vector3 position = cubePositions[i]; - DrawModelEx(cube, position, (Vector3) { 1, 1, 1 }, cubeRotations[i], (Vector3) { CUBE_SCALE, CUBE_SCALE, CUBE_SCALE }, WHITE); } @@ -220,9 +227,10 @@ int main(void) { rlDisableFramebuffer(); rlClearScreenBuffers(); // Clear color & depth buffer - switch(activeTexture) + switch (mode) { case DEFERRED_SHADING: + { BeginMode3D(camera); rlDisableColorBlend(); rlEnableShader(deferredShader.id); @@ -243,11 +251,10 @@ int main(void) { rlEnableColorBlend(); EndMode3D(); - // As a last step, we now copy over the depth buffer from our g-buffer to the - // default framebuffer. - glBindFramebuffer(GL_READ_FRAMEBUFFER, gBuffer.framebuffer); - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - glBlitFramebuffer(0, 0, screenWidth, screenHeight, 0, 0, screenWidth, screenHeight, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + // As a last step, we now copy over the depth buffer from our g-buffer to the default framebuffer. + rlEnableFramebuffer(gBuffer.framebuffer); //glBindFramebuffer(GL_READ_FRAMEBUFFER, gBuffer.framebuffer); + rlEnableFramebuffer(0); //glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); + rlBlitFramebuffer(0, 0, screenWidth, screenHeight, 0, 0, screenWidth, screenHeight, 0x00000100); // GL_DEPTH_BUFFER_BIT rlDisableFramebuffer(); // Since our shader is now done and disabled, we can draw our lights in default @@ -256,44 +263,50 @@ int main(void) { rlEnableShader(rlGetShaderIdDefault()); for(int i = 0; i < MAX_LIGHTS; i++) { - if(lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, lights[i].color); + if (lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, lights[i].color); else DrawSphereWires(lights[i].position, 0.2f, 8, 8, ColorAlpha(lights[i].color, 0.3f)); } rlDisableShader(); EndMode3D(); DrawText("FINAL RESULT", 10, screenHeight - 30, 20, DARKGREEN); - break; - case POSITION: - DrawTextureRec((Texture2D) { + } break; + + case DEFERRED_POSITION: + { + DrawTextureRec((Texture2D){ .id = gBuffer.positionTexture, .width = screenWidth, .height = screenHeight, }, (Rectangle) { 0, 0, screenWidth, -screenHeight }, Vector2Zero(), RAYWHITE); DrawText("POSITION TEXTURE", 10, screenHeight - 30, 20, DARKGREEN); - break; - case NORMAL: - DrawTextureRec((Texture2D) { + } break; + + case DEFERRED_NORMAL: + { + DrawTextureRec((Texture2D){ .id = gBuffer.normalTexture, .width = screenWidth, .height = screenHeight, }, (Rectangle) { 0, 0, screenWidth, -screenHeight }, Vector2Zero(), RAYWHITE); DrawText("NORMAL TEXTURE", 10, screenHeight - 30, 20, DARKGREEN); - break; + } break; - case ALBEDO: - DrawTextureRec((Texture2D) { + case DEFERRED_ALBEDO: + { + DrawTextureRec((Texture2D){ .id = gBuffer.albedoSpecTexture, .width = screenWidth, .height = screenHeight, }, (Rectangle) { 0, 0, screenWidth, -screenHeight }, Vector2Zero(), RAYWHITE); DrawText("ALBEDO TEXTURE", 10, screenHeight - 30, 20, DARKGREEN); - break; + } break; } - DrawFPS(10, 10); + DrawText("Toggle lights keys: [Y][R][G][B]", 10, 40, 20, DARKGRAY); + DrawText("Switch G-buffer textures: [1][2][3][4]", 10, 70, 20, DARKGRAY); - DrawText("Use keys [Y][R][G][B] to toggle lights", 10, 40, 20, DARKGRAY); - DrawText("Use keys [1]-[4] to switch between G-buffer textures", 10, 70, 20, DARKGRAY); + DrawFPS(10, 10); + EndDrawing(); // ----------------------------------------------------------------------------- } From df0f7ba61e6a89e493d8b6313001ecf6a7231e5a Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 31 Oct 2023 20:59:08 +0100 Subject: [PATCH 0463/1350] Update shaders_deferred_render.c --- examples/shaders/shaders_deferred_render.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/shaders/shaders_deferred_render.c b/examples/shaders/shaders_deferred_render.c index 5bb4db1d2..21d2af346 100644 --- a/examples/shaders/shaders_deferred_render.c +++ b/examples/shaders/shaders_deferred_render.c @@ -31,6 +31,8 @@ #include // Required for: NULL +#define MAX_CUBES 30 + typedef struct GBuffer { unsigned int framebuffer; @@ -144,18 +146,18 @@ int main(void) lights[2] = CreateLight(LIGHT_POINT, (Vector3){ -2, 1, 2 }, Vector3Zero(), GREEN, deferredShader); lights[3] = CreateLight(LIGHT_POINT, (Vector3){ 2, 1, -2 }, Vector3Zero(), BLUE, deferredShader); - const int MAX_CUBES = 30; const float CUBE_SCALE = 0.25; - Vector3 cubePositions[MAX_CUBES]; - float cubeRotations[MAX_CUBES]; + Vector3 cubePositions[MAX_CUBES] = { 0 }; + float cubeRotations[MAX_CUBES] = { 0 }; - for(int i = 0; i < MAX_CUBES; i++) + for (int i = 0; i < MAX_CUBES; i++) { cubePositions[i] = (Vector3){ .x = (float)(rand()%10) - 5, .y = (float)(rand()%5), .z = (float)(rand()%10) - 5, }; + cubeRotations[i] = (float)(rand()%360); } From d4c0d3bebe9e1f7bdaa197b77fc868aca683ad35 Mon Sep 17 00:00:00 2001 From: Techuuu <135726634+Techuuu@users.noreply.github.com> Date: Wed, 1 Nov 2023 16:45:47 +0530 Subject: [PATCH 0464/1350] Update README.md (#3495) * Update README.md Fixed docs and improved. * Update README.md Done fixed it! * Update README.md Fixed! --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bff6980b0..fadc3b659 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ features basic example -------------- -This is a basic raylib example, it creates a window and it draws the text `"Congrats! You created your first window!"` in the middle of the screen. Check this example [running live on web here](https://www.raylib.com/examples/core/loader.html?name=core_basic_window). +This is a basic raylib example, it creates a window and draws the text `"Congrats! You created your first window!"` in the middle of the screen. Check this example [running live on web here](https://www.raylib.com/examples/core/loader.html?name=core_basic_window). ```c #include "raylib.h" From 38205d67da4cf1216bbc4824e4c3af25e51792e0 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Wed, 1 Nov 2023 04:16:14 -0700 Subject: [PATCH 0465/1350] Remove unused structures from lighting fragment shaders (#3497) --- examples/shaders/resources/shaders/glsl100/lighting.fs | 6 ------ examples/shaders/resources/shaders/glsl120/lighting.fs | 6 ------ examples/shaders/resources/shaders/glsl330/lighting.fs | 6 ------ 3 files changed, 18 deletions(-) diff --git a/examples/shaders/resources/shaders/glsl100/lighting.fs b/examples/shaders/resources/shaders/glsl100/lighting.fs index 736716198..73531a8bb 100644 --- a/examples/shaders/resources/shaders/glsl100/lighting.fs +++ b/examples/shaders/resources/shaders/glsl100/lighting.fs @@ -18,12 +18,6 @@ uniform vec4 colDiffuse; #define LIGHT_DIRECTIONAL 0 #define LIGHT_POINT 1 -struct MaterialProperty { - vec3 color; - int useSampler; - sampler2D sampler; -}; - struct Light { int enabled; int type; diff --git a/examples/shaders/resources/shaders/glsl120/lighting.fs b/examples/shaders/resources/shaders/glsl120/lighting.fs index d9cfb4472..8dda5a549 100644 --- a/examples/shaders/resources/shaders/glsl120/lighting.fs +++ b/examples/shaders/resources/shaders/glsl120/lighting.fs @@ -16,12 +16,6 @@ uniform vec4 colDiffuse; #define LIGHT_DIRECTIONAL 0 #define LIGHT_POINT 1 -struct MaterialProperty { - vec3 color; - int useSampler; - sampler2D sampler; -}; - struct Light { int enabled; int type; diff --git a/examples/shaders/resources/shaders/glsl330/lighting.fs b/examples/shaders/resources/shaders/glsl330/lighting.fs index 58845c86b..d1f3140a1 100644 --- a/examples/shaders/resources/shaders/glsl330/lighting.fs +++ b/examples/shaders/resources/shaders/glsl330/lighting.fs @@ -19,12 +19,6 @@ out vec4 finalColor; #define LIGHT_DIRECTIONAL 0 #define LIGHT_POINT 1 -struct MaterialProperty { - vec3 color; - int useSampler; - sampler2D sampler; -}; - struct Light { int enabled; int type; From ba21b8d2744b39dadc6b17b9091a30cc5e00f0b8 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 1 Nov 2023 08:20:33 -0300 Subject: [PATCH 0466/1350] Moves keymapUS[] and fixes GetCharPressed for DRM (#3498) --- src/platforms/rcore_drm.c | 66 ++++++++++++++++++++++++++------------- 1 file changed, 45 insertions(+), 21 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index 632686767..c6307773f 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -137,6 +137,39 @@ extern CoreData CORE; // Global CORE state context static PlatformData platform = { 0 }; // Platform specific data +//---------------------------------------------------------------------------------- +// Local Variables Definition +//---------------------------------------------------------------------------------- +// Scancode to keycode mapping for US keyboards +// TODO: Replace this with a keymap from the X11 to get the correct regional map for the keyboard: +// Currently non US keyboards will have the wrong mapping for some keys +static const int keymapUS[] = { + 0, 256, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 259, 258, 81, 87, 69, 82, 84, + 89, 85, 73, 79, 80, 91, 93, 257, 341, 65, 83, 68, 70, 71, 72, 74, 75, 76, 59, 39, 96, + 340, 92, 90, 88, 67, 86, 66, 78, 77, 44, 46, 47, 344, 332, 342, 32, 280, 290, 291, + 292, 293, 294, 295, 296, 297, 298, 299, 282, 281, 327, 328, 329, 333, 324, 325, + 326, 334, 321, 322, 323, 320, 330, 0, 85, 86, 300, 301, 89, 90, 91, 92, 93, 94, 95, + 335, 345, 331, 283, 346, 101, 268, 265, 266, 263, 262, 269, 264, 267, 260, 261, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 347, 127, + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, + 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, + 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, + 192, 193, 194, 0, 0, 0, 0, 0, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, + 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, + 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, + 243, 244, 245, 246, 247, 248, 0, 0, 0, 0, 0, 0, 0 +}; + +// NOTE: The complete evdev EV_KEY list can be found at /usr/include/linux/input-event-codes.h +// TODO: Complete the LUT with all unicode decimal values +static const int EvkeyToUnicodeLUT[] = { + 0, 27, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 8, 0, 113, 119, 101, 114, + 116, 121, 117, 105, 111, 112, 0, 0, 13, 0, 97, 115, 100, 102, 103, 104, 106, 107, 108, 59, + 39, 96, 0, 92, 122, 120, 99, 118, 98, 110, 109, 44, 46, 47, 0, 0, 0, 32 + // LUT currently incomplete, just mapped the most essential keys +}; + //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- @@ -1454,27 +1487,6 @@ static void ConfigureEvdevDevice(char *device) // Poll and process evdev keyboard events static void PollKeyboardEvents(void) { - // Scancode to keycode mapping for US keyboards - // TODO: Replace this with a keymap from the X11 to get the correct regional map for the keyboard: - // Currently non US keyboards will have the wrong mapping for some keys - static const int keymapUS[] = { - 0, 256, 49, 50, 51, 52, 53, 54, 55, 56, 57, 48, 45, 61, 259, 258, 81, 87, 69, 82, 84, - 89, 85, 73, 79, 80, 91, 93, 257, 341, 65, 83, 68, 70, 71, 72, 74, 75, 76, 59, 39, 96, - 340, 92, 90, 88, 67, 86, 66, 78, 77, 44, 46, 47, 344, 332, 342, 32, 280, 290, 291, - 292, 293, 294, 295, 296, 297, 298, 299, 282, 281, 327, 328, 329, 333, 324, 325, - 326, 334, 321, 322, 323, 320, 330, 0, 85, 86, 300, 301, 89, 90, 91, 92, 93, 94, 95, - 335, 345, 331, 283, 346, 101, 268, 265, 266, 263, 262, 269, 264, 267, 260, 261, - 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 347, 127, - 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, - 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, - 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, - 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, - 192, 193, 194, 0, 0, 0, 0, 0, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, - 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, - 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, - 243, 244, 245, 246, 247, 248, 0, 0, 0, 0, 0, 0, 0 - }; - int fd = platform.keyboardFd; if (fd == -1) return; @@ -1518,6 +1530,18 @@ static void PollKeyboardEvents(void) } #endif + // Detect char presses (unicode) + if (event.value == 1) + { + // Check if there is space available in the queue + if (CORE.Input.Keyboard.charPressedQueueCount < MAX_CHAR_PRESSED_QUEUE) + { + // Add character to the queue + CORE.Input.Keyboard.charPressedQueue[CORE.Input.Keyboard.charPressedQueueCount] = EvkeyToUnicodeLUT[event.code]; + CORE.Input.Keyboard.charPressedQueueCount++; + } + } + if (CORE.Input.Keyboard.currentKeyState[CORE.Input.Keyboard.exitKey] == 1) CORE.Window.shouldClose = true; TRACELOGD("RPI: KEY_%s ScanCode: %4i KeyCode: %4i", (event.value == 0)? "UP" : "DOWN", event.code, keycode); From 64d64cc18114c02ecb068b20b6c4820df6182415 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 1 Nov 2023 15:28:18 +0100 Subject: [PATCH 0467/1350] REVIEWED: Potential code issues reported by CodeQL #3476 --- examples/core/core_automation_events.c | 6 +-- examples/core/core_loading_thread.c | 2 +- examples/core/core_random_values.c | 2 +- examples/models/models_skybox.c | 13 ++--- examples/others/raymath_vector_angle.c | 2 +- examples/shaders/shaders_raymarching.c | 3 +- examples/text/text_unicode.c | 18 +++---- src/rcore.c | 6 +-- src/rlgl.h | 63 ++++++++++++------------ src/rmodels.c | 4 +- src/rtext.c | 67 ++++++++++++++------------ src/rtextures.c | 4 +- 12 files changed, 98 insertions(+), 92 deletions(-) diff --git a/examples/core/core_automation_events.c b/examples/core/core_automation_events.c index 27711b39d..0739a6e7d 100644 --- a/examples/core/core_automation_events.c +++ b/examples/core/core_automation_events.c @@ -75,9 +75,9 @@ int main(void) bool eventRecording = false; bool eventPlaying = false; - int frameCounter = 0; - int playFrameCounter = 0; - int currentPlayFrame = 0; + unsigned int frameCounter = 0; + unsigned int playFrameCounter = 0; + unsigned int currentPlayFrame = 0; SetTargetFPS(60); //-------------------------------------------------------------------------------------- diff --git a/examples/core/core_loading_thread.c b/examples/core/core_loading_thread.c index 0538dcee3..8451ff037 100644 --- a/examples/core/core_loading_thread.c +++ b/examples/core/core_loading_thread.c @@ -41,7 +41,7 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [core] example - loading thread"); - pthread_t threadId; // Loading data thread id + pthread_t threadId = { 0 }; // Loading data thread id enum { STATE_WAITING, STATE_LOADING, STATE_FINISHED } state = STATE_WAITING; int framesCounter = 0; diff --git a/examples/core/core_random_values.c b/examples/core/core_random_values.c index c2225bcaf..bec1de27b 100644 --- a/examples/core/core_random_values.c +++ b/examples/core/core_random_values.c @@ -29,7 +29,7 @@ int main(void) int randValue = GetRandomValue(-8, 5); // Get a random integer number between -8 and 5 (both included) - int framesCounter = 0; // Variable used to count frames + unsigned int framesCounter = 0; // Variable used to count frames SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- diff --git a/examples/models/models_skybox.c b/examples/models/models_skybox.c index 7a500e04d..c583128b6 100644 --- a/examples/models/models_skybox.c +++ b/examples/models/models_skybox.c @@ -68,14 +68,12 @@ int main(void) char skyboxFileName[256] = { 0 }; - Texture2D panorama; - if (useHDR) { TextCopy(skyboxFileName, "resources/dresden_square_2k.hdr"); // Load HDR panorama (sphere) texture - panorama = LoadTexture(skyboxFileName); + Texture2D panorama = LoadTexture(skyboxFileName); // Generate cubemap (texture with 6 quads-cube-mapping) from panorama HDR texture // NOTE 1: New texture is generated rendering to texture, shader calculates the sphere->cube coordinates mapping @@ -83,7 +81,7 @@ int main(void) // despite texture can be successfully created.. so using PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 instead of PIXELFORMAT_UNCOMPRESSED_R32G32B32A32 skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = GenTextureCubemap(shdrCubemap, panorama, 1024, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8); - //UnloadTexture(panorama); // Texture not required anymore, cubemap already generated + UnloadTexture(panorama); // Texture not required anymore, cubemap already generated } else { @@ -113,15 +111,18 @@ int main(void) { if (IsFileExtension(droppedFiles.paths[0], ".png;.jpg;.hdr;.bmp;.tga")) { - // Unload current cubemap texture and load new one + // Unload current cubemap texture to load new one UnloadTexture(skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture); + if (useHDR) { + // Load HDR panorama (sphere) texture Texture2D panorama = LoadTexture(droppedFiles.paths[0]); // Generate cubemap from panorama texture skybox.materials[0].maps[MATERIAL_MAP_CUBEMAP].texture = GenTextureCubemap(shdrCubemap, panorama, 1024, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8); - UnloadTexture(panorama); + + UnloadTexture(panorama); // Texture not required anymore, cubemap already generated } else { diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c index ad573f54d..d0f81548c 100644 --- a/examples/others/raymath_vector_angle.c +++ b/examples/others/raymath_vector_angle.c @@ -42,7 +42,7 @@ int main(void) { // Update //---------------------------------------------------------------------------------- - float startangle; + float startangle = 0.0f; if (angleMode == 0) startangle = -Vector2LineAngle(v0, v1)*RAD2DEG; if (angleMode == 1) startangle = 0.0f; diff --git a/examples/shaders/shaders_raymarching.c b/examples/shaders/shaders_raymarching.c index e9b7755ad..ff403e619 100644 --- a/examples/shaders/shaders_raymarching.c +++ b/examples/shaders/shaders_raymarching.c @@ -82,7 +82,8 @@ int main(void) // Check if screen is resized if (IsWindowResized()) { - float resolution[2] = { (float)GetScreenWidth(), (float)GetScreenHeight() }; + resolution[0] = (float)GetScreenWidth(); + resolution[1] = (float)GetScreenHeight(); SetShaderValue(shader, resolutionLoc, resolution, SHADER_UNIFORM_VEC2); } //---------------------------------------------------------------------------------- diff --git a/examples/text/text_unicode.c b/examples/text/text_unicode.c index b25e3273b..42227f89c 100644 --- a/examples/text/text_unicode.c +++ b/examples/text/text_unicode.c @@ -195,7 +195,7 @@ int main(void) } Vector2 mouse = GetMousePosition(); - Vector2 pos = { 28.8f, 10.0f }; + Vector2 position = { 28.8f, 10.0f }; hovered = -1; //---------------------------------------------------------------------------------- @@ -210,21 +210,21 @@ int main(void) for (int i = 0; i < SIZEOF(emoji); ++i) { const char *txt = &emojiCodepoints[emoji[i].index]; - Rectangle emojiRect = { pos.x, pos.y, (float)fontEmoji.baseSize, (float)fontEmoji.baseSize }; + Rectangle emojiRect = { position.x, position.y, (float)fontEmoji.baseSize, (float)fontEmoji.baseSize }; if (!CheckCollisionPointRec(mouse, emojiRect)) { - DrawTextEx(fontEmoji, txt, pos, (float)fontEmoji.baseSize, 1.0f, selected == i ? emoji[i].color : Fade(LIGHTGRAY, 0.4f)); + DrawTextEx(fontEmoji, txt, position, (float)fontEmoji.baseSize, 1.0f, selected == i ? emoji[i].color : Fade(LIGHTGRAY, 0.4f)); } else { - DrawTextEx(fontEmoji, txt, pos, (float)fontEmoji.baseSize, 1.0f, emoji[i].color ); + DrawTextEx(fontEmoji, txt, position, (float)fontEmoji.baseSize, 1.0f, emoji[i].color ); hovered = i; - hoveredPos = pos; + hoveredPos = position; } - if ((i != 0) && (i%EMOJI_PER_WIDTH == 0)) { pos.y += fontEmoji.baseSize + 24.25f; pos.x = 28.8f; } - else pos.x += fontEmoji.baseSize + 28.8f; + if ((i != 0) && (i%EMOJI_PER_WIDTH == 0)) { position.y += fontEmoji.baseSize + 24.25f; position.x = 28.8f; } + else position.x += fontEmoji.baseSize + 28.8f; } //------------------------------------------------------------------------------ @@ -282,8 +282,8 @@ int main(void) int length = GetCodepointCount(messages[message].text); const char *info = TextFormat("%s %u characters %i bytes", messages[message].language, length, size); sz = MeasureTextEx(GetFontDefault(), info, 10, 1.0f); - Vector2 pos = { textRect.x + textRect.width - sz.x, msgRect.y + msgRect.height - sz.y - 2 }; - DrawText(info, (int)pos.x, (int)pos.y, 10, RAYWHITE); + + DrawText(info, (int)(textRect.x + textRect.width - sz.x), (int)(msgRect.y + msgRect.height - sz.y - 2), 10, RAYWHITE); } //------------------------------------------------------------------------------ diff --git a/src/rcore.c b/src/rcore.c index 175d68611..d4e2573b0 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -436,8 +436,8 @@ struct AutomationEvent { }; */ -static AutomationEventList *currentEventList = NULL; // Current automation events list, set by user, keep internal pointer -static bool automationEventRecording = false; // Recording automation events flag +static AutomationEventList *currentEventList = NULL; // Current automation events list, set by user, keep internal pointer +static bool automationEventRecording = false; // Recording automation events flag //static short automationEventEnabled = 0b0000001111111111; // TODO: Automation events enabled for recording/playing #endif //----------------------------------------------------------------------------------- @@ -2465,7 +2465,7 @@ bool ExportAutomationEventList(AutomationEventList list, const char *fileName) byteCount += sprintf(txtData + byteCount, "c %i\n", list.count); for (int i = 0; i < list.count; i++) { - byteCount += sprintf(txtData + byteCount, "e %i %i %i %i %i %i // Event: %s\n", list.events[i].frame, list.events[i].type, + byteCount += snprintf(txtData + byteCount, 256, "e %i %i %i %i %i %i // Event: %s\n", list.events[i].frame, list.events[i].type, list.events[i].params[0], list.events[i].params[1], list.events[i].params[2], list.events[i].params[3], autoEventTypeName[list.events[i].type]); } diff --git a/src/rlgl.h b/src/rlgl.h index da6573d62..0c695c20c 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -516,28 +516,28 @@ typedef enum { // Framebuffer attachment type // NOTE: By default up to 8 color channels defined, but it can be more typedef enum { - RL_ATTACHMENT_COLOR_CHANNEL0 = 0, // Framebuffer attachment type: color 0 - RL_ATTACHMENT_COLOR_CHANNEL1, // Framebuffer attachment type: color 1 - RL_ATTACHMENT_COLOR_CHANNEL2, // Framebuffer attachment type: color 2 - RL_ATTACHMENT_COLOR_CHANNEL3, // Framebuffer attachment type: color 3 - RL_ATTACHMENT_COLOR_CHANNEL4, // Framebuffer attachment type: color 4 - RL_ATTACHMENT_COLOR_CHANNEL5, // Framebuffer attachment type: color 5 - RL_ATTACHMENT_COLOR_CHANNEL6, // Framebuffer attachment type: color 6 - RL_ATTACHMENT_COLOR_CHANNEL7, // Framebuffer attachment type: color 7 - RL_ATTACHMENT_DEPTH = 100, // Framebuffer attachment type: depth - RL_ATTACHMENT_STENCIL = 200, // Framebuffer attachment type: stencil + RL_ATTACHMENT_COLOR_CHANNEL0 = 0, // Framebuffer attachment type: color 0 + RL_ATTACHMENT_COLOR_CHANNEL1 = 1, // Framebuffer attachment type: color 1 + RL_ATTACHMENT_COLOR_CHANNEL2 = 2, // Framebuffer attachment type: color 2 + RL_ATTACHMENT_COLOR_CHANNEL3 = 3, // Framebuffer attachment type: color 3 + RL_ATTACHMENT_COLOR_CHANNEL4 = 4, // Framebuffer attachment type: color 4 + RL_ATTACHMENT_COLOR_CHANNEL5 = 5, // Framebuffer attachment type: color 5 + RL_ATTACHMENT_COLOR_CHANNEL6 = 6, // Framebuffer attachment type: color 6 + RL_ATTACHMENT_COLOR_CHANNEL7 = 7, // Framebuffer attachment type: color 7 + RL_ATTACHMENT_DEPTH = 100, // Framebuffer attachment type: depth + RL_ATTACHMENT_STENCIL = 200, // Framebuffer attachment type: stencil } rlFramebufferAttachType; // Framebuffer texture attachment type typedef enum { - RL_ATTACHMENT_CUBEMAP_POSITIVE_X = 0, // Framebuffer texture attachment type: cubemap, +X side - RL_ATTACHMENT_CUBEMAP_NEGATIVE_X, // Framebuffer texture attachment type: cubemap, -X side - RL_ATTACHMENT_CUBEMAP_POSITIVE_Y, // Framebuffer texture attachment type: cubemap, +Y side - RL_ATTACHMENT_CUBEMAP_NEGATIVE_Y, // Framebuffer texture attachment type: cubemap, -Y side - RL_ATTACHMENT_CUBEMAP_POSITIVE_Z, // Framebuffer texture attachment type: cubemap, +Z side - RL_ATTACHMENT_CUBEMAP_NEGATIVE_Z, // Framebuffer texture attachment type: cubemap, -Z side - RL_ATTACHMENT_TEXTURE2D = 100, // Framebuffer texture attachment type: texture2d - RL_ATTACHMENT_RENDERBUFFER = 200, // Framebuffer texture attachment type: renderbuffer + RL_ATTACHMENT_CUBEMAP_POSITIVE_X = 0, // Framebuffer texture attachment type: cubemap, +X side + RL_ATTACHMENT_CUBEMAP_NEGATIVE_X = 1, // Framebuffer texture attachment type: cubemap, -X side + RL_ATTACHMENT_CUBEMAP_POSITIVE_Y = 2, // Framebuffer texture attachment type: cubemap, +Y side + RL_ATTACHMENT_CUBEMAP_NEGATIVE_Y = 3, // Framebuffer texture attachment type: cubemap, -Y side + RL_ATTACHMENT_CUBEMAP_POSITIVE_Z = 4, // Framebuffer texture attachment type: cubemap, +Z side + RL_ATTACHMENT_CUBEMAP_NEGATIVE_Z = 5, // Framebuffer texture attachment type: cubemap, -Z side + RL_ATTACHMENT_TEXTURE2D = 100, // Framebuffer texture attachment type: texture2d + RL_ATTACHMENT_RENDERBUFFER = 200, // Framebuffer texture attachment type: renderbuffer } rlFramebufferAttachTextureType; // Face culling mode @@ -823,6 +823,9 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad typedef void (GL_APIENTRYP PFNGLVERTEXATTRIBDIVISOREXTPROC) (GLuint index, GLuint divisor); #endif #endif +#if defined(GRAPHICS_API_OPENGL_ES3) + #include +#endif #include // Required for: malloc(), free() #include // Required for: strcmp(), strlen() [Used in rlglInit(), on extensions loading] @@ -2243,7 +2246,7 @@ void rlLoadExtensions(void *loader) #if defined(GRAPHICS_API_OPENGL_ES3) // Register supported extensions flags - // OpenGL ES 3.0 extensions supported by default + // OpenGL ES 3.0 extensions supported by default (or it should be) RLGL.ExtSupported.vao = true; RLGL.ExtSupported.instancing = true; RLGL.ExtSupported.texNPOT = true; @@ -2254,20 +2257,20 @@ void rlLoadExtensions(void *loader) RLGL.ExtSupported.maxDepthBits = 24; RLGL.ExtSupported.texAnisoFilter = true; RLGL.ExtSupported.texMirrorClamp = true; - // TODO: Make sure that the ones above are actually present by default - // TODO: Check for these... - // RLGL.ExtSupported.texCompDXT - // RLGL.ExtSupported.texCompETC1 - // RLGL.ExtSupported.texCompETC2 - // RLGL.ExtSupported.texCompPVRT - // RLGL.ExtSupported.texCompASTC - // RLGL.ExtSupported.computeShader - // RLGL.ExtSupported.ssbo - // RLGL.ExtSupported.maxAnisotropyLevel + // TODO: Check for additional OpenGL ES 3.0 supported extensions: + //RLGL.ExtSupported.texCompDXT = true; + //RLGL.ExtSupported.texCompETC1 = true; + //RLGL.ExtSupported.texCompETC2 = true; + //RLGL.ExtSupported.texCompPVRT = true; + //RLGL.ExtSupported.texCompASTC = true; + //RLGL.ExtSupported.maxAnisotropyLevel = true; + //RLGL.ExtSupported.computeShader = true; + //RLGL.ExtSupported.ssbo = true; + #elif defined(GRAPHICS_API_OPENGL_ES2) #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_DESKTOP_SDL) - // TODO: Support OpenGL ES 3.0 + // TODO: Support GLAD loader for OpenGL ES 3.0 if (gladLoadGLES2((GLADloadfunc)loader) == 0) TRACELOG(RL_LOG_WARNING, "GLAD: Cannot load OpenGL ES2.0 functions"); else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL ES 2.0 loaded successfully"); #endif diff --git a/src/rmodels.c b/src/rmodels.c index 3cae43d12..229d373fc 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2142,11 +2142,11 @@ Mesh GenMeshPoly(int sides, float radius) Vector3 *vertices = (Vector3 *)RL_MALLOC(vertexCount*sizeof(Vector3)); float d = 0.0f, dStep = 360.0f/sides; - for (int v = 0; v < vertexCount; v += 3) + for (int v = 0; v < vertexCount - 2; v += 3) { vertices[v] = (Vector3){ 0.0f, 0.0f, 0.0f }; vertices[v + 1] = (Vector3){ sinf(DEG2RAD*d)*radius, 0.0f, cosf(DEG2RAD*d)*radius }; - vertices[v + 2] = (Vector3){sinf(DEG2RAD*(d+dStep))*radius, 0.0f, cosf(DEG2RAD*(d+dStep))*radius }; + vertices[v + 2] = (Vector3){ sinf(DEG2RAD*(d+dStep))*radius, 0.0f, cosf(DEG2RAD*(d+dStep))*radius }; d += dStep; } diff --git a/src/rtext.c b/src/rtext.c index b83eb171b..dd7c83f39 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -2002,7 +2002,6 @@ int GetCodepointPrevious(const char *text, int *codepointSize) // Module specific Functions Definition //---------------------------------------------------------------------------------- #if defined(SUPPORT_FILEFORMAT_FNT) - // Read a line from memory // REQUIRES: memcpy() // NOTE: Returns the number of bytes read @@ -2032,7 +2031,9 @@ static Font LoadBMFont(const char *fileName) int imHeight = 0; char imFileName[129] = { 0 }; - int base = 0; // Useless data + int base = 0; // Useless data + int readBytes = 0; // Data bytes read + int readVars = 0; // Variables filled by sscanf() char *fileText = LoadFileText(fileName); @@ -2041,32 +2042,30 @@ static Font LoadBMFont(const char *fileName) char *fileTextPtr = fileText; // NOTE: We skip first line, it contains no useful information - int lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); - fileTextPtr += (lineBytes + 1); + readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); + fileTextPtr += (readBytes + 1); // Read line data - lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); + readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); searchPoint = strstr(buffer, "lineHeight"); - sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i", &fontSize, &base, &imWidth, &imHeight); - fileTextPtr += (lineBytes + 1); + readVars = sscanf(searchPoint, "lineHeight=%i base=%i scaleW=%i scaleH=%i", &fontSize, &base, &imWidth, &imHeight); + fileTextPtr += (readBytes + 1); + + if (readVars < 4) { UnloadFileText(fileText); return font; } // Some data not available, file malformed - TRACELOGD("FONT: [%s] Loaded font info:", fileName); - TRACELOGD(" > Base size: %i", fontSize); - TRACELOGD(" > Texture scale: %ix%i", imWidth, imHeight); - - lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); + readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); searchPoint = strstr(buffer, "file"); - sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName); - fileTextPtr += (lineBytes + 1); + readVars = sscanf(searchPoint, "file=\"%128[^\"]\"", imFileName); + fileTextPtr += (readBytes + 1); - TRACELOGD(" > Texture filename: %s", imFileName); + if (readVars < 1) { UnloadFileText(fileText); return font; } // No fileName read - lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); + readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); searchPoint = strstr(buffer, "count"); - sscanf(searchPoint, "count=%i", &glyphCount); - fileTextPtr += (lineBytes + 1); + readVars = sscanf(searchPoint, "count=%i", &glyphCount); + fileTextPtr += (readBytes + 1); - TRACELOGD(" > Chars count: %i", glyphCount); + if (readVars < 1) { UnloadFileText(fileText); return font; } // No glyphCount read // Compose correct path using route of .fnt file (fileName) and imFileName char *imPath = NULL; @@ -2124,22 +2123,26 @@ static Font LoadBMFont(const char *fileName) for (int i = 0; i < glyphCount; i++) { - lineBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); - sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i", + readBytes = GetLine(fileTextPtr, buffer, MAX_BUFFER_SIZE); + readVars = sscanf(buffer, "char id=%i x=%i y=%i width=%i height=%i xoffset=%i yoffset=%i xadvance=%i", &charId, &charX, &charY, &charWidth, &charHeight, &charOffsetX, &charOffsetY, &charAdvanceX); - fileTextPtr += (lineBytes + 1); + fileTextPtr += (readBytes + 1); + + if (readVars == 8) // Make sure all char data has been properly read + { + // Get character rectangle in the font atlas texture + font.recs[i] = (Rectangle){ (float)charX, (float)charY, (float)charWidth, (float)charHeight }; - // Get character rectangle in the font atlas texture - font.recs[i] = (Rectangle){ (float)charX, (float)charY, (float)charWidth, (float)charHeight }; + // Save data properly in sprite font + font.glyphs[i].value = charId; + font.glyphs[i].offsetX = charOffsetX; + font.glyphs[i].offsetY = charOffsetY; + font.glyphs[i].advanceX = charAdvanceX; - // Save data properly in sprite font - font.glyphs[i].value = charId; - font.glyphs[i].offsetX = charOffsetX; - font.glyphs[i].offsetY = charOffsetY; - font.glyphs[i].advanceX = charAdvanceX; - - // Fill character image data from imFont data - font.glyphs[i].image = ImageFromImage(imFont, font.recs[i]); + // Fill character image data from imFont data + font.glyphs[i].image = ImageFromImage(imFont, font.recs[i]); + } + else TRACELOG(LOG_WARNING, "FONT: [%s] Some characters data not correctly provided", fileName); } UnloadImage(imFont); diff --git a/src/rtextures.c b/src/rtextures.c index bf1b35e40..9465c5b39 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -1288,8 +1288,6 @@ void ImageFormat(Image *image, int newFormat) image->data = NULL; image->format = newFormat; - int k = 0; - switch (image->format) { case PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: @@ -1306,7 +1304,7 @@ void ImageFormat(Image *image, int newFormat) { image->data = (unsigned char *)RL_MALLOC(image->width*image->height*2*sizeof(unsigned char)); - for (int i = 0; i < image->width*image->height*2; i += 2, k++) + for (int i = 0, k = 0; i < image->width*image->height*2; i += 2, k++) { ((unsigned char *)image->data)[i] = (unsigned char)((pixels[k].x*0.299f + (float)pixels[k].y*0.587f + (float)pixels[k].z*0.114f)*255.0f); ((unsigned char *)image->data)[i + 1] = (unsigned char)(pixels[k].w*255.0f); From c563490cf8370f5741e37bdd72d6b14c65240c8c Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 1 Nov 2023 15:28:31 +0100 Subject: [PATCH 0468/1350] Update rcore_web.c --- src/platforms/rcore_web.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 959f60332..4d0bfcc21 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -46,7 +46,7 @@ **********************************************************************************************/ #define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) -// #define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?) +// #define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (translated to WebGL2?) #include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management #include // Emscripten functionality for C From d77c918d51d3be901759d5f3755f7beee1469658 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 1 Nov 2023 16:24:06 +0100 Subject: [PATCH 0469/1350] Update rcore_web.c --- src/platforms/rcore_web.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 4d0bfcc21..c5bd55369 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -45,8 +45,6 @@ * **********************************************************************************************/ -#define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) -// #define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (translated to WebGL2?) #include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management #include // Emscripten functionality for C From 35d27fb11b916d77075ec22dc07c4c411108d02d Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 2 Nov 2023 09:04:05 +0100 Subject: [PATCH 0470/1350] REVIEWED: Pointers exposing not required for ES3 --- src/rlgl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rlgl.h b/src/rlgl.h index 0c695c20c..8f2b5258f 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -1042,7 +1042,7 @@ typedef void *(*rlglLoadProc)(const char *name); // OpenGL extension functions static rlglData RLGL = { 0 }; #endif // GRAPHICS_API_OPENGL_33 || GRAPHICS_API_OPENGL_ES2 -#if defined(GRAPHICS_API_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_ES2) && !defined(GRAPHICS_API_OPENGL_ES3) // NOTE: VAO functionality is exposed through extensions (OES) static PFNGLGENVERTEXARRAYSOESPROC glGenVertexArrays = NULL; static PFNGLBINDVERTEXARRAYOESPROC glBindVertexArray = NULL; From de1ceae4b0ab5c68d75959490e9a81fa0c9f0593 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Thu, 2 Nov 2023 05:13:41 -0300 Subject: [PATCH 0471/1350] [core] Fix gestures for `PLATFORM_DESKTOP_SDL` (#3499) * Fix gestures for SDL * Review the gesture handling for SDL * Review 2 --- src/platforms/rcore_desktop_sdl.c | 48 ++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 7245b2d5c..91442d8e2 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -964,6 +964,15 @@ void PollInputEvents(void) // so, if mouse is not moved it returns a (0, 0) position... this behaviour should be reviewed! //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; + // Map touch position to mouse position for convenience + // WARNING: If the target desktop device supports touch screen, this behavious should be reviewed! + // https://www.codeproject.com/Articles/668404/Programming-for-Multi-Touch + // https://docs.microsoft.com/en-us/windows/win32/wintouch/getting-started-with-multi-touch-messages + CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; + + int touchAction = -1; // 0-TOUCH_ACTION_UP, 1-TOUCH_ACTION_DOWN, 2-TOUCH_ACTION_MOVE + bool gestureUpdate = false; // Flag to note gestures require to update + // Register previous keys states // NOTE: Android supports up to 260 keys for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) @@ -1077,10 +1086,16 @@ void PollInputEvents(void) case SDL_MOUSEBUTTONDOWN: { CORE.Input.Mouse.currentButtonState[event.button.button - 1] = 1; + + touchAction = 1; + gestureUpdate = true; } break; case SDL_MOUSEBUTTONUP: { CORE.Input.Mouse.currentButtonState[event.button.button - 1] = 0; + + touchAction = 0; + gestureUpdate = true; } break; case SDL_MOUSEWHEEL: { @@ -1100,6 +1115,10 @@ void PollInputEvents(void) CORE.Input.Mouse.currentPosition.x = (float)event.motion.x; CORE.Input.Mouse.currentPosition.y = (float)event.motion.y; } + + CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; + touchAction = 2; + gestureUpdate = true; } break; // Check gamepad events @@ -1122,11 +1141,38 @@ void PollInputEvents(void) } break; default: break; } + +#if defined(SUPPORT_GESTURES_SYSTEM) + if (gestureUpdate) + { + // Process mouse events as touches to be able to use mouse-gestures + GestureEvent gestureEvent = { 0 }; + + // Register touch actions + gestureEvent.touchAction = touchAction; + + // Assign a pointer ID + gestureEvent.pointId[0] = 0; + + // Register touch points count + gestureEvent.pointCount = 1; + + // Register touch points position, only one point registered + if (touchAction == 2) gestureEvent.position[0] = CORE.Input.Touch.position[0]; + else gestureEvent.position[0] = GetMousePosition(); + + // Normalize gestureEvent.position[0] for CORE.Window.screen.width and CORE.Window.screen.height + gestureEvent.position[0].x /= (float)GetScreenWidth(); + gestureEvent.position[0].y /= (float)GetScreenHeight(); + + // Gesture data is sent to gestures-system for processing + ProcessGestureEvent(gestureEvent); + } +#endif } //----------------------------------------------------------------------------- } - //---------------------------------------------------------------------------------- // Module Internal Functions Definition //---------------------------------------------------------------------------------- From ba75a7a23bbfcae1747ab6a87ecb155979ce6bc5 Mon Sep 17 00:00:00 2001 From: Caleb Barger <59521636+cabarger@users.noreply.github.com> Date: Thu, 2 Nov 2023 01:14:34 -0700 Subject: [PATCH 0472/1350] build.zig updates for 0.11.0 release. (#3501) --- build.zig | 2 +- src/build.zig | 80 +++++++++++++++++++++++++-------------------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/build.zig b/build.zig index 21638601d..f4d7d1590 100644 --- a/build.zig +++ b/build.zig @@ -1,7 +1,7 @@ const std = @import("std"); const raylib = @import("src/build.zig"); -// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) +// This has been tested to work with zig 0.11.0 pub fn build(b: *std.Build) void { raylib.build(b); } diff --git a/src/build.zig b/src/build.zig index 5e2b5916e..08f4b6bdf 100644 --- a/src/build.zig +++ b/src/build.zig @@ -20,55 +20,55 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built raylib.addIncludePath(.{ .path = srcdir ++ "/external/glfw/include" }); } - raylib.addCSourceFiles(.{ - .files = &.{ + raylib.addCSourceFiles( + &.{ srcdir ++ "/rcore.c", srcdir ++ "/utils.c", }, - .flags = raylib_flags, - }); + raylib_flags, + ); if (options.raudio) { - raylib.addCSourceFiles(.{ - .files = &.{ + raylib.addCSourceFiles( + &.{ srcdir ++ "/raudio.c", }, - .flags = raylib_flags, - }); + raylib_flags, + ); } if (options.rmodels) { - raylib.addCSourceFiles(.{ - .files = &.{ + raylib.addCSourceFiles( + &.{ srcdir ++ "/rmodels.c", }, - .flags = &[_][]const u8{ + &[_][]const u8{ "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 } ++ raylib_flags, - }); + ); } if (options.rshapes) { - raylib.addCSourceFiles(.{ - .files = &.{ + raylib.addCSourceFiles( + &.{ srcdir ++ "/rshapes.c", }, - .flags = raylib_flags, - }); + raylib_flags, + ); } if (options.rtext) { - raylib.addCSourceFiles(.{ - .files = &.{ + raylib.addCSourceFiles( + &.{ srcdir ++ "/rtext.c", }, - .flags = raylib_flags, - }); + raylib_flags, + ); } if (options.rtextures) { - raylib.addCSourceFiles(.{ - .files = &.{ + raylib.addCSourceFiles( + &.{ srcdir ++ "/rtextures.c", }, - .flags = raylib_flags, - }); + raylib_flags, + ); } var gen_step = b.addWriteFiles(); @@ -83,10 +83,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built switch (target.getOsTag()) { .windows => { - raylib.addCSourceFiles(.{ - .files = &.{srcdir ++ "/rglfw.c"}, - .flags = raylib_flags, - }); + raylib.addCSourceFiles( + &.{srcdir ++ "/rglfw.c"}, + raylib_flags, + ); raylib.linkSystemLibrary("winmm"); raylib.linkSystemLibrary("gdi32"); raylib.linkSystemLibrary("opengl32"); @@ -96,10 +96,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built }, .linux => { if (!options.platform_drm) { - raylib.addCSourceFiles(.{ - .files = &.{srcdir ++ "/rglfw.c"}, - .flags = raylib_flags, - }); + raylib.addCSourceFiles( + &.{srcdir ++ "/rglfw.c"}, + raylib_flags, + ); raylib.linkSystemLibrary("GL"); raylib.linkSystemLibrary("rt"); raylib.linkSystemLibrary("dl"); @@ -127,10 +127,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built } }, .freebsd, .openbsd, .netbsd, .dragonfly => { - raylib.addCSourceFiles(.{ - .files = &.{srcdir ++ "/rglfw.c"}, - .flags = raylib_flags, - }); + raylib.addCSourceFiles( + &.{srcdir ++ "/rglfw.c"}, + raylib_flags, + ); raylib.linkSystemLibrary("GL"); raylib.linkSystemLibrary("rt"); raylib.linkSystemLibrary("dl"); @@ -149,10 +149,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built const raylib_flags_extra_macos = &[_][]const u8{ "-ObjC", }; - raylib.addCSourceFiles(.{ - .files = &.{srcdir ++ "/rglfw.c"}, - .flags = raylib_flags ++ raylib_flags_extra_macos, - }); + raylib.addCSourceFiles( + &.{srcdir ++ "/rglfw.c"}, + raylib_flags ++ raylib_flags_extra_macos, + ); raylib.linkFramework("Foundation"); raylib.linkFramework("CoreServices"); raylib.linkFramework("CoreGraphics"); From fe34fc7c6b7e3731ce7f68b379f9847eaaaf2a8f Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Thu, 2 Nov 2023 13:35:11 -0300 Subject: [PATCH 0473/1350] Partial fix the gesture system for DRM (#3502) --- src/platforms/rcore_drm.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index c6307773f..ac5a1b6ca 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -170,6 +170,11 @@ static const int EvkeyToUnicodeLUT[] = { // LUT currently incomplete, just mapped the most essential keys }; +#if defined(SUPPORT_GESTURES_SYSTEM) +GestureEvent gestureEvent = { 0 }; // Gesture event to hold data between EventThread() and PollInputEvents() +bool newGesture = false; // Var to trigger ProcessGestureEvent(gestureEvent) on PollInputEvents() +#endif + //---------------------------------------------------------------------------------- // Module Internal Functions Declaration //---------------------------------------------------------------------------------- @@ -592,6 +597,18 @@ void PollInputEvents(void) // Reset touch positions //for (int i = 0; i < MAX_TOUCH_POINTS; i++) CORE.Input.Touch.position[i] = (Vector2){ 0, 0 }; + // Map touch position to mouse position for convenience + CORE.Input.Touch.position[0] = CORE.Input.Mouse.currentPosition; + +#if defined(SUPPORT_GESTURES_SYSTEM) + // Call the ProcessGestureEvent here instead of on EventThread() to workaround the threads not matching + if (newGesture) + { + ProcessGestureEvent(gestureEvent); + newGesture = false; + } +#endif + #if defined(SUPPORT_SSH_KEYBOARD_RPI) // NOTE: Keyboard reading could be done using input_event(s) or just read from stdin, both methods are used here. // stdin reading is still used for legacy purposes, it allows keyboard input trough SSH console @@ -603,7 +620,6 @@ void PollInputEvents(void) #endif } - //---------------------------------------------------------------------------------- // Module Internal Functions Definition //---------------------------------------------------------------------------------- @@ -1715,7 +1731,7 @@ static void *EventThread(void *arg) #if defined(SUPPORT_GESTURES_SYSTEM) if (gestureUpdate) { - GestureEvent gestureEvent = { 0 }; + //GestureEvent gestureEvent = { 0 }; gestureEvent.touchAction = touchAction; gestureEvent.pointCount = CORE.Input.Touch.pointCount; @@ -1726,7 +1742,8 @@ static void *EventThread(void *arg) gestureEvent.position[i] = CORE.Input.Touch.position[i]; } - ProcessGestureEvent(gestureEvent); + //ProcessGestureEvent(gestureEvent); + newGesture = true; } #endif } From 01bbd425196631ea16e3c8fd4666f801fe8e4692 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 2 Nov 2023 18:11:13 +0100 Subject: [PATCH 0474/1350] Update rprand.h --- src/external/rprand.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/external/rprand.h b/src/external/rprand.h index a9b3a6eaa..b8dc4a279 100644 --- a/src/external/rprand.h +++ b/src/external/rprand.h @@ -196,15 +196,14 @@ unsigned int *rprand_load_sequence(unsigned int count, int min, int max) sequence = (unsigned int *)RPRAND_CALLOC(count, sizeof(unsigned int)); uint32_t value = 0; - int value_count = 0; bool value_is_dup = false; - for (int i = 0; value_count < count; i++) + for (int i = 0; i < count;) { value = rprand_xoshiro()%(max - min) + min; value_is_dup = false; - for (int j = 0; j < value_count; j++) + for (int j = 0; j < i; j++) { if (sequence[j] == value) { @@ -215,8 +214,8 @@ unsigned int *rprand_load_sequence(unsigned int count, int min, int max) if (!value_is_dup) { - sequence[value_count] = value; - value_count++; + sequence[i] = value; + i++; } } From b40f93b9e35ca8b2aa2e897eadb90187c2f93d4a Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 2 Nov 2023 18:12:22 +0100 Subject: [PATCH 0475/1350] Comments tweaks --- examples/Makefile | 32 +++++++++++++++---------------- src/Makefile | 32 +++++++++++++++---------------- src/platforms/rcore_desktop.c | 2 +- src/platforms/rcore_desktop_sdl.c | 3 +-- src/platforms/rcore_drm.c | 4 ++-- src/rcore.c | 32 +++++++++++++++---------------- 6 files changed, 52 insertions(+), 53 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index fe0ee9fbd..e126ab609 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -4,22 +4,22 @@ # # This file supports building raylib examples for the following platforms: # -# - PLATFORM_DESKTOP (GLFW backend): -# > Windows (Win32, Win64) -# > Linux (X11/Wayland desktop mode) -# > macOS/OSX (x64, arm64) -# > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) -# - PLATFORM_DESKTOP_SDL (SDL backend): -# > Windows (Win32, Win64) -# > Linux (X11/Wayland desktop mode) -# > Others (not tested) -# - PLATFORM_WEB: -# > HTML5 (WebAssembly) -# - PLATFORM_DRM: -# > Raspberry Pi 0-5 (no X11/Wayland) -# > Linux native mode (KMS driver) -# - PLATFORM_ANDROID: -# > Android (ARM, ARM64) +# > PLATFORM_DESKTOP (GLFW backend): +# - Windows (Win32, Win64) +# - Linux (X11/Wayland desktop mode) +# - macOS/OSX (x64, arm64) +# - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +# > PLATFORM_DESKTOP_SDL (SDL backend): +# - Windows (Win32, Win64) +# - Linux (X11/Wayland desktop mode) +# - Others (not tested) +# > PLATFORM_WEB: +# - HTML5 (WebAssembly) +# > PLATFORM_DRM: +# - Raspberry Pi 0-5 (DRM/KMS) +# - Linux DRM subsystem (KMS mode) +# > PLATFORM_ANDROID: +# - Android (ARM, ARM64) # # Copyright (c) 2013-2023 Ramon Santamaria (@raysan5) # diff --git a/src/Makefile b/src/Makefile index 2406588b8..7bcece7c8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -4,22 +4,22 @@ # # This file supports building raylib library for the following platforms: # -# - PLATFORM_DESKTOP (GLFW backend): -# > Windows (Win32, Win64) -# > Linux (X11/Wayland desktop mode) -# > macOS/OSX (x64, arm64) -# > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) -# - PLATFORM_DESKTOP_SDL (SDL backend): -# > Windows (Win32, Win64) -# > Linux (X11/Wayland desktop mode) -# > Others (not tested) -# - PLATFORM_WEB: -# > HTML5 (WebAssembly) -# - PLATFORM_DRM: -# > Raspberry Pi 0-5 (no X11/Wayland) -# > Linux native mode (KMS driver) -# - PLATFORM_ANDROID: -# > Android (ARM, ARM64) +# > PLATFORM_DESKTOP (GLFW backend): +# - Windows (Win32, Win64) +# - Linux (X11/Wayland desktop mode) +# - macOS/OSX (x64, arm64) +# - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +# > PLATFORM_DESKTOP_SDL (SDL backend): +# - Windows (Win32, Win64) +# - Linux (X11/Wayland desktop mode) +# - Others (not tested) +# > PLATFORM_WEB: +# - HTML5 (WebAssembly) +# > PLATFORM_DRM: +# - Raspberry Pi 0-5 (DRM/KMS) +# - Linux DRM subsystem (KMS mode) +# > PLATFORM_ANDROID: +# - Android (ARM, ARM64) # # Many thanks to Milan Nikolic (@gen2brain) for implementing Android platform pipeline. # Many thanks to Emanuele Petriglia for his contribution on GNU/Linux pipeline. diff --git a/src/platforms/rcore_desktop.c b/src/platforms/rcore_desktop.c index 3d38dc898..426d9fc01 100644 --- a/src/platforms/rcore_desktop.c +++ b/src/platforms/rcore_desktop.c @@ -6,7 +6,7 @@ * - Windows (Win32, Win64) * - Linux (X11/Wayland desktop mode) * - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) -* - OSX/macOS +* - OSX/macOS (x64, arm64) * * LIMITATIONS: * - Limitation 01 diff --git a/src/platforms/rcore_desktop_sdl.c b/src/platforms/rcore_desktop_sdl.c index 91442d8e2..87ceacb86 100644 --- a/src/platforms/rcore_desktop_sdl.c +++ b/src/platforms/rcore_desktop_sdl.c @@ -5,8 +5,7 @@ * PLATFORM: DESKTOP: SDL * - Windows (Win32, Win64) * - Linux (X11/Wayland desktop mode) -* - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) -* - OSX/macOS (x64, arm64) +* - Others (not tested) * * LIMITATIONS: * - Limitation 01 diff --git a/src/platforms/rcore_drm.c b/src/platforms/rcore_drm.c index ac5a1b6ca..7bdc2d00d 100644 --- a/src/platforms/rcore_drm.c +++ b/src/platforms/rcore_drm.c @@ -3,8 +3,8 @@ * rcore_drm - Functions to manage window, graphics device and inputs * * PLATFORM: DRM -* - Raspberry Pi 0-5 (native mode) -* - Linux native mode (KMS driver) +* - Raspberry Pi 0-5 (DRM/KMS) +* - Linux DRM subsystem (KMS mode) * * LIMITATIONS: * - Most of the window/monitor functions are not implemented (not required) diff --git a/src/rcore.c b/src/rcore.c index d4e2573b0..e4d8eae5c 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3,22 +3,22 @@ * rcore - Window/display management, Graphic device/context management and input management * * PLATFORMS SUPPORTED: -* - PLATFORM_DESKTOP (GLFW backend): -* > Windows (Win32, Win64) -* > Linux (X11/Wayland desktop mode) -* > macOS/OSX (x64, arm64) -* > FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) -* - PLATFORM_DESKTOP_SDL (SDL backend): -* > Windows (Win32, Win64) -* > Linux (X11/Wayland desktop mode) -* > Others (not tested) -* - PLATFORM_WEB: -* > HTML5 (WebAssembly) -* - PLATFORM_DRM: -* > Raspberry Pi 0-5 -* > Linux native mode (KMS driver) -* - PLATFORM_ANDROID: -* > Android (ARM, ARM64) +* > PLATFORM_DESKTOP (GLFW backend): +* - Windows (Win32, Win64) +* - Linux (X11/Wayland desktop mode) +* - macOS/OSX (x64, arm64) +* - FreeBSD, OpenBSD, NetBSD, DragonFly (X11 desktop) +* > PLATFORM_DESKTOP_SDL (SDL backend): +* - Windows (Win32, Win64) +* - Linux (X11/Wayland desktop mode) +* - Others (not tested) +* > PLATFORM_WEB: +* - HTML5 (WebAssembly) +* > PLATFORM_DRM: +* - Raspberry Pi 0-5 (DRM/KMS) +* - Linux DRM subsystem (KMS mode) +* > PLATFORM_ANDROID: +* - Android (ARM, ARM64) * * CONFIGURATION: * #define SUPPORT_DEFAULT_FONT (default) From 807516a991deef0eae23980b9a829721028ab8c8 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 2 Nov 2023 18:13:37 +0100 Subject: [PATCH 0476/1350] Support OpenGL ES 3.0 building on Web For some reason, the equivalent requested context (WebGL 2.0) is not provided, despite being properly requested. --- src/platforms/rcore_web.c | 5 ++++- src/rlgl.h | 17 ++++++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index c5bd55369..233616182 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -45,7 +45,9 @@ * **********************************************************************************************/ -#include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management +#define GLFW_INCLUDE_NONE // Disable the standard OpenGL header inclusion on GLFW3 + // NOTE: Already provided by rlgl implementation (on glad.h) +#include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management #include // Emscripten functionality for C #include // Emscripten HTML5 library @@ -759,6 +761,7 @@ int InitPlatform(void) } else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context { + // TODO: It seems WebGL 2.0 context is not set despite being requested glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); diff --git a/src/rlgl.h b/src/rlgl.h index 8f2b5258f..76704dfc2 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -799,10 +799,14 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #define GLAD_FREE RL_FREE #define GLAD_GL_IMPLEMENTATION - #include "external/glad.h" // GLAD extensions loading library, includes OpenGL headers + #include "external/glad.h" // GLAD extensions loading library, includes OpenGL headers #endif -#if defined(GRAPHICS_API_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_ES3) + #include // OpenGL ES 3.0 library + #define GL_GLEXT_PROTOTYPES + #include // OpenGL ES 2.0 extensions library +#elif defined(GRAPHICS_API_OPENGL_ES2) // NOTE: OpenGL ES 2.0 can be enabled on PLATFORM_DESKTOP, // in that case, functions are loaded from a custom glad for OpenGL ES 2.0 #if defined(PLATFORM_DESKTOP) || defined(PLATFORM_DESKTOP_SDL) @@ -823,9 +827,6 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad typedef void (GL_APIENTRYP PFNGLVERTEXATTRIBDIVISOREXTPROC) (GLuint index, GLuint divisor); #endif #endif -#if defined(GRAPHICS_API_OPENGL_ES3) - #include -#endif #include // Required for: malloc(), free() #include // Required for: strcmp(), strlen() [Used in rlglInit(), on extensions loading] @@ -902,8 +903,10 @@ RLAPI void rlLoadDrawQuad(void); // Load and draw a quad #if defined(GRAPHICS_API_OPENGL_ES2) #define glClearDepth glClearDepthf - #define GL_READ_FRAMEBUFFER GL_FRAMEBUFFER - #define GL_DRAW_FRAMEBUFFER GL_FRAMEBUFFER + #if !defined(GRAPHICS_API_OPENGL_ES3) + #define GL_READ_FRAMEBUFFER GL_FRAMEBUFFER + #define GL_DRAW_FRAMEBUFFER GL_FRAMEBUFFER + #endif #endif // Default shader vertex attribute names to set location points From 3c3e311190215451f31b7ea868f7950a8bed909f Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 2 Nov 2023 18:18:05 +0100 Subject: [PATCH 0477/1350] Remove unneeded line on web platform --- src/platforms/rcore_web.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/platforms/rcore_web.c b/src/platforms/rcore_web.c index 233616182..1fe4d5841 100644 --- a/src/platforms/rcore_web.c +++ b/src/platforms/rcore_web.c @@ -896,7 +896,6 @@ int InitPlatform(void) // If graphic device is no properly initialized, we end program if (!CORE.Window.ready) { TRACELOG(LOG_FATAL, "PLATFORM: Failed to initialize graphic device"); return -1; } - else SetWindowPosition(GetMonitorWidth(GetCurrentMonitor())/2 - CORE.Window.screen.width/2, GetMonitorHeight(GetCurrentMonitor())/2 - CORE.Window.screen.height/2); // Load OpenGL extensions // NOTE: GL procedures address loader is required to load extensions From 301d1b85ab62b13d072a0430668602398b382104 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 2 Nov 2023 18:18:27 +0100 Subject: [PATCH 0478/1350] Update raylib version to **raylib 5.0** --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index 3f6328ada..c732d8ef1 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -84,7 +84,7 @@ #define RAYLIB_VERSION_MAJOR 5 #define RAYLIB_VERSION_MINOR 0 #define RAYLIB_VERSION_PATCH 0 -#define RAYLIB_VERSION "5.0-dev" +#define RAYLIB_VERSION "5.0" // Function specifiers in case library is build/used as a shared library (Windows) // NOTE: Microsoft specifiers to tell compiler that symbols are imported/exported from a .dll From 5da0074fed50f3a63d3c47aa0f2e1fbdc9051059 Mon Sep 17 00:00:00 2001 From: AndreaBoroni <38136330+AndreaBoroni@users.noreply.github.com> Date: Fri, 3 Nov 2023 19:12:42 +0100 Subject: [PATCH 0479/1350] Fixed Issue 3504 (#3505) --- src/rcore.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index e4d8eae5c..ac5dea842 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2520,7 +2520,16 @@ void PlayAutomationEvent(AutomationEvent event) { // Input event case INPUT_KEY_UP: CORE.Input.Keyboard.currentKeyState[event.params[0]] = false; break; // param[0]: key - case INPUT_KEY_DOWN: CORE.Input.Keyboard.currentKeyState[event.params[0]] = true; break; // param[0]: key + case INPUT_KEY_DOWN: { // param[0]: key + CORE.Input.Keyboard.currentKeyState[event.params[0]] = true; + if (CORE.Input.Keyboard.previousKeyState[event.params[0]] == false) { + if (CORE.Input.Keyboard.keyPressedQueueCount < MAX_KEY_PRESSED_QUEUE) { + // Add character to the queue + CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = event.params[0]; + CORE.Input.Keyboard.keyPressedQueueCount++; + } + } + } break; case INPUT_MOUSE_BUTTON_UP: CORE.Input.Mouse.currentButtonState[event.params[0]] = false; break; // param[0]: key case INPUT_MOUSE_BUTTON_DOWN: CORE.Input.Mouse.currentButtonState[event.params[0]] = true; break; // param[0]: key case INPUT_MOUSE_POSITION: // param[0]: x, param[1]: y From 2d1b2119202d08245ca4d313b846a6d0a8b2139b Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 3 Nov 2023 20:21:43 +0100 Subject: [PATCH 0480/1350] ADDED: `LoadRandomSequence()`/`UnloadRandomSequence()` --- src/external/rprand.h | 28 +++++++++++------------ src/raylib.h | 5 ++++- src/rcore.c | 52 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/src/external/rprand.h b/src/external/rprand.h index b8dc4a279..acdeac671 100644 --- a/src/external/rprand.h +++ b/src/external/rprand.h @@ -117,10 +117,10 @@ extern "C" { // Prevents name mangling of functions // Module Functions Declaration //---------------------------------------------------------------------------------- RPRANDAPI void rprand_set_seed(unsigned long long seed); // Set rprand_state for Xoshiro128**, seed is 64bit -RPRANDAPI unsigned int rprand_get_value(int min, int max); // Get random value within a range, min and max included +RPRANDAPI int rprand_get_value(int min, int max); // Get random value within a range, min and max included -RPRANDAPI unsigned int *rprand_load_sequence(unsigned int count, int min, int max); // Load pseudo-random numbers sequence with no duplicates -RPRANDAPI void rprand_unload_sequence(unsigned int *sequence); // Unload pseudo-random numbers sequence +RPRANDAPI int *rprand_load_sequence(unsigned int count, int min, int max); // Load pseudo-random numbers sequence with no duplicates +RPRANDAPI void rprand_unload_sequence(int *sequence); // Unload pseudo-random numbers sequence #ifdef __cplusplus } @@ -136,7 +136,7 @@ RPRANDAPI void rprand_unload_sequence(unsigned int *sequence); // Unload pseudo #if defined(RPRAND_IMPLEMENTATION) -#include // Required for: calloc(), free() +#include // Required for: calloc(), free(), abs() #include // Required for data types: uint32_t, uint64_t //---------------------------------------------------------------------------------- @@ -174,33 +174,33 @@ void rprand_set_seed(unsigned long long seed) } // Get random value within a range, min and max included -unsigned int rprand_get_value(int min, int max) +int rprand_get_value(int min, int max) { - unsigned int value = rprand_xoshiro()%(max - min) + min; + int value = rprand_xoshiro()%(abs(max - min) + 1) + min; return value; } -// Load pseudo-random numbers sequence with no duplicates -unsigned int *rprand_load_sequence(unsigned int count, int min, int max) +// Load pseudo-random numbers sequence with no duplicates, min and max included +int *rprand_load_sequence(unsigned int count, int min, int max) { - unsigned int *sequence = NULL; + int *sequence = NULL; - if (count > (max - min)) + if (count > (abs(max - min) + 1)) { RPRAND_LOG("WARNING: Sequence count required is greater than range provided\n"); //count = (max - min); return sequence; } - sequence = (unsigned int *)RPRAND_CALLOC(count, sizeof(unsigned int)); + sequence = (int *)RPRAND_CALLOC(count, sizeof(int)); - uint32_t value = 0; + int value = 0; bool value_is_dup = false; for (int i = 0; i < count;) { - value = rprand_xoshiro()%(max - min) + min; + value = (rprand_xoshiro()%(abs(max - min) + 1)) + min; value_is_dup = false; for (int j = 0; j < i; j++) @@ -223,7 +223,7 @@ unsigned int *rprand_load_sequence(unsigned int count, int min, int max) } // Unload pseudo-random numbers sequence -void rprand_unload_sequence(unsigned int *sequence) +void rprand_unload_sequence(int *sequence) { RPRAND_FREE(sequence); sequence = NULL; diff --git a/src/raylib.h b/src/raylib.h index c732d8ef1..4a353a70f 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1067,10 +1067,13 @@ RLAPI void SwapScreenBuffer(void); // Swap back b RLAPI void PollInputEvents(void); // Register all input events RLAPI void WaitTime(double seconds); // Wait for some time (halt program execution) -// Misc. functions +// Random values generation functions RLAPI void SetRandomSeed(unsigned int seed); // Set the seed for the random number generator RLAPI int GetRandomValue(int min, int max); // Get a random value between min and max (both included) +RLAPI int *LoadRandomSequence(unsigned int count, int min, int max); // Load random values sequence, no values repeated +RLAPI void UnloadRandomSequence(int *sequence); // Unload random values sequence +// Misc. functions RLAPI void TakeScreenshot(const char *fileName); // Takes a screenshot of current screen (filename extension defines format) RLAPI void SetConfigFlags(unsigned int flags); // Setup init configuration flags (view FLAGS) RLAPI void OpenURL(const char *url); // Open URL with default system browser (if available) diff --git a/src/rcore.c b/src/rcore.c index ac5dea842..b09dffe67 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1677,7 +1677,7 @@ void SetRandomSeed(unsigned int seed) #endif } -// Get a random value between min and max (both included) +// Get a random value between min and max included int GetRandomValue(int min, int max) { int value = 0; @@ -1695,6 +1695,7 @@ int GetRandomValue(int min, int max) // WARNING: Ranges higher than RAND_MAX will return invalid results // More specifically, if (max - min) > INT_MAX there will be an overflow, // and otherwise if (max - min) > RAND_MAX the random value will incorrectly never exceed a certain threshold + // NOTE: Depending on the library it can be as low as 32767 if ((unsigned int)(max - min) > (unsigned int)RAND_MAX) { TRACELOG(LOG_WARNING, "Invalid GetRandomValue() arguments, range should not be higher than %i", RAND_MAX); @@ -1705,6 +1706,55 @@ int GetRandomValue(int min, int max) return value; } +// Load random values sequence, no values repeated, min and max included +int *LoadRandomSequence(unsigned int count, int min, int max) +{ + int *values = NULL; + +#if defined(SUPPORT_RPRAND_GENERATOR) + rprand_load_sequence(count, min, max); +#else + if (count > (abs(max - min) + 1)) return values; + + values = (int *)RL_CALLOC(count, sizeof(int)); + + int value = 0; + bool dupValue = false; + + for (int i = 0; i < count;) + { + value = (rand()%(abs(max - min) + 1) + min); + dupValue = false; + + for (int j = 0; j < i; j++) + { + if (values[j] == value) + { + dupValue = true; + break; + } + } + + if (!dupValue) + { + values[i] = value; + i++; + } + } +#endif + return values; +} + +// Unload random values sequence +void UnloadRandomSequence(int *sequence) +{ +#if defined(SUPPORT_RPRAND_GENERATOR) + rprand_unload_sequence(sequence); +#else + RL_FREE(sequence); +#endif +} + // Takes a screenshot of current screen (saved a .png) void TakeScreenshot(const char *fileName) { From 32e4be6fb9ec23b325d64b651a945a234351bb8f Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 3 Nov 2023 22:23:50 +0100 Subject: [PATCH 0481/1350] Update rcore.c --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index b09dffe67..9a9ac3f44 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1712,7 +1712,7 @@ int *LoadRandomSequence(unsigned int count, int min, int max) int *values = NULL; #if defined(SUPPORT_RPRAND_GENERATOR) - rprand_load_sequence(count, min, max); + values = rprand_load_sequence(count, min, max); #else if (count > (abs(max - min) + 1)) return values; From 39b539c42f61270631c501e481076902b7f8fd7c Mon Sep 17 00:00:00 2001 From: freakmangd <53349189+freakmangd@users.noreply.github.com> Date: Sat, 4 Nov 2023 16:05:21 -0400 Subject: [PATCH 0482/1350] Allow raylib to be built with zig 0.11.0 and zig 0.12.0dev (master) (#3506) * allow zig 0.11.0 and 0.12.0dev to build raylib * update comment --- src/build.zig | 102 +++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 56 deletions(-) diff --git a/src/build.zig b/src/build.zig index 08f4b6bdf..12d4a7a58 100644 --- a/src/build.zig +++ b/src/build.zig @@ -1,6 +1,7 @@ const std = @import("std"); +const builtin = @import("builtin"); -// This has been tested to work with zig 0.11.0 (67709b6, Aug 4 2023) +// This has been tested to work with zig 0.11.0 and zig 0.12.0-dev.1390+94cee4fb2 pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode, options: Options) *std.Build.CompileStep { const raylib_flags = &[_][]const u8{ "-std=gnu99", @@ -20,55 +21,37 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built raylib.addIncludePath(.{ .path = srcdir ++ "/external/glfw/include" }); } - raylib.addCSourceFiles( - &.{ - srcdir ++ "/rcore.c", - srcdir ++ "/utils.c", - }, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rcore.c", + srcdir ++ "/utils.c", + }, raylib_flags); if (options.raudio) { - raylib.addCSourceFiles( - &.{ - srcdir ++ "/raudio.c", - }, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/raudio.c", + }, raylib_flags); } if (options.rmodels) { - raylib.addCSourceFiles( - &.{ - srcdir ++ "/rmodels.c", - }, - &[_][]const u8{ - "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 - } ++ raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rmodels.c", + }, &[_][]const u8{ + "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 + } ++ raylib_flags); } if (options.rshapes) { - raylib.addCSourceFiles( - &.{ - srcdir ++ "/rshapes.c", - }, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rshapes.c", + }, raylib_flags); } if (options.rtext) { - raylib.addCSourceFiles( - &.{ - srcdir ++ "/rtext.c", - }, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rtext.c", + }, raylib_flags); } if (options.rtextures) { - raylib.addCSourceFiles( - &.{ - srcdir ++ "/rtextures.c", - }, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rtextures.c", + }, raylib_flags); } var gen_step = b.addWriteFiles(); @@ -83,10 +66,9 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built switch (target.getOsTag()) { .windows => { - raylib.addCSourceFiles( - &.{srcdir ++ "/rglfw.c"}, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rglfw.c", + }, raylib_flags); raylib.linkSystemLibrary("winmm"); raylib.linkSystemLibrary("gdi32"); raylib.linkSystemLibrary("opengl32"); @@ -96,10 +78,9 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built }, .linux => { if (!options.platform_drm) { - raylib.addCSourceFiles( - &.{srcdir ++ "/rglfw.c"}, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rglfw.c", + }, raylib_flags); raylib.linkSystemLibrary("GL"); raylib.linkSystemLibrary("rt"); raylib.linkSystemLibrary("dl"); @@ -127,10 +108,9 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built } }, .freebsd, .openbsd, .netbsd, .dragonfly => { - raylib.addCSourceFiles( - &.{srcdir ++ "/rglfw.c"}, - raylib_flags, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rglfw.c", + }, raylib_flags); raylib.linkSystemLibrary("GL"); raylib.linkSystemLibrary("rt"); raylib.linkSystemLibrary("dl"); @@ -149,10 +129,9 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built const raylib_flags_extra_macos = &[_][]const u8{ "-ObjC", }; - raylib.addCSourceFiles( - &.{srcdir ++ "/rglfw.c"}, - raylib_flags ++ raylib_flags_extra_macos, - ); + addCSourceFilesVersioned(raylib, &.{ + srcdir ++ "/rglfw.c", + }, raylib_flags ++ raylib_flags_extra_macos); raylib.linkFramework("Foundation"); raylib.linkFramework("CoreServices"); raylib.linkFramework("CoreGraphics"); @@ -235,3 +214,14 @@ const srcdir = struct { return std.fs.path.dirname(@src().file).?; } }.getSrcDir(); + +fn addCSourceFilesVersioned(exe: *std.Build.Step.Compile, files: []const []const u8, flags: []const []const u8) void { + if (comptime builtin.zig_version.minor >= 12) { + exe.addCSourceFiles(.{ + .files = files, + .flags = flags, + }); + } else { + exe.addCSourceFiles(files, flags); + } +} From cbf0324c3469df0303f2b00ed67b194a9dd98f0a Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 12:18:54 +0100 Subject: [PATCH 0483/1350] Updating web shells open-graph info --- src/minshell.html | 7 ++++--- src/shell.html | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/minshell.html b/src/minshell.html index 2aa75578f..7a944b7b9 100644 --- a/src/minshell.html +++ b/src/minshell.html @@ -8,10 +8,11 @@ - + + @@ -20,12 +21,12 @@ - + - + diff --git a/src/shell.html b/src/shell.html index b373394bb..d4ef3a03c 100644 --- a/src/shell.html +++ b/src/shell.html @@ -8,10 +8,11 @@ - + + @@ -20,12 +21,12 @@ - + - + From 53451e98a72934c8619afc5e934b95a9e5fd9c1c Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 13:02:29 +0100 Subject: [PATCH 0484/1350] Updated open graph for examples --- src/minshell.html | 4 ++-- src/shell.html | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/minshell.html b/src/minshell.html index 7a944b7b9..e18974ba1 100644 --- a/src/minshell.html +++ b/src/minshell.html @@ -15,8 +15,8 @@ - - + + diff --git a/src/shell.html b/src/shell.html index d4ef3a03c..15c3f39c0 100644 --- a/src/shell.html +++ b/src/shell.html @@ -15,8 +15,8 @@ - - + + From 17a968d2ed327eeaac406b9c123b70f14963cc81 Mon Sep 17 00:00:00 2001 From: glowiak <52356948+glowiak@users.noreply.github.com> Date: Sun, 5 Nov 2023 15:58:48 +0100 Subject: [PATCH 0485/1350] Jaylib is now up-to-date (#3508) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 400bc3846..63c9566d9 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -34,7 +34,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-hx | 4.2 | [Haxe](https://haxe.org/) | Zlib | https://github.com/foreignsasquatch/raylib-hx | | hb-raylib | 3.5 | [Harbour](https://harbour.github.io) | MIT | https://github.com/MarcosLeonardoMendezGerencir/hb-raylib | | jaylib | 4.5 | [Janet](https://janet-lang.org/) | MIT | https://github.com/janet-lang/jaylib | -| jaylib | 4.2 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | GPLv3+CE | https://github.com/electronstudio/jaylib/ | +| jaylib | **4.5** | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | GPLv3+CE | https://github.com/electronstudio/jaylib/ | | raylib-j | 4.0 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | Zlib | https://github.com/CreedVI/Raylib-J | | raylib.jl | 4.2 | [Julia](https://julialang.org/) | Zlib | https://github.com/irishgreencitrus/raylib.jl | | kaylib | 3.7 | [Kotlin/native](https://kotlinlang.org) | ? | https://github.com/electronstudio/kaylib | From 127d69e8870ab4cf3269b367191f3655f472794c Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 19:24:45 +0100 Subject: [PATCH 0486/1350] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index fadc3b659..4604abff6 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,6 @@ Ready to learn? Jump to [code examples!](https://www.raylib.com/examples.html) [![Windows](https://github.com/raysan5/raylib/workflows/Windows/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AWindows) [![Linux](https://github.com/raysan5/raylib/workflows/Linux/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3ALinux) [![macOS](https://github.com/raysan5/raylib/workflows/macOS/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AmacOS) -[![Android](https://github.com/raysan5/raylib/workflows/Android/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AAndroid) [![WebAssembly](https://github.com/raysan5/raylib/workflows/WebAssembly/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3AWebAssembly) [![CMakeBuilds](https://github.com/raysan5/raylib/workflows/CMakeBuilds/badge.svg)](https://github.com/raysan5/raylib/actions?query=workflow%3ACMakeBuilds) From 57f77c38589b3ee84b8fbeafef54fd307d41fc0c Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 19:39:57 +0100 Subject: [PATCH 0487/1350] Update qoi.h --- src/external/qoi.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/external/qoi.h b/src/external/qoi.h index 6734ac46e..f2800b0cc 100644 --- a/src/external/qoi.h +++ b/src/external/qoi.h @@ -594,7 +594,7 @@ void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) { int qoi_write(const char *filename, const void *data, const qoi_desc *desc) { FILE *f = fopen(filename, "wb"); - int size; + int size, err; void *encoded; if (!f) { @@ -608,10 +608,12 @@ int qoi_write(const char *filename, const void *data, const qoi_desc *desc) { } fwrite(encoded, 1, size, f); + fflush(f); + err = ferror(f); fclose(f); QOI_FREE(encoded); - return size; + return err ? 0 : size; } void *qoi_read(const char *filename, qoi_desc *desc, int channels) { @@ -625,11 +627,10 @@ void *qoi_read(const char *filename, qoi_desc *desc, int channels) { fseek(f, 0, SEEK_END); size = ftell(f); - if (size <= 0) { + if (size <= 0 || fseek(f, 0, SEEK_SET) != 0) { fclose(f); return NULL; } - fseek(f, 0, SEEK_SET); data = QOI_MALLOC(size); if (!data) { @@ -639,8 +640,7 @@ void *qoi_read(const char *filename, qoi_desc *desc, int channels) { bytes_read = fread(data, 1, size, f); fclose(f); - - pixels = qoi_decode(data, bytes_read, desc, channels); + pixels = (bytes_read != size) ? NULL : qoi_decode(data, bytes_read, desc, channels); QOI_FREE(data); return pixels; } From 6ebfc6023b24c297de3286a7fd8b0ffdcffc4c1a Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 19:40:00 +0100 Subject: [PATCH 0488/1350] Update rl_gputex.h --- src/external/rl_gputex.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index fb7b5e8de..2cbe59636 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -1,6 +1,6 @@ /********************************************************************************************** * -* rl_gputex - GPU compressed textures loading and saving +* rl_gputex v1.0 - GPU compressed textures loading and saving * * DESCRIPTION: * From 925978ffde371d5cc1b049d8cf7b861ebd6bb2b5 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 19:41:22 +0100 Subject: [PATCH 0489/1350] Update qoa.h --- src/external/qoa.h | 170 +++++++++++++++++++++++++++++++-------------- 1 file changed, 119 insertions(+), 51 deletions(-) diff --git a/src/external/qoa.h b/src/external/qoa.h index 59d90aded..fc62f4765 100644 --- a/src/external/qoa.h +++ b/src/external/qoa.h @@ -8,71 +8,96 @@ QOA - The "Quite OK Audio" format for fast, lossy audio compression -- Data Format -A QOA file has an 8 byte file header, followed by a number of frames. Each frame -consists of an 8 byte frame header, the current 8 byte en-/decoder state per -channel and 256 slices per channel. Each slice is 8 bytes wide and encodes 20 -samples of audio data. +QOA encodes pulse-code modulated (PCM) audio data with up to 255 channels, +sample rates from 1 up to 16777215 hertz and a bit depth of 16 bits. -Note that the last frame of a file may contain less than 256 slices per channel. -The last slice (per channel) in the last frame may contain less 20 samples, but -the slice will still be 8 bytes wide, with the unused samples zeroed out. +The compression method employed in QOA is lossy; it discards some information +from the uncompressed PCM data. For many types of audio signals this compression +is "transparent", i.e. the difference from the original file is often not +audible. -The samplerate and number of channels is only stated in the frame headers, but -not in the file header. A decoder may peek into the first frame of the file to -find these values. +QOA encodes 20 samples of 16 bit PCM data into slices of 64 bits. A single +sample therefore requires 3.2 bits of storage space, resulting in a 5x +compression (16 / 3.2). -In a valid QOA file all frames have the same number of channels and the same -samplerate. These restrictions may be relaxed for streaming. This remains to -be decided. +A QOA file consists of an 8 byte file header, followed by a number of frames. +Each frame contains an 8 byte frame header, the current 16 byte en-/decoder +state per channel and 256 slices per channel. Each slice is 8 bytes wide and +encodes 20 samples of audio data. -All values in a QOA file are BIG ENDIAN. Luckily, EVERYTHING in a QOA file, -including the headers, is 64 bit aligned, so it's possible to read files with -just a read_u64() that does the byte swapping if necessary. - -In pseudocode, the file layout is as follows: +All values, including the slices, are big endian. The file layout is as follows: struct { struct { - char magic[4]; // magic bytes 'qoaf' - uint32_t samples; // number of samples per channel in this file - } file_header; // = 64 bits + char magic[4]; // magic bytes "qoaf" + uint32_t samples; // samples per channel in this file + } file_header; struct { struct { - uint8_t num_channels; // number of channels + uint8_t num_channels; // no. of channels uint24_t samplerate; // samplerate in hz - uint16_t fsamples; // sample count per channel in this frame - uint16_t fsize; // frame size (including the frame header) - } frame_header; // = 64 bits + uint16_t fsamples; // samples per channel in this frame + uint16_t fsize; // frame size (includes this header) + } frame_header; struct { - int16_t history[4]; // = 64 bits - int16_t weights[4]; // = 64 bits + int16_t history[4]; // most recent last + int16_t weights[4]; // most recent last } lms_state[num_channels]; - qoa_slice_t slices[256][num_channels]; // = 64 bits each - } frames[samples * channels / qoa_max_framesize()]; -} qoa_file; + qoa_slice_t slices[256][num_channels]; -Wheras the 64bit qoa_slice_t is defined as follows: + } frames[ceil(samples / (256 * 20))]; +} qoa_file_t; + +Each `qoa_slice_t` contains a quantized scalefactor `sf_quant` and 20 quantized +residuals `qrNN`: .- QOA_SLICE -- 64 bits, 20 samples --------------------------/ /------------. | Byte[0] | Byte[1] | Byte[2] \ \ Byte[7] | | 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 | 7 6 5 / / 2 1 0 | |------------+--------+--------+--------+---------+---------+-\ \--+---------| -| sf_index | r00 | r01 | r02 | r03 | r04 | / / | r19 | +| sf_quant | qr00 | qr01 | qr02 | qr03 | qr04 | / / | qr19 | `-------------------------------------------------------------\ \------------` -`sf_index` defines the scalefactor to use for this slice as an index into the -qoa_scalefactor_tab[16] +Each frame except the last must contain exactly 256 slices per channel. The last +frame may contain between 1 .. 256 (inclusive) slices per channel. The last +slice (for each channel) in the last frame may contain less than 20 samples; the +slice still must be 8 bytes wide, with the unused samples zeroed out. -`r00`--`r19` are the residuals for the individual samples, divided by the -scalefactor and quantized by the qoa_quant_tab[]. +Channels are interleaved per slice. E.g. for 2 channel stereo: +slice[0] = L, slice[1] = R, slice[2] = L, slice[3] = R ... -In the decoder, a prediction of the next sample is computed by multiplying the -state (the last four output samples) with the predictor. The residual from the -slice is then dequantized using the qoa_dequant_tab[] and added to the -prediction. The result is clamped to int16 to form the final output sample. +A valid QOA file or stream must have at least one frame. Each frame must contain +at least one channel and one sample with a samplerate between 1 .. 16777215 +(inclusive). + +If the total number of samples is not known by the encoder, the samples in the +file header may be set to 0x00000000 to indicate that the encoder is +"streaming". In a streaming context, the samplerate and number of channels may +differ from frame to frame. For static files (those with samples set to a +non-zero value), each frame must have the same number of channels and same +samplerate. + +Note that this implementation of QOA only handles files with a known total +number of samples. + +A decoder should support at least 8 channels. The channel layout for channel +counts 1 .. 8 is: + + 1. Mono + 2. L, R + 3. L, R, C + 4. FL, FR, B/SL, B/SR + 5. FL, FR, C, B/SL, B/SR + 6. FL, FR, C, LFE, B/SL, B/SR + 7. FL, FR, C, LFE, B, SL, SR + 8. FL, FR, C, LFE, BL, BR, SL, SR + +QOA predicts each audio sample based on the previously decoded ones using a +"Sign-Sign Least Mean Squares Filter" (LMS). This prediction plus the +dequantized residual forms the final output sample. */ @@ -158,7 +183,7 @@ the higher end. Note that the residual zero is identical to the lowest positive value. This is mostly fine, since the qoa_div() function always rounds away from zero. */ -static int qoa_quant_tab[17] = { +static const int qoa_quant_tab[17] = { 7, 7, 7, 5, 5, 3, 3, 1, /* -8..-1 */ 0, /* 0 */ 0, 2, 2, 4, 4, 6, 6, 6 /* 1.. 8 */ @@ -169,13 +194,13 @@ static int qoa_quant_tab[17] = { less accurate at the higher end. In theory, the highest scalefactor that we would need to encode the highest 16bit residual is (2**16)/8 = 8192. However we rely on the LMS filter to predict samples accurately enough that a maximum -residual of one quarter of the 16 bit range is high sufficient. I.e. with the +residual of one quarter of the 16 bit range is sufficient. I.e. with the scalefactor 2048 times the quant range of 8 we can encode residuals up to 2**14. The scalefactor values are computed as: scalefactor_tab[s] <- round(pow(s + 1, 2.75)) */ -static int qoa_scalefactor_tab[16] = { +static const int qoa_scalefactor_tab[16] = { 1, 7, 21, 45, 84, 138, 211, 304, 421, 562, 731, 928, 1157, 1419, 1715, 2048 }; @@ -188,7 +213,7 @@ do this in .16 fixed point with integers, instead of floats. The reciprocal_tab is computed as: reciprocal_tab[s] <- ((1<<16) + scalefactor_tab[s] - 1) / scalefactor_tab[s] */ -static int qoa_reciprocal_tab[16] = { +static const int qoa_reciprocal_tab[16] = { 65536, 9363, 3121, 1457, 781, 475, 311, 216, 156, 117, 90, 71, 57, 47, 39, 32 }; @@ -200,9 +225,13 @@ Since qoa_div rounds away from the zero, the smallest entries are mapped to 3/4 instead of 1. The dequant_tab assumes the following dequantized values for each of the quant_tab indices and is computed as: float dqt[8] = {0.75, -0.75, 2.5, -2.5, 4.5, -4.5, 7, -7}; -dequant_tab[s][q] <- round(scalefactor_tab[s] * dqt[q]) */ +dequant_tab[s][q] <- round_ties_away_from_zero(scalefactor_tab[s] * dqt[q]) -static int qoa_dequant_tab[16][8] = { +The rounding employed here is "to nearest, ties away from zero", i.e. positive +and negative values are treated symmetrically. +*/ + +static const int qoa_dequant_tab[16][8] = { { 1, -1, 3, -3, 5, -5, 7, -7}, { 5, -5, 18, -18, 32, -32, 49, -49}, { 16, -16, 53, -53, 95, -95, 147, -147}, @@ -270,7 +299,21 @@ static inline int qoa_div(int v, int scalefactor) { } static inline int qoa_clamp(int v, int min, int max) { - return (v < min) ? min : (v > max) ? max : v; + if (v < min) { return min; } + if (v > max) { return max; } + return v; +} + +/* This specialized clamp function for the signed 16 bit range improves decode +performance quite a bit. The extra if() statement works nicely with the CPUs +branch prediction as this branch is rarely taken. */ + +static inline int qoa_clamp_s16(int v) { + if ((unsigned int)(v + 32768) > 65535) { + if (v < -32768) { return -32768; } + if (v > 32767) { return 32767; } + } + return v; } static inline qoa_uint64_t qoa_read_u64(const unsigned char *bytes, unsigned int *p) { @@ -312,6 +355,7 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned unsigned int p = 0; unsigned int slices = (frame_len + QOA_SLICE_LEN - 1) / QOA_SLICE_LEN; unsigned int frame_size = QOA_FRAME_SIZE(channels, slices); + int prev_scalefactor[QOA_MAX_CHANNELS] = {0}; /* Write the frame header */ qoa_write_u64(( @@ -321,8 +365,24 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned (qoa_uint64_t)frame_size ), bytes, &p); - /* Write the current LMS state */ + for (int c = 0; c < channels; c++) { + /* If the weights have grown too large, reset them to 0. This may happen + with certain high-frequency sounds. This is a last resort and will + introduce quite a bit of noise, but should at least prevent pops/clicks */ + int weights_sum = + qoa->lms[c].weights[0] * qoa->lms[c].weights[0] + + qoa->lms[c].weights[1] * qoa->lms[c].weights[1] + + qoa->lms[c].weights[2] * qoa->lms[c].weights[2] + + qoa->lms[c].weights[3] * qoa->lms[c].weights[3]; + if (weights_sum > 0x2fffffff) { + qoa->lms[c].weights[0] = 0; + qoa->lms[c].weights[1] = 0; + qoa->lms[c].weights[2] = 0; + qoa->lms[c].weights[3] = 0; + } + + /* Write the current LMS state */ qoa_uint64_t weights = 0; qoa_uint64_t history = 0; for (int i = 0; i < QOA_LMS_LEN; i++) { @@ -348,8 +408,13 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned qoa_uint64_t best_error = -1; qoa_uint64_t best_slice; qoa_lms_t best_lms; + int best_scalefactor; - for (int scalefactor = 0; scalefactor < 16; scalefactor++) { + for (int sfi = 0; sfi < 16; sfi++) { + /* There is a strong correlation between the scalefactors of + neighboring slices. As an optimization, start testing + the best scalefactor of the previous slice first. */ + int scalefactor = (sfi + prev_scalefactor[c]) % 16; /* We have to reset the LMS state to the last known good one before trying each scalefactor, as each pass updates the LMS @@ -367,7 +432,7 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned int clamped = qoa_clamp(scaled, -8, 8); int quantized = qoa_quant_tab[clamped + 8]; int dequantized = qoa_dequant_tab[scalefactor][quantized]; - int reconstructed = qoa_clamp(predicted + dequantized, -32768, 32767); + int reconstructed = qoa_clamp_s16(predicted + dequantized); long long error = (sample - reconstructed); current_error += error * error; @@ -383,9 +448,12 @@ unsigned int qoa_encode_frame(const short *sample_data, qoa_desc *qoa, unsigned best_error = current_error; best_slice = slice; best_lms = lms; + best_scalefactor = scalefactor; } } + prev_scalefactor[c] = best_scalefactor; + qoa->lms[c] = best_lms; #ifdef QOA_RECORD_TOTAL_ERROR qoa->error += best_error; @@ -553,7 +621,7 @@ unsigned int qoa_decode_frame(const unsigned char *bytes, unsigned int size, qoa int predicted = qoa_lms_predict(&qoa->lms[c]); int quantized = (slice >> 57) & 0x7; int dequantized = qoa_dequant_tab[scalefactor][quantized]; - int reconstructed = qoa_clamp(predicted + dequantized, -32768, 32767); + int reconstructed = qoa_clamp_s16(predicted + dequantized); sample_data[si] = reconstructed; slice <<= 3; From 2252f747b734e3f5f9b78a46aa1c8e667c44ab0c Mon Sep 17 00:00:00 2001 From: JupiterRider <60042618+JupiterRider@users.noreply.github.com> Date: Sun, 5 Nov 2023 19:42:56 +0100 Subject: [PATCH 0490/1350] Update Makefile (#3509) --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 7bcece7c8..2ee0c5a06 100644 --- a/src/Makefile +++ b/src/Makefile @@ -602,7 +602,7 @@ ifeq ($(PLATFORM),PLATFORM_WEB) @echo "raylib library generated (lib$(RAYLIB_LIB_NAME).a)!" else ifeq ($(RAYLIB_LIBTYPE),SHARED) - ifeq ($(PLATFORM),PLATFORM_DESKTOP) + ifeq ($(PLATFORM),$(filter $(PLATFORM),PLATFORM_DESKTOP PLATFORM_DESKTOP_SDL)) ifeq ($(PLATFORM_OS),WINDOWS) # NOTE: Linking with provided resource file $(CC) -shared -o $(RAYLIB_RELEASE_PATH)/$(RAYLIB_LIB_NAME).dll $(OBJS) $(RAYLIB_RES_FILE) $(LDFLAGS) $(LDLIBS) From 343e92322bac845511651dc6e15a2e16fdeb94c1 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 19:46:25 +0100 Subject: [PATCH 0491/1350] Update README.md --- README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 4604abff6..a88e0f5ce 100644 --- a/README.md +++ b/README.md @@ -38,20 +38,20 @@ features - **NO external dependencies**, all required libraries are [bundled into raylib](https://github.com/raysan5/raylib/tree/master/src/external) - Multiple platforms supported: **Windows, Linux, MacOS, RPI, Android, HTML5... and more!** - Written in plain C code (C99) using PascalCase/camelCase notation - - Hardware accelerated with OpenGL (**1.1, 2.1, 3.3, 4.3 or ES 2.0**) + - Hardware accelerated with OpenGL (**1.1, 2.1, 3.3, 4.3, ES 2.0, ES 3.0**) - **Unique OpenGL abstraction layer** (usable as standalone module): [rlgl](https://github.com/raysan5/raylib/blob/master/src/rlgl.h) - - Multiple **Fonts** formats supported (TTF, Image fonts, AngelCode fonts) + - Multiple **Fonts** formats supported (TTF, OTF, Image fonts, AngelCode fonts) - Multiple texture formats supported, including **compressed formats** (DXT, ETC, ASTC) - **Full 3D support**, including 3D Shapes, Models, Billboards, Heightmaps and more! - Flexible Materials system, supporting classic maps and **PBR maps** - - **Animated 3D models** supported (skeletal bones animation) (IQM) - - Shaders support, including model and **postprocessing** shaders. + - **Animated 3D models** supported (skeletal bones animation) (IQM, M3D, glTF) + - Shaders support, including model shaders and **postprocessing** shaders - **Powerful math module** for Vector, Matrix and Quaternion operations: [raymath](https://github.com/raysan5/raylib/blob/master/src/raymath.h) - - Audio loading and playing with streaming support (WAV, OGG, MP3, FLAC, XM, MOD) + - Audio loading and playing with streaming support (WAV, QOA, OGG, MP3, FLAC, XM, MOD) - **VR stereo rendering** support with configurable HMD device parameters - - Huge examples collection with [+120 code examples](https://github.com/raysan5/raylib/tree/master/examples)! - - Bindings to [+60 programming languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)! - - **Free and open source**. + - Huge examples collection with [+130 code examples](https://github.com/raysan5/raylib/tree/master/examples)! + - Bindings to [+70 programming languages](https://github.com/raysan5/raylib/blob/master/BINDINGS.md)! + - **Free and open source** basic example -------------- From 8b3c18de545eb63b7dc2d08621ba83a215a07463 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 5 Nov 2023 20:11:14 +0100 Subject: [PATCH 0492/1350] Updated GLFW for 64bit --- examples/others/external/include/GLFW/glfw3.h | 1064 +++++++++++------ .../external/include/GLFW/glfw3native.h | 226 ++-- examples/others/external/lib/libglfw3.a | Bin 269591 -> 290550 bytes 3 files changed, 860 insertions(+), 430 deletions(-) diff --git a/examples/others/external/include/GLFW/glfw3.h b/examples/others/external/include/GLFW/glfw3.h index 990fe3f7f..31b201ae5 100644 --- a/examples/others/external/include/GLFW/glfw3.h +++ b/examples/others/external/include/GLFW/glfw3.h @@ -3,7 +3,7 @@ * A library for OpenGL, window and input *------------------------------------------------------------------------ * Copyright (c) 2002-2006 Marcus Geelnard - * Copyright (c) 2006-2016 Camilla Löwy + * Copyright (c) 2006-2019 Camilla Löwy * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages @@ -52,7 +52,7 @@ extern "C" { * This is the reference documentation for OpenGL and OpenGL ES context related * functions. For more task-oriented information, see the @ref context_guide. */ -/*! @defgroup vulkan Vulkan reference +/*! @defgroup vulkan Vulkan support reference * @brief Functions and types related to Vulkan. * * This is the reference documentation for Vulkan related functions and types. @@ -96,11 +96,30 @@ extern "C" { #define _WIN32 #endif /* _WIN32 */ +/* Include because most Windows GLU headers need wchar_t and + * the macOS OpenGL header blocks the definition of ptrdiff_t by glext.h. + * Include it unconditionally to avoid surprising side-effects. + */ +#include + +/* Include because it is needed by Vulkan and related functions. + * Include it unconditionally to avoid surprising side-effects. + */ +#include + +#if defined(GLFW_INCLUDE_VULKAN) + #include +#endif /* Vulkan header */ + +/* The Vulkan header may have indirectly included windows.h (because of + * VK_USE_PLATFORM_WIN32_KHR) so we offer our replacement symbols after it. + */ + /* It is customary to use APIENTRY for OpenGL function pointer declarations on * all platforms. Additionally, the Windows OpenGL header needs APIENTRY. */ -#ifndef APIENTRY - #ifdef _WIN32 +#if !defined(APIENTRY) + #if defined(_WIN32) #define APIENTRY __stdcall #else #define APIENTRY @@ -122,17 +141,6 @@ extern "C" { #define GLFW_CALLBACK_DEFINED #endif /* CALLBACK */ -/* Include because most Windows GLU headers need wchar_t and - * the macOS OpenGL header blocks the definition of ptrdiff_t by glext.h. - * Include it unconditionally to avoid surprising side-effects. - */ -#include - -/* Include because it is needed by Vulkan and related functions. - * Include it unconditionally to avoid surprising side-effects. - */ -#include - /* Include the chosen OpenGL or OpenGL ES headers. */ #if defined(GLFW_INCLUDE_ES1) @@ -182,10 +190,44 @@ extern "C" { #else /*__APPLE__*/ #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif #endif /*__APPLE__*/ -#elif !defined(GLFW_INCLUDE_NONE) +#elif defined(GLFW_INCLUDE_GLU) + + #if defined(__APPLE__) + + #if defined(GLFW_INCLUDE_GLU) + #include + #endif + + #else /*__APPLE__*/ + + #if defined(GLFW_INCLUDE_GLU) + #include + #endif + + #endif /*__APPLE__*/ + +#elif !defined(GLFW_INCLUDE_NONE) && \ + !defined(__gl_h_) && \ + !defined(__gles1_gl_h_) && \ + !defined(__gles2_gl2_h_) && \ + !defined(__gles2_gl3_h_) && \ + !defined(__gles2_gl31_h_) && \ + !defined(__gles2_gl32_h_) && \ + !defined(__gl_glcorearb_h_) && \ + !defined(__gl2_h_) /*legacy*/ && \ + !defined(__gl3_h_) /*legacy*/ && \ + !defined(__gl31_h_) /*legacy*/ && \ + !defined(__gl32_h_) /*legacy*/ && \ + !defined(__glcorearb_h_) /*legacy*/ && \ + !defined(__GL_H__) /*non-standard*/ && \ + !defined(__gltypes_h_) /*non-standard*/ && \ + !defined(__glee_h_) /*non-standard*/ #if defined(__APPLE__) @@ -193,9 +235,6 @@ extern "C" { #define GL_GLEXT_LEGACY #endif #include - #if defined(GLFW_INCLUDE_GLU) - #include - #endif #else /*__APPLE__*/ @@ -203,18 +242,11 @@ extern "C" { #if defined(GLFW_INCLUDE_GLEXT) #include #endif - #if defined(GLFW_INCLUDE_GLU) - #include - #endif #endif /*__APPLE__*/ #endif /* OpenGL and OpenGL ES headers */ -#if defined(GLFW_INCLUDE_VULKAN) - #include -#endif /* Vulkan header */ - #if defined(GLFW_DLL) && defined(_GLFW_BUILD_DLL) /* GLFW_DLL must be defined by applications that are linking against the DLL * version of the GLFW library. _GLFW_BUILD_DLL is defined by the GLFW @@ -230,13 +262,12 @@ extern "C" { /* We are building GLFW as a Win32 DLL */ #define GLFWAPI __declspec(dllexport) #elif defined(_WIN32) && defined(GLFW_DLL) - /* We are calling GLFW as a Win32 DLL */ + /* We are calling a GLFW Win32 DLL */ #define GLFWAPI __declspec(dllimport) #elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL) - /* We are building GLFW as a shared / dynamic library */ + /* We are building GLFW as a Unix shared library */ #define GLFWAPI __attribute__((visibility("default"))) #else - /* We are building or calling GLFW as a static library */ #define GLFWAPI #endif @@ -247,45 +278,47 @@ extern "C" { /*! @name GLFW version macros * @{ */ -/*! @brief The major version number of the GLFW library. +/*! @brief The major version number of the GLFW header. * - * This is incremented when the API is changed in non-compatible ways. + * The major version number of the GLFW header. This is incremented when the + * API is changed in non-compatible ways. * @ingroup init */ #define GLFW_VERSION_MAJOR 3 -/*! @brief The minor version number of the GLFW library. +/*! @brief The minor version number of the GLFW header. * - * This is incremented when features are added to the API but it remains - * backward-compatible. + * The minor version number of the GLFW header. This is incremented when + * features are added to the API but it remains backward-compatible. * @ingroup init */ #define GLFW_VERSION_MINOR 3 -/*! @brief The revision number of the GLFW library. +/*! @brief The revision number of the GLFW header. * - * This is incremented when a bug fix release is made that does not contain any - * API changes. + * The revision number of the GLFW header. This is incremented when a bug fix + * release is made that does not contain any API changes. * @ingroup init */ -#define GLFW_VERSION_REVISION 0 +#define GLFW_VERSION_REVISION 8 /*! @} */ -/*! @name Boolean values - * @{ */ /*! @brief One. * - * One. Seriously. You don't _need_ to use this symbol in your code. It's - * semantic sugar for the number 1. You can also use `1` or `true` or `_True` - * or `GL_TRUE` or whatever you want. + * This is only semantic sugar for the number 1. You can instead use `1` or + * `true` or `_True` or `GL_TRUE` or `VK_TRUE` or anything else that is equal + * to one. + * + * @ingroup init */ #define GLFW_TRUE 1 /*! @brief Zero. * - * Zero. Seriously. You don't _need_ to use this symbol in your code. It's - * semantic sugar for the number 0. You can also use `0` or `false` or - * `_False` or `GL_FALSE` or whatever you want. + * This is only semantic sugar for the number 0. You can instead use `0` or + * `false` or `_False` or `GL_FALSE` or `VK_FALSE` or anything else that is + * equal to zero. + * + * @ingroup init */ #define GLFW_FALSE 0 -/*! @} */ /*! @name Key and button actions * @{ */ @@ -313,6 +346,7 @@ extern "C" { /*! @} */ /*! @defgroup hat_state Joystick hat states + * @brief Joystick hat states. * * See [joystick hat input](@ref joystick_hat) for how these are used. * @@ -915,70 +949,87 @@ extern "C" { #define GLFW_CLIENT_API 0x00022001 /*! @brief Context client API major version hint and attribute. * - * Context client API major version [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Context client API major version [hint](@ref GLFW_CONTEXT_VERSION_MAJOR_hint) + * and [attribute](@ref GLFW_CONTEXT_VERSION_MAJOR_attrib). */ #define GLFW_CONTEXT_VERSION_MAJOR 0x00022002 /*! @brief Context client API minor version hint and attribute. * - * Context client API minor version [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Context client API minor version [hint](@ref GLFW_CONTEXT_VERSION_MINOR_hint) + * and [attribute](@ref GLFW_CONTEXT_VERSION_MINOR_attrib). */ #define GLFW_CONTEXT_VERSION_MINOR 0x00022003 -/*! @brief Context client API revision number hint and attribute. +/*! @brief Context client API revision number attribute. * - * Context client API revision number [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Context client API revision number + * [attribute](@ref GLFW_CONTEXT_REVISION_attrib). */ #define GLFW_CONTEXT_REVISION 0x00022004 /*! @brief Context robustness hint and attribute. * - * Context client API revision number [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Context client API revision number [hint](@ref GLFW_CONTEXT_ROBUSTNESS_hint) + * and [attribute](@ref GLFW_CONTEXT_ROBUSTNESS_attrib). */ #define GLFW_CONTEXT_ROBUSTNESS 0x00022005 /*! @brief OpenGL forward-compatibility hint and attribute. * - * OpenGL forward-compatibility [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * OpenGL forward-compatibility [hint](@ref GLFW_OPENGL_FORWARD_COMPAT_hint) + * and [attribute](@ref GLFW_OPENGL_FORWARD_COMPAT_attrib). */ #define GLFW_OPENGL_FORWARD_COMPAT 0x00022006 -/*! @brief OpenGL debug context hint and attribute. +/*! @brief Debug mode context hint and attribute. * - * OpenGL debug context [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Debug mode context [hint](@ref GLFW_OPENGL_DEBUG_CONTEXT_hint) and + * [attribute](@ref GLFW_OPENGL_DEBUG_CONTEXT_attrib). */ #define GLFW_OPENGL_DEBUG_CONTEXT 0x00022007 /*! @brief OpenGL profile hint and attribute. * - * OpenGL profile [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * OpenGL profile [hint](@ref GLFW_OPENGL_PROFILE_hint) and + * [attribute](@ref GLFW_OPENGL_PROFILE_attrib). */ #define GLFW_OPENGL_PROFILE 0x00022008 /*! @brief Context flush-on-release hint and attribute. * - * Context flush-on-release [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Context flush-on-release [hint](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_hint) and + * [attribute](@ref GLFW_CONTEXT_RELEASE_BEHAVIOR_attrib). */ #define GLFW_CONTEXT_RELEASE_BEHAVIOR 0x00022009 /*! @brief Context error suppression hint and attribute. * - * Context error suppression [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Context error suppression [hint](@ref GLFW_CONTEXT_NO_ERROR_hint) and + * [attribute](@ref GLFW_CONTEXT_NO_ERROR_attrib). */ #define GLFW_CONTEXT_NO_ERROR 0x0002200A /*! @brief Context creation API hint and attribute. * - * Context creation API [hint](@ref GLFW_CLIENT_API_hint) and - * [attribute](@ref GLFW_CLIENT_API_attrib). + * Context creation API [hint](@ref GLFW_CONTEXT_CREATION_API_hint) and + * [attribute](@ref GLFW_CONTEXT_CREATION_API_attrib). */ #define GLFW_CONTEXT_CREATION_API 0x0002200B - +/*! @brief Window content area scaling window + * [window hint](@ref GLFW_SCALE_TO_MONITOR). + */ +#define GLFW_SCALE_TO_MONITOR 0x0002200C +/*! @brief macOS specific + * [window hint](@ref GLFW_COCOA_RETINA_FRAMEBUFFER_hint). + */ #define GLFW_COCOA_RETINA_FRAMEBUFFER 0x00023001 +/*! @brief macOS specific + * [window hint](@ref GLFW_COCOA_FRAME_NAME_hint). + */ #define GLFW_COCOA_FRAME_NAME 0x00023002 +/*! @brief macOS specific + * [window hint](@ref GLFW_COCOA_GRAPHICS_SWITCHING_hint). + */ #define GLFW_COCOA_GRAPHICS_SWITCHING 0x00023003 - +/*! @brief X11 specific + * [window hint](@ref GLFW_X11_CLASS_NAME_hint). + */ #define GLFW_X11_CLASS_NAME 0x00024001 +/*! @brief X11 specific + * [window hint](@ref GLFW_X11_CLASS_NAME_hint). + */ #define GLFW_X11_INSTANCE_NAME 0x00024002 /*! @} */ @@ -998,6 +1049,7 @@ extern "C" { #define GLFW_STICKY_KEYS 0x00033002 #define GLFW_STICKY_MOUSE_BUTTONS 0x00033003 #define GLFW_LOCK_KEY_MODS 0x00033004 +#define GLFW_RAW_MOUSE_MOTION 0x00033005 #define GLFW_CURSOR_NORMAL 0x00034001 #define GLFW_CURSOR_HIDDEN 0x00034002 @@ -1056,9 +1108,20 @@ extern "C" { /*! @addtogroup init * @{ */ +/*! @brief Joystick hat buttons init hint. + * + * Joystick hat buttons [init hint](@ref GLFW_JOYSTICK_HAT_BUTTONS). + */ #define GLFW_JOYSTICK_HAT_BUTTONS 0x00050001 - +/*! @brief macOS specific init hint. + * + * macOS specific [init hint](@ref GLFW_COCOA_CHDIR_RESOURCES_hint). + */ #define GLFW_COCOA_CHDIR_RESOURCES 0x00051001 +/*! @brief macOS specific init hint. + * + * macOS specific [init hint](@ref GLFW_COCOA_MENUBAR_hint). + */ #define GLFW_COCOA_MENUBAR 0x00051002 /*! @} */ @@ -1129,17 +1192,25 @@ typedef struct GLFWwindow GLFWwindow; * * @since Added in version 3.1. * - * @ingroup cursor + * @ingroup input */ typedef struct GLFWcursor GLFWcursor; -/*! @brief The function signature for error callbacks. +/*! @brief The function pointer type for error callbacks. * - * This is the function signature for error callback functions. + * This is the function pointer type for error callbacks. An error callback + * function has the following signature: + * @code + * void callback_name(int error_code, const char* description) + * @endcode * - * @param[in] error An [error code](@ref errors). + * @param[in] error_code An [error code](@ref errors). Future releases may add + * more error codes. * @param[in] description A UTF-8 encoded string describing the error. * + * @pointer_lifetime The error description string is valid until the callback + * function returns. + * * @sa @ref error_handling * @sa @ref glfwSetErrorCallback * @@ -1147,17 +1218,21 @@ typedef struct GLFWcursor GLFWcursor; * * @ingroup init */ -typedef void (* GLFWerrorfun)(int,const char*); +typedef void (* GLFWerrorfun)(int error_code, const char* description); -/*! @brief The function signature for window position callbacks. +/*! @brief The function pointer type for window position callbacks. * - * This is the function signature for window position callback functions. + * This is the function pointer type for window position callbacks. A window + * position callback function has the following signature: + * @code + * void callback_name(GLFWwindow* window, int xpos, int ypos) + * @endcode * * @param[in] window The window that was moved. * @param[in] xpos The new x-coordinate, in screen coordinates, of the - * upper-left corner of the client area of the window. + * upper-left corner of the content area of the window. * @param[in] ypos The new y-coordinate, in screen coordinates, of the - * upper-left corner of the client area of the window. + * upper-left corner of the content area of the window. * * @sa @ref window_pos * @sa @ref glfwSetWindowPosCallback @@ -1166,11 +1241,15 @@ typedef void (* GLFWerrorfun)(int,const char*); * * @ingroup window */ -typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int); +typedef void (* GLFWwindowposfun)(GLFWwindow* window, int xpos, int ypos); -/*! @brief The function signature for window resize callbacks. +/*! @brief The function pointer type for window size callbacks. * - * This is the function signature for window size callback functions. + * This is the function pointer type for window size callbacks. A window size + * callback function has the following signature: + * @code + * void callback_name(GLFWwindow* window, int width, int height) + * @endcode * * @param[in] window The window that was resized. * @param[in] width The new width, in screen coordinates, of the window. @@ -1184,11 +1263,15 @@ typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int); * * @ingroup window */ -typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int); +typedef void (* GLFWwindowsizefun)(GLFWwindow* window, int width, int height); -/*! @brief The function signature for window close callbacks. +/*! @brief The function pointer type for window close callbacks. * - * This is the function signature for window close callback functions. + * This is the function pointer type for window close callbacks. A window + * close callback function has the following signature: + * @code + * void function_name(GLFWwindow* window) + * @endcode * * @param[in] window The window that the user attempted to close. * @@ -1200,11 +1283,15 @@ typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int); * * @ingroup window */ -typedef void (* GLFWwindowclosefun)(GLFWwindow*); +typedef void (* GLFWwindowclosefun)(GLFWwindow* window); -/*! @brief The function signature for window content refresh callbacks. +/*! @brief The function pointer type for window content refresh callbacks. * - * This is the function signature for window refresh callback functions. + * This is the function pointer type for window content refresh callbacks. + * A window content refresh callback function has the following signature: + * @code + * void function_name(GLFWwindow* window); + * @endcode * * @param[in] window The window whose content needs to be refreshed. * @@ -1216,11 +1303,15 @@ typedef void (* GLFWwindowclosefun)(GLFWwindow*); * * @ingroup window */ -typedef void (* GLFWwindowrefreshfun)(GLFWwindow*); +typedef void (* GLFWwindowrefreshfun)(GLFWwindow* window); -/*! @brief The function signature for window focus/defocus callbacks. +/*! @brief The function pointer type for window focus callbacks. * - * This is the function signature for window focus callback functions. + * This is the function pointer type for window focus callbacks. A window + * focus callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int focused) + * @endcode * * @param[in] window The window that gained or lost input focus. * @param[in] focused `GLFW_TRUE` if the window was given input focus, or @@ -1233,12 +1324,15 @@ typedef void (* GLFWwindowrefreshfun)(GLFWwindow*); * * @ingroup window */ -typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int); +typedef void (* GLFWwindowfocusfun)(GLFWwindow* window, int focused); -/*! @brief The function signature for window iconify/restore callbacks. +/*! @brief The function pointer type for window iconify callbacks. * - * This is the function signature for window iconify/restore callback - * functions. + * This is the function pointer type for window iconify callbacks. A window + * iconify callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int iconified) + * @endcode * * @param[in] window The window that was iconified or restored. * @param[in] iconified `GLFW_TRUE` if the window was iconified, or @@ -1251,15 +1345,18 @@ typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int); * * @ingroup window */ -typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); +typedef void (* GLFWwindowiconifyfun)(GLFWwindow* window, int iconified); -/*! @brief The function signature for window maximize/restore callbacks. +/*! @brief The function pointer type for window maximize callbacks. * - * This is the function signature for window maximize/restore callback - * functions. + * This is the function pointer type for window maximize callbacks. A window + * maximize callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int maximized) + * @endcode * * @param[in] window The window that was maximized or restored. - * @param[in] iconified `GLFW_TRUE` if the window was maximized, or + * @param[in] maximized `GLFW_TRUE` if the window was maximized, or * `GLFW_FALSE` if it was restored. * * @sa @ref window_maximize @@ -1269,12 +1366,15 @@ typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); * * @ingroup window */ -typedef void (* GLFWwindowmaximizefun)(GLFWwindow*,int); +typedef void (* GLFWwindowmaximizefun)(GLFWwindow* window, int maximized); -/*! @brief The function signature for framebuffer resize callbacks. +/*! @brief The function pointer type for framebuffer size callbacks. * - * This is the function signature for framebuffer resize callback - * functions. + * This is the function pointer type for framebuffer size callbacks. + * A framebuffer size callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int width, int height) + * @endcode * * @param[in] window The window whose framebuffer was resized. * @param[in] width The new width, in pixels, of the framebuffer. @@ -1287,12 +1387,15 @@ typedef void (* GLFWwindowmaximizefun)(GLFWwindow*,int); * * @ingroup window */ -typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int); +typedef void (* GLFWframebuffersizefun)(GLFWwindow* window, int width, int height); -/*! @brief The function signature for window content scale callbacks. +/*! @brief The function pointer type for window content scale callbacks. * - * This is the function signature for window content scale callback - * functions. + * This is the function pointer type for window content scale callbacks. + * A window content scale callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, float xscale, float yscale) + * @endcode * * @param[in] window The window whose content scale changed. * @param[in] xscale The new x-axis content scale of the window. @@ -1305,16 +1408,21 @@ typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int); * * @ingroup window */ -typedef void (* GLFWwindowcontentscalefun)(GLFWwindow*,float,float); +typedef void (* GLFWwindowcontentscalefun)(GLFWwindow* window, float xscale, float yscale); -/*! @brief The function signature for mouse button callbacks. +/*! @brief The function pointer type for mouse button callbacks. * - * This is the function signature for mouse button callback functions. + * This is the function pointer type for mouse button callback functions. + * A mouse button callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int button, int action, int mods) + * @endcode * * @param[in] window The window that received the event. * @param[in] button The [mouse button](@ref buttons) that was pressed or * released. - * @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`. + * @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`. Future releases + * may add more actions. * @param[in] mods Bit field describing which [modifier keys](@ref mods) were * held down. * @@ -1326,17 +1434,21 @@ typedef void (* GLFWwindowcontentscalefun)(GLFWwindow*,float,float); * * @ingroup input */ -typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int); +typedef void (* GLFWmousebuttonfun)(GLFWwindow* window, int button, int action, int mods); -/*! @brief The function signature for cursor position callbacks. +/*! @brief The function pointer type for cursor position callbacks. * - * This is the function signature for cursor position callback functions. + * This is the function pointer type for cursor position callbacks. A cursor + * position callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, double xpos, double ypos); + * @endcode * * @param[in] window The window that received the event. * @param[in] xpos The new cursor x-coordinate, relative to the left edge of - * the client area. + * the content area. * @param[in] ypos The new cursor y-coordinate, relative to the top edge of the - * client area. + * content area. * * @sa @ref cursor_pos * @sa @ref glfwSetCursorPosCallback @@ -1345,14 +1457,18 @@ typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int); * * @ingroup input */ -typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double); +typedef void (* GLFWcursorposfun)(GLFWwindow* window, double xpos, double ypos); -/*! @brief The function signature for cursor enter/leave callbacks. +/*! @brief The function pointer type for cursor enter/leave callbacks. * - * This is the function signature for cursor enter/leave callback functions. + * This is the function pointer type for cursor enter/leave callbacks. + * A cursor enter/leave callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int entered) + * @endcode * * @param[in] window The window that received the event. - * @param[in] entered `GLFW_TRUE` if the cursor entered the window's client + * @param[in] entered `GLFW_TRUE` if the cursor entered the window's content * area, or `GLFW_FALSE` if it left it. * * @sa @ref cursor_enter @@ -1362,11 +1478,15 @@ typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double); * * @ingroup input */ -typedef void (* GLFWcursorenterfun)(GLFWwindow*,int); +typedef void (* GLFWcursorenterfun)(GLFWwindow* window, int entered); -/*! @brief The function signature for scroll callbacks. +/*! @brief The function pointer type for scroll callbacks. * - * This is the function signature for scroll callback functions. + * This is the function pointer type for scroll callbacks. A scroll callback + * function has the following signature: + * @code + * void function_name(GLFWwindow* window, double xoffset, double yoffset) + * @endcode * * @param[in] window The window that received the event. * @param[in] xoffset The scroll offset along the x-axis. @@ -1379,16 +1499,21 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int); * * @ingroup input */ -typedef void (* GLFWscrollfun)(GLFWwindow*,double,double); +typedef void (* GLFWscrollfun)(GLFWwindow* window, double xoffset, double yoffset); -/*! @brief The function signature for keyboard key callbacks. +/*! @brief The function pointer type for keyboard key callbacks. * - * This is the function signature for keyboard key callback functions. + * This is the function pointer type for keyboard key callbacks. A keyboard + * key callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int key, int scancode, int action, int mods) + * @endcode * * @param[in] window The window that received the event. * @param[in] key The [keyboard key](@ref keys) that was pressed or released. * @param[in] scancode The system-specific scancode of the key. - * @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`. + * @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`. Future + * releases may add more actions. * @param[in] mods Bit field describing which [modifier keys](@ref mods) were * held down. * @@ -1400,11 +1525,15 @@ typedef void (* GLFWscrollfun)(GLFWwindow*,double,double); * * @ingroup input */ -typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int); +typedef void (* GLFWkeyfun)(GLFWwindow* window, int key, int scancode, int action, int mods); -/*! @brief The function signature for Unicode character callbacks. +/*! @brief The function pointer type for Unicode character callbacks. * - * This is the function signature for Unicode character callback functions. + * This is the function pointer type for Unicode character callbacks. + * A Unicode character callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint) + * @endcode * * @param[in] window The window that received the event. * @param[in] codepoint The Unicode code point of the character. @@ -1417,14 +1546,18 @@ typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int); * * @ingroup input */ -typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int); +typedef void (* GLFWcharfun)(GLFWwindow* window, unsigned int codepoint); -/*! @brief The function signature for Unicode character with modifiers +/*! @brief The function pointer type for Unicode character with modifiers * callbacks. * - * This is the function signature for Unicode character with modifiers callback - * functions. It is called for each input character, regardless of what - * modifier keys are held down. + * This is the function pointer type for Unicode character with modifiers + * callbacks. It is called for each input character, regardless of what + * modifier keys are held down. A Unicode character with modifiers callback + * function has the following signature: + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint, int mods) + * @endcode * * @param[in] window The window that received the event. * @param[in] codepoint The Unicode code point of the character. @@ -1440,16 +1573,23 @@ typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int); * * @ingroup input */ -typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int); +typedef void (* GLFWcharmodsfun)(GLFWwindow* window, unsigned int codepoint, int mods); -/*! @brief The function signature for file drop callbacks. +/*! @brief The function pointer type for path drop callbacks. * - * This is the function signature for file drop callbacks. + * This is the function pointer type for path drop callbacks. A path drop + * callback function has the following signature: + * @code + * void function_name(GLFWwindow* window, int path_count, const char* paths[]) + * @endcode * * @param[in] window The window that received the event. - * @param[in] count The number of dropped files. + * @param[in] path_count The number of dropped paths. * @param[in] paths The UTF-8 encoded file and/or directory path names. * + * @pointer_lifetime The path array and its strings are valid until the + * callback function returns. + * * @sa @ref path_drop * @sa @ref glfwSetDropCallback * @@ -1457,15 +1597,19 @@ typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int); * * @ingroup input */ -typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**); +typedef void (* GLFWdropfun)(GLFWwindow* window, int path_count, const char* paths[]); -/*! @brief The function signature for monitor configuration callbacks. +/*! @brief The function pointer type for monitor configuration callbacks. * - * This is the function signature for monitor configuration callback functions. + * This is the function pointer type for monitor configuration callbacks. + * A monitor callback function has the following signature: + * @code + * void function_name(GLFWmonitor* monitor, int event) + * @endcode * * @param[in] monitor The monitor that was connected or disconnected. - * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Remaining - * values reserved for future use. + * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Future + * releases may add more events. * * @sa @ref monitor_event * @sa @ref glfwSetMonitorCallback @@ -1474,16 +1618,19 @@ typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**); * * @ingroup monitor */ -typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); +typedef void (* GLFWmonitorfun)(GLFWmonitor* monitor, int event); -/*! @brief The function signature for joystick configuration callbacks. +/*! @brief The function pointer type for joystick configuration callbacks. * - * This is the function signature for joystick configuration callback - * functions. + * This is the function pointer type for joystick configuration callbacks. + * A joystick configuration callback function has the following signature: + * @code + * void function_name(int jid, int event) + * @endcode * * @param[in] jid The joystick that was connected or disconnected. - * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Remaining - * values reserved for future use. + * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. Future + * releases may add more events. * * @sa @ref joystick_event * @sa @ref glfwSetJoystickCallback @@ -1492,7 +1639,7 @@ typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); * * @ingroup input */ -typedef void (* GLFWjoystickfun)(int,int); +typedef void (* GLFWjoystickfun)(int jid, int event); /*! @brief Video mode type. * @@ -1567,6 +1714,8 @@ typedef struct GLFWgammaramp * * @since Added in version 2.1. * @glfw3 Removed format and bytes-per-pixel members. + * + * @ingroup window */ typedef struct GLFWimage { @@ -1589,6 +1738,8 @@ typedef struct GLFWimage * @sa @ref glfwGetGamepadState * * @since Added in version 3.3. + * + * @ingroup input */ typedef struct GLFWgamepadstate { @@ -1630,6 +1781,10 @@ typedef struct GLFWgamepadstate * bundle, if present. This can be disabled with the @ref * GLFW_COCOA_CHDIR_RESOURCES init hint. * + * @remark @x11 This function will set the `LC_CTYPE` category of the + * application locale according to the current environment if that category is + * still "C". This is because the "C" locale breaks Unicode text input. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref intro_init @@ -1653,6 +1808,8 @@ GLFWAPI int glfwInit(void); * call this function, as it is called by @ref glfwInit before it returns * failure. * + * This function has no effect if GLFW is not initialized. + * * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. * * @remark This function may be called before @ref glfwInit. @@ -1814,10 +1971,17 @@ GLFWAPI int glfwGetError(const char** description); * Once set, the error callback remains set even after the library has been * terminated. * - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set. * + * @callback_signature + * @code + * void callback_name(int error_code, const char* description) + * @endcode + * For more information about the callback parameters, see the + * [callback pointer type](@ref GLFWerrorfun). + * * @errors None. * * @remark This function may be called before @ref glfwInit. @@ -1831,7 +1995,7 @@ GLFWAPI int glfwGetError(const char** description); * * @ingroup init */ -GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun); +GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun callback); /*! @brief Returns the currently connected monitors. * @@ -1911,6 +2075,37 @@ GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void); */ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); +/*! @brief Retrieves the work area of the monitor. + * + * This function returns the position, in screen coordinates, of the upper-left + * corner of the work area of the specified monitor along with the work area + * size in screen coordinates. The work area is defined as the area of the + * monitor not occluded by the operating system task bar where present. If no + * task bar exists then the work area is the monitor resolution in screen + * coordinates. + * + * Any or all of the position and size arguments may be `NULL`. If an error + * occurs, all non-`NULL` position and size arguments will be set to zero. + * + * @param[in] monitor The monitor to query. + * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`. + * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`. + * @param[out] width Where to store the monitor width, or `NULL`. + * @param[out] height Where to store the monitor height, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_workarea + * + * @since Added in version 3.3. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorWorkarea(GLFWmonitor* monitor, int* xpos, int* ypos, int* width, int* height); + /*! @brief Returns the physical size of the monitor. * * This function returns the size, in millimetres, of the display area of the @@ -1932,8 +2127,8 @@ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * - * @remark @win32 calculates the returned physical size from the - * current resolution and system DPI instead of querying the monitor EDID data. + * @remark @win32 On Windows 8 and earlier the physical size is calculated from + * the current resolution and system DPI instead of querying the monitor EDID data. * * @thread_safety This function must only be called from the main thread. * @@ -1949,9 +2144,11 @@ GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int* * * This function retrieves the content scale for the specified monitor. The * content scale is the ratio between the current DPI and the platform's - * default DPI. If you scale all pixel dimensions by this scale then your - * content should appear at an appropriate size. This is especially important - * for text and any UI elements. + * default DPI. This is especially important for text and any UI elements. If + * the pixel dimensions of your UI scaled by this look appropriate on your + * machine then it should appear at a reasonable size on other machines + * regardless of their DPI and scaling settings. This relies on the system DPI + * and scaling settings being somewhat correct. * * The content scale may depend on both the monitor resolution and pixel * density and on user settings. It may be very different from the raw DPI @@ -2057,11 +2254,18 @@ GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* monitor); * currently set callback. This is called when a monitor is connected to or * disconnected from the system. * - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWmonitor* monitor, int event) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWmonitorfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -2072,14 +2276,15 @@ GLFWAPI void* glfwGetMonitorUserPointer(GLFWmonitor* monitor); * * @ingroup monitor */ -GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun); +GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun callback); /*! @brief Returns the available video modes for the specified monitor. * * This function returns an array of all video modes supported by the specified * monitor. The returned array is sorted in ascending order, first by color - * bit depth (the sum of all channel depths) and then by resolution area (the - * product of width and height). + * bit depth (the sum of all channel depths), then by resolution area (the + * product of width and height), then resolution width and finally by refresh + * rate. * * @param[in] monitor The monitor to query. * @param[out] count Where to store the number of video modes in the returned @@ -2137,9 +2342,9 @@ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); /*! @brief Generates a gamma ramp and sets it for the specified monitor. * - * This function generates a 256-element gamma ramp from the specified exponent - * and then calls @ref glfwSetGammaRamp with it. The value must be a finite - * number greater than zero. + * This function generates an appropriately sized gamma ramp from the specified + * exponent and then calls @ref glfwSetGammaRamp with it. The value must be + * a finite number greater than zero. * * The software controlled gamma ramp is applied _in addition_ to the hardware * gamma correction, which today is usually an approximation of sRGB gamma. @@ -2155,7 +2360,7 @@ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. * - * @remark @wayland Gamma handling is a priviledged protocol, this function + * @remark @wayland Gamma handling is a privileged protocol, this function * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR. * * @thread_safety This function must only be called from the main thread. @@ -2179,7 +2384,7 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* monitor, float gamma); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * - * @remark @wayland Gamma handling is a priviledged protocol, this function + * @remark @wayland Gamma handling is a privileged protocol, this function * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR while * returning `NULL`. * @@ -2218,12 +2423,12 @@ GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * - * @remark Gamma ramp sizes other than 256 are not supported by all platforms - * or graphics hardware. + * @remark The size of the specified gamma ramp should match the size of the + * current ramp for that monitor. * * @remark @win32 The gamma ramp size must be 256. * - * @remark @wayland Gamma handling is a priviledged protocol, this function + * @remark @wayland Gamma handling is a privileged protocol, this function * will thus never be implemented and emits @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The specified gamma ramp is copied before this function @@ -2442,7 +2647,7 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value); * * @remark @macos When activating frame autosaving with * [GLFW_COCOA_FRAME_NAME](@ref GLFW_COCOA_FRAME_NAME_hint), the specified - * window size and position may be overriden by previously saved values. + * window size and position may be overridden by previously saved values. * * @remark @x11 Some window managers will not respect the placement of * initially hidden windows. @@ -2455,15 +2660,18 @@ GLFWAPI void glfwWindowHintString(int hint, const char* value); * @remark @x11 The class part of the `WM_CLASS` window property will by * default be set to the window title passed to this function. The instance * part will use the contents of the `RESOURCE_NAME` environment variable, if - * present and not empty, or fall back to the window title. Set the @ref - * GLFW_X11_CLASS_NAME and @ref GLFW_X11_INSTANCE_NAME window hints to override - * this. + * present and not empty, or fall back to the window title. Set the + * [GLFW_X11_CLASS_NAME](@ref GLFW_X11_CLASS_NAME_hint) and + * [GLFW_X11_INSTANCE_NAME](@ref GLFW_X11_INSTANCE_NAME_hint) window hints to + * override this. * - * @remark @wayland The window frame is currently very simple, only allowing - * window resize or move. A compositor can still emit close, maximize or - * fullscreen events, using for example a keybind mechanism. Additionally, - * the wp_viewporter protocol is required for this feature, otherwise the - * window will not be decorated. + * @remark @wayland Compositors should implement the xdg-decoration protocol + * for GLFW to decorate the window properly. If this protocol isn't + * supported, or if the compositor prefers client-side decorations, a very + * simple fallback frame will be drawn using the wp_viewporter protocol. A + * compositor can still emit close, maximize or fullscreen events, using for + * instance a keybind mechanism. If neither of these protocols is supported, + * the window won't be decorated. * * @remark @wayland A full screen window will not attempt to change the mode, * no matter what the requested size or refresh rate. @@ -2599,8 +2807,8 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); * @param[in] images The images to create the icon from. This is ignored if * count is zero. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The specified image data is copied before this function * returns. @@ -2625,19 +2833,19 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); */ GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* images); -/*! @brief Retrieves the position of the client area of the specified window. +/*! @brief Retrieves the position of the content area of the specified window. * * This function retrieves the position, in screen coordinates, of the - * upper-left corner of the client area of the specified window. + * upper-left corner of the content area of the specified window. * * Any or all of the position arguments may be `NULL`. If an error occurs, all * non-`NULL` position arguments will be set to zero. * * @param[in] window The window to query. * @param[out] xpos Where to store the x-coordinate of the upper-left corner of - * the client area, or `NULL`. + * the content area, or `NULL`. * @param[out] ypos Where to store the y-coordinate of the upper-left corner of - * the client area, or `NULL`. + * the content area, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -2657,10 +2865,10 @@ GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* i */ GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); -/*! @brief Sets the position of the client area of the specified window. +/*! @brief Sets the position of the content area of the specified window. * * This function sets the position, in screen coordinates, of the upper-left - * corner of the client area of the specified windowed mode window. If the + * corner of the content area of the specified windowed mode window. If the * window is a full screen window, this function does nothing. * * __Do not use this function__ to move an already visible window unless you @@ -2670,8 +2878,8 @@ GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); * cannot and should not override these limits. * * @param[in] window The window to query. - * @param[in] xpos The x-coordinate of the upper-left corner of the client area. - * @param[in] ypos The y-coordinate of the upper-left corner of the client area. + * @param[in] xpos The x-coordinate of the upper-left corner of the content area. + * @param[in] ypos The y-coordinate of the upper-left corner of the content area. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -2692,9 +2900,9 @@ GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); */ GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos); -/*! @brief Retrieves the size of the client area of the specified window. +/*! @brief Retrieves the size of the content area of the specified window. * - * This function retrieves the size, in screen coordinates, of the client area + * This function retrieves the size, in screen coordinates, of the content area * of the specified window. If you wish to retrieve the size of the * framebuffer of the window in pixels, see @ref glfwGetFramebufferSize. * @@ -2703,9 +2911,9 @@ GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos); * * @param[in] window The window whose size to retrieve. * @param[out] width Where to store the width, in screen coordinates, of the - * client area, or `NULL`. + * content area, or `NULL`. * @param[out] height Where to store the height, in screen coordinates, of the - * client area, or `NULL`. + * content area, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -2724,7 +2932,7 @@ GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); /*! @brief Sets the size limits of the specified window. * - * This function sets the size limits of the client area of the specified + * This function sets the size limits of the content area of the specified * window. If the window is full screen, the size limits only take effect * once it is made windowed. If the window is not resizable, this function * does nothing. @@ -2736,14 +2944,14 @@ GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); * dimensions and all must be greater than or equal to zero. * * @param[in] window The window to set limits for. - * @param[in] minwidth The minimum width, in screen coordinates, of the client + * @param[in] minwidth The minimum width, in screen coordinates, of the content * area, or `GLFW_DONT_CARE`. * @param[in] minheight The minimum height, in screen coordinates, of the - * client area, or `GLFW_DONT_CARE`. - * @param[in] maxwidth The maximum width, in screen coordinates, of the client + * content area, or `GLFW_DONT_CARE`. + * @param[in] maxwidth The maximum width, in screen coordinates, of the content * area, or `GLFW_DONT_CARE`. * @param[in] maxheight The maximum height, in screen coordinates, of the - * client area, or `GLFW_DONT_CARE`. + * content area, or `GLFW_DONT_CARE`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. @@ -2767,7 +2975,7 @@ GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minhe /*! @brief Sets the aspect ratio of the specified window. * - * This function sets the required aspect ratio of the client area of the + * This function sets the required aspect ratio of the content area of the * specified window. If the window is full screen, the aspect ratio only takes * effect once it is made windowed. If the window is not resizable, this * function does nothing. @@ -2808,9 +3016,9 @@ GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minhe */ GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); -/*! @brief Sets the size of the client area of the specified window. +/*! @brief Sets the size of the content area of the specified window. * - * This function sets the size, in screen coordinates, of the client area of + * This function sets the size, in screen coordinates, of the content area of * the specified window. * * For full screen windows, this function updates the resolution of its desired @@ -2826,9 +3034,9 @@ GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); * * @param[in] window The window to resize. * @param[in] width The desired width, in screen coordinates, of the window - * client area. + * content area. * @param[in] height The desired height, in screen coordinates, of the window - * client area. + * content area. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -2919,9 +3127,11 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int * * This function retrieves the content scale for the specified window. The * content scale is the ratio between the current DPI and the platform's - * default DPI. If you scale all pixel dimensions by this scale then your - * content should appear at an appropriate size. This is especially important - * for text and any UI elements. + * default DPI. This is especially important for text and any UI elements. If + * the pixel dimensions of your UI scaled by this look appropriate on your + * machine then it should appear at a reasonable size on other machines + * regardless of their DPI and scaling settings. This relies on the system DPI + * and scaling settings being somewhat correct. * * On systems where each monitors can have its own content scale, the window * content scale will depend on which monitor the system considers the window @@ -3008,18 +3218,15 @@ GLFWAPI void glfwSetWindowOpacity(GLFWwindow* window, float opacity); * previously restored. If the window is already iconified, this function does * nothing. * - * If the specified window is a full screen window, the original monitor - * resolution is restored until the window is restored. + * If the specified window is a full screen window, GLFW restores the original + * video mode of the monitor. The window's desired video mode is set again + * when the window is restored. * * @param[in] window The window to iconify. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * - * @remark @wayland There is no concept of iconification in wl_shell, this - * function will emit @ref GLFW_PLATFORM_ERROR when using this deprecated - * protocol. - * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_iconify @@ -3039,8 +3246,8 @@ GLFWAPI void glfwIconifyWindow(GLFWwindow* window); * (minimized) or maximized. If the window is already restored, this function * does nothing. * - * If the specified window is a full screen window, the resolution chosen for - * the window is restored on the selected monitor. + * If the specified window is an iconified full screen window, its desired + * video mode is set again for its monitor when the window is restored. * * @param[in] window The window to restore. * @@ -3101,6 +3308,11 @@ GLFWAPI void glfwMaximizeWindow(GLFWwindow* window); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark @wayland Because Wayland wants every frame of the desktop to be + * complete, this function does not immediately make the window visible. + * Instead it will become visible the next time the window framebuffer is + * updated after this call. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_hide @@ -3232,7 +3444,7 @@ GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); * The window position is ignored when setting a monitor. * * When the monitor is `NULL`, the position, width and height are used to - * place the window client area. The refresh rate is ignored when no monitor + * place the window content area. The refresh rate is ignored when no monitor * is specified. * * If you only wish to update the resolution of a full screen window or the @@ -3245,12 +3457,12 @@ GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); * @param[in] window The window whose monitor, size or video mode to set. * @param[in] monitor The desired monitor, or `NULL` to set windowed mode. * @param[in] xpos The desired x-coordinate of the upper-left corner of the - * client area. + * content area. * @param[in] ypos The desired y-coordinate of the upper-left corner of the - * client area. - * @param[in] width The desired with, in screen coordinates, of the client area - * or video mode. - * @param[in] height The desired height, in screen coordinates, of the client + * content area. + * @param[in] width The desired with, in screen coordinates, of the content + * area or video mode. + * @param[in] height The desired height, in screen coordinates, of the content * area or video mode. * @param[in] refreshRate The desired refresh rate, in Hz, of the video mode, * or `GLFW_DONT_CARE`. @@ -3303,6 +3515,9 @@ GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int * errors. However, this function should not fail as long as it is passed * valid arguments and the library has been [initialized](@ref intro_init). * + * @remark @wayland The Wayland protocol provides no way to check whether a + * window is iconfied, so @ref GLFW_ICONIFIED always returns `GLFW_FALSE`. + * * @thread_safety This function must only be called from the main thread. * * @sa @ref window_attribs @@ -3400,15 +3615,22 @@ GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); * * This function sets the position callback of the specified window, which is * called when the window is moved. The callback is provided with the - * position, in screen coordinates, of the upper-left corner of the client area - * of the window. + * position, in screen coordinates, of the upper-left corner of the content + * area of the window. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int xpos, int ypos) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowposfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @remark @wayland This callback will never be called, as there is no way for @@ -3422,20 +3644,27 @@ GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); * * @ingroup window */ -GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindowposfun cbfun); +GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindowposfun callback); /*! @brief Sets the size callback for the specified window. * * This function sets the size callback of the specified window, which is * called when the window is resized. The callback is provided with the size, - * in screen coordinates, of the client area of the window. + * in screen coordinates, of the content area of the window. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int width, int height) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowsizefun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -3447,7 +3676,7 @@ GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindow * * @ingroup window */ -GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwindowsizefun cbfun); +GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwindowsizefun callback); /*! @brief Sets the close callback for the specified window. * @@ -3461,11 +3690,18 @@ GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwind * The close callback is not triggered by @ref glfwDestroyWindow. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowclosefun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @remark @macos Selecting Quit from the application menu will trigger the @@ -3480,12 +3716,12 @@ GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwind * * @ingroup window */ -GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwindowclosefun cbfun); +GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwindowclosefun callback); /*! @brief Sets the refresh callback for the specified window. * * This function sets the refresh callback of the specified window, which is - * called when the client area of the window needs to be redrawn, for example + * called when the content area of the window needs to be redrawn, for example * if the window has been exposed after having been covered by another window. * * On compositing window systems such as Aero, Compiz, Aqua or Wayland, where @@ -3493,11 +3729,18 @@ GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwi * very infrequently or never at all. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window); + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowrefreshfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -3509,7 +3752,7 @@ GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwi * * @ingroup window */ -GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GLFWwindowrefreshfun cbfun); +GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GLFWwindowrefreshfun callback); /*! @brief Sets the focus callback for the specified window. * @@ -3522,11 +3765,18 @@ GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GL * and @ref glfwSetMouseButtonCallback. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int focused) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowfocusfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -3537,7 +3787,7 @@ GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GL * * @ingroup window */ -GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwindowfocusfun cbfun); +GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwindowfocusfun callback); /*! @brief Sets the iconify callback for the specified window. * @@ -3545,15 +3795,22 @@ GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwi * is called when the window is iconified or restored. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int iconified) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowiconifyfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * - * @remark @wayland The wl_shell protocol has no concept of iconification, - * this callback will never be called when using this deprecated protocol. + * @remark @wayland The XDG-shell protocol has no event for iconification, so + * this callback will never be called. * * @thread_safety This function must only be called from the main thread. * @@ -3563,7 +3820,7 @@ GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwi * * @ingroup window */ -GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GLFWwindowiconifyfun cbfun); +GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GLFWwindowiconifyfun callback); /*! @brief Sets the maximize callback for the specified window. * @@ -3571,11 +3828,18 @@ GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GL * is called when the window is maximized or restored. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int maximized) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowmaximizefun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -3586,7 +3850,7 @@ GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GL * * @ingroup window */ -GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, GLFWwindowmaximizefun cbfun); +GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, GLFWwindowmaximizefun callback); /*! @brief Sets the framebuffer resize callback for the specified window. * @@ -3594,11 +3858,18 @@ GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, * which is called when the framebuffer of the specified window is resized. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int width, int height) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWframebuffersizefun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -3609,7 +3880,7 @@ GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, * * @ingroup window */ -GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window, GLFWframebuffersizefun cbfun); +GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window, GLFWframebuffersizefun callback); /*! @brief Sets the window content scale callback for the specified window. * @@ -3617,11 +3888,18 @@ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window * which is called when the content scale of the specified window changes. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, float xscale, float yscale) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWwindowcontentscalefun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -3633,7 +3911,7 @@ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window * * @ingroup window */ -GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* window, GLFWwindowcontentscalefun cbfun); +GLFWAPI GLFWwindowcontentscalefun glfwSetWindowContentScaleCallback(GLFWwindow* window, GLFWwindowcontentscalefun callback); /*! @brief Processes all pending events. * @@ -3699,10 +3977,6 @@ GLFWAPI void glfwPollEvents(void); * GLFW will pass those events on to the application callbacks before * returning. * - * If no windows exist, this function returns immediately. For synchronization - * of threads in applications that do not create windows, use your threading - * library of choice. - * * Event processing is not required for joystick input to work. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref @@ -3750,14 +4024,13 @@ GLFWAPI void glfwWaitEvents(void); * GLFW will pass those events on to the application callbacks before * returning. * - * If no windows exist, this function returns immediately. For synchronization - * of threads in applications that do not create windows, use your threading - * library of choice. - * * Event processing is not required for joystick input to work. * * @param[in] timeout The maximum amount of time, in seconds, to wait. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * * @reentrancy This function must not be called from a callback. * * @thread_safety This function must only be called from the main thread. @@ -3777,10 +4050,6 @@ GLFWAPI void glfwWaitEventsTimeout(double timeout); * This function posts an empty event from the current thread to the event * queue, causing @ref glfwWaitEvents or @ref glfwWaitEventsTimeout to return. * - * If no windows exist, this function returns immediately. For synchronization - * of threads in applications that do not create windows, use your threading - * library of choice. - * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * @@ -3800,11 +4069,13 @@ GLFWAPI void glfwPostEmptyEvent(void); * * This function returns the value of an input option for the specified window. * The mode must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS, - * @ref GLFW_STICKY_MOUSE_BUTTONS or @ref GLFW_LOCK_KEY_MODS. + * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or + * @ref GLFW_RAW_MOUSE_MOTION. * * @param[in] window The window to query. * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`, - * `GLFW_STICKY_MOUSE_BUTTONS` or `GLFW_LOCK_KEY_MODS`. + * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or + * `GLFW_RAW_MOUSE_MOTION`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_ENUM. @@ -3823,13 +4094,14 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); * * This function sets an input mode option for the specified window. The mode * must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS, - * @ref GLFW_STICKY_MOUSE_BUTTONS or @ref GLFW_LOCK_KEY_MODS. + * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or + * @ref GLFW_RAW_MOUSE_MOTION. * * If the mode is `GLFW_CURSOR`, the value must be one of the following cursor * modes: * - `GLFW_CURSOR_NORMAL` makes the cursor visible and behaving normally. - * - `GLFW_CURSOR_HIDDEN` makes the cursor invisible when it is over the client - * area of the window but does not restrict the cursor from leaving. + * - `GLFW_CURSOR_HIDDEN` makes the cursor invisible when it is over the + * content area of the window but does not restrict the cursor from leaving. * - `GLFW_CURSOR_DISABLED` hides and grabs the cursor, providing virtual * and unlimited cursor movement. This is useful for implementing for * example 3D camera controls. @@ -3855,9 +4127,16 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); * GLFW_MOD_CAPS_LOCK bit set when the event was generated with Caps Lock on, * and the @ref GLFW_MOD_NUM_LOCK bit when Num Lock was on. * + * If the mode is `GLFW_RAW_MOUSE_MOTION`, the value must be either `GLFW_TRUE` + * to enable raw (unscaled and unaccelerated) mouse motion when the cursor is + * disabled, or `GLFW_FALSE` to disable it. If raw motion is not supported, + * attempting to set this will emit @ref GLFW_PLATFORM_ERROR. Call @ref + * glfwRawMouseMotionSupported to check for support. + * * @param[in] window The window whose input mode to set. * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`, - * `GLFW_STICKY_MOUSE_BUTTONS` or `GLFW_LOCK_KEY_MODS`. + * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or + * `GLFW_RAW_MOUSE_MOTION`. * @param[in] value The new value of the specified input mode. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref @@ -3873,6 +4152,35 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); */ GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); +/*! @brief Returns whether raw mouse motion is supported. + * + * This function returns whether raw mouse motion is supported on the current + * system. This status does not change after GLFW has been initialized so you + * only need to check this once. If you attempt to enable raw motion on + * a system that does not support it, @ref GLFW_PLATFORM_ERROR will be emitted. + * + * Raw mouse motion is closer to the actual motion of the mouse across + * a surface. It is not affected by the scaling and acceleration applied to + * the motion of the desktop cursor. That processing is suitable for a cursor + * while raw motion is better for controlling for example a 3D camera. Because + * of this, raw mouse motion is only provided when the cursor is disabled. + * + * @return `GLFW_TRUE` if raw mouse motion is supported on the current machine, + * or `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref raw_mouse_motion + * @sa @ref glfwSetInputMode + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwRawMouseMotionSupported(void); + /*! @brief Returns the layout-specific name of the specified printable key. * * This function returns the name of the specified printable key, encoded as @@ -3925,9 +4233,11 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * + * @remark The contents of the returned string may change when a keyboard + * layout change event is received. + * * @pointer_lifetime The returned string is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the next call to @ref - * glfwGetKeyName, or until the library is terminated. + * should not free it yourself. It is valid until the library is terminated. * * @thread_safety This function must only be called from the main thread. * @@ -3968,8 +4278,7 @@ GLFWAPI int glfwGetKeyScancode(int key); * * This function returns the last state reported for the specified key to the * specified window. The returned state is one of `GLFW_PRESS` or - * `GLFW_RELEASE`. The higher-level action `GLFW_REPEAT` is only reported to - * the key callback. + * `GLFW_RELEASE`. The action `GLFW_REPEAT` is only reported to the key callback. * * If the @ref GLFW_STICKY_KEYS input mode is enabled, this function returns * `GLFW_PRESS` the first time you call it for a key that was pressed, even if @@ -4011,8 +4320,8 @@ GLFWAPI int glfwGetKey(GLFWwindow* window, int key); * `GLFW_RELEASE`. * * If the @ref GLFW_STICKY_MOUSE_BUTTONS input mode is enabled, this function - * `GLFW_PRESS` the first time you call it for a mouse button that was pressed, - * even if that mouse button has already been released. + * returns `GLFW_PRESS` the first time you call it for a mouse button that was + * pressed, even if that mouse button has already been released. * * @param[in] window The desired window. * @param[in] button The desired [mouse button](@ref buttons). @@ -4032,11 +4341,11 @@ GLFWAPI int glfwGetKey(GLFWwindow* window, int key); */ GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); -/*! @brief Retrieves the position of the cursor relative to the client area of +/*! @brief Retrieves the position of the cursor relative to the content area of * the window. * * This function returns the position of the cursor, in screen coordinates, - * relative to the upper-left corner of the client area of the specified + * relative to the upper-left corner of the content area of the specified * window. * * If the cursor is disabled (with `GLFW_CURSOR_DISABLED`) then the cursor @@ -4052,9 +4361,9 @@ GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); * * @param[in] window The desired window. * @param[out] xpos Where to store the cursor x-coordinate, relative to the - * left edge of the client area, or `NULL`. + * left edge of the content area, or `NULL`. * @param[out] ypos Where to store the cursor y-coordinate, relative to the to - * top edge of the client area, or `NULL`. + * top edge of the content area, or `NULL`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -4070,11 +4379,11 @@ GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); */ GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); -/*! @brief Sets the position of the cursor, relative to the client area of the +/*! @brief Sets the position of the cursor, relative to the content area of the * window. * * This function sets the position, in screen coordinates, of the cursor - * relative to the upper-left corner of the client area of the specified + * relative to the upper-left corner of the content area of the specified * window. The window must have input focus. If the window does not have * input focus when this function is called, it fails silently. * @@ -4089,9 +4398,9 @@ GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); * * @param[in] window The desired window. * @param[in] xpos The desired x-coordinate, relative to the left edge of the - * client area. + * content area. * @param[in] ypos The desired y-coordinate, relative to the top edge of the - * client area. + * content area. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. @@ -4130,8 +4439,8 @@ GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); * @return The handle of the created cursor, or `NULL` if an * [error](@ref error_handling) occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The specified image data is copied before this function * returns. @@ -4201,7 +4510,7 @@ GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor); /*! @brief Sets the cursor for the window. * * This function sets the cursor image to be used when the cursor is over the - * client area of the specified window. The set cursor will only be visible + * content area of the specified window. The set cursor will only be visible * when the [cursor mode](@ref cursor_mode) of the window is * `GLFW_CURSOR_NORMAL`. * @@ -4250,11 +4559,18 @@ GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor); * scancode may be zero. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new key callback, or `NULL` to remove the currently + * @param[in] callback The new key callback, or `NULL` to remove the currently * set callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int key, int scancode, int action, int mods) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWkeyfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -4266,7 +4582,7 @@ GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor); * * @ingroup input */ -GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun); +GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun callback); /*! @brief Sets the Unicode character callback. * @@ -4283,16 +4599,21 @@ GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun); * The character callback behaves as system text input normally does and will * not be called if modifier keys are held down that would prevent normal text * input on that platform, for example a Super (Command) key on macOS or Alt key - * on Windows. There is a - * [character with modifiers callback](@ref glfwSetCharModsCallback) that - * receives these events. + * on Windows. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcharfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -4304,7 +4625,7 @@ GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun); * * @ingroup input */ -GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun); +GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun callback); /*! @brief Sets the Unicode character with modifiers callback. * @@ -4322,11 +4643,18 @@ GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun); * [key callback](@ref glfwSetKeyCallback) instead. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or an * [error](@ref error_handling) occurred. * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, unsigned int codepoint, int mods) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcharmodsfun). + * * @deprecated Scheduled for removal in version 4.0. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. @@ -4339,7 +4667,7 @@ GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun); * * @ingroup input */ -GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmodsfun cbfun); +GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmodsfun callback); /*! @brief Sets the mouse button callback. * @@ -4353,11 +4681,18 @@ GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmods * [window focus callback](@ref glfwSetWindowFocusCallback) has been called. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int button, int action, int mods) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWmousebuttonfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -4369,21 +4704,28 @@ GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmods * * @ingroup input */ -GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun cbfun); +GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun callback); /*! @brief Sets the cursor position callback. * * This function sets the cursor position callback of the specified window, * which is called when the cursor is moved. The callback is provided with the * position, in screen coordinates, relative to the upper-left corner of the - * client area of the window. + * content area of the window. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, double xpos, double ypos); + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcursorposfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -4394,20 +4736,27 @@ GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmo * * @ingroup input */ -GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun cbfun); +GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun callback); -/*! @brief Sets the cursor enter/exit callback. +/*! @brief Sets the cursor enter/leave callback. * * This function sets the cursor boundary crossing callback of the specified - * window, which is called when the cursor enters or leaves the client area of + * window, which is called when the cursor enters or leaves the content area of * the window. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int entered) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWcursorenterfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -4418,7 +4767,7 @@ GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursor * * @ingroup input */ -GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcursorenterfun cbfun); +GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcursorenterfun callback); /*! @brief Sets the scroll callback. * @@ -4430,11 +4779,18 @@ GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcu * wheel or a touchpad scrolling area. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new scroll callback, or `NULL` to remove the currently - * set callback. + * @param[in] callback The new scroll callback, or `NULL` to remove the + * currently set callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, double xoffset, double yoffset) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWscrollfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -4445,12 +4801,12 @@ GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcu * * @ingroup input */ -GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cbfun); +GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun callback); -/*! @brief Sets the file drop callback. +/*! @brief Sets the path drop callback. * - * This function sets the file drop callback of the specified window, which is - * called when one or more dragged files are dropped on the window. + * This function sets the path drop callback of the specified window, which is + * called when one or more dragged paths are dropped on the window. * * Because the path array and its strings may have been generated specifically * for that event, they are not guaranteed to be valid after the callback has @@ -4458,11 +4814,18 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cb * make a deep copy. * * @param[in] window The window whose callback to set. - * @param[in] cbfun The new file drop callback, or `NULL` to remove the + * @param[in] callback The new file drop callback, or `NULL` to remove the * currently set callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(GLFWwindow* window, int path_count, const char* paths[]) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWdropfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @remark @wayland File drop is currently unimplemented. @@ -4475,7 +4838,7 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cb * * @ingroup input */ -GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun cbfun); +GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun callback); /*! @brief Returns whether the specified joystick is present. * @@ -4581,7 +4944,7 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count); * Each element in the array is one of the following values: * * Name | Value - * --------------------- | -------------------------------- + * ---- | ----- * `GLFW_HAT_CENTERED` | 0 * `GLFW_HAT_UP` | 1 * `GLFW_HAT_RIGHT` | 2 @@ -4663,7 +5026,7 @@ GLFWAPI const unsigned char* glfwGetJoystickHats(int jid, int* count); */ GLFWAPI const char* glfwGetJoystickName(int jid); -/*! @brief Returns the SDL comaptible GUID of the specified joystick. +/*! @brief Returns the SDL compatible GUID of the specified joystick. * * This function returns the SDL compatible GUID, as a UTF-8 encoded * hexadecimal string, of the specified joystick. The returned string is @@ -4794,11 +5157,18 @@ GLFWAPI int glfwJoystickIsGamepad(int jid); * called by joystick functions. The function will then return whatever it * returns if the joystick is not present. * - * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * @param[in] callback The new callback, or `NULL` to remove the currently set * callback. * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * + * @callback_signature + * @code + * void function_name(int jid, int event) + * @endcode + * For more information about the callback parameters, see the + * [function pointer type](@ref GLFWjoystickfun). + * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function must only be called from the main thread. @@ -4809,7 +5179,7 @@ GLFWAPI int glfwJoystickIsGamepad(int jid); * * @ingroup input */ -GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun); +GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun callback); /*! @brief Adds the specified SDL_GameControllerDB gamepad mappings. * @@ -4860,6 +5230,8 @@ GLFWAPI int glfwUpdateGamepadMappings(const char* string); * joystick is not present, does not have a mapping or an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref GLFW_INVALID_ENUM. + * * @pointer_lifetime The returned string is allocated and freed by GLFW. You * should not free it yourself. It is valid until the specified joystick is * disconnected, the gamepad mappings are updated or the library is terminated. @@ -4877,7 +5249,7 @@ GLFWAPI const char* glfwGetGamepadName(int jid); /*! @brief Retrieves the state of the specified joystick remapped as a gamepad. * - * This function retrives the state of the specified joystick remapped to + * This function retrieves the state of the specified joystick remapped to * an Xbox-like gamepad. * * If the specified joystick is not present or does not have a gamepad mapping @@ -4901,6 +5273,8 @@ GLFWAPI const char* glfwGetGamepadName(int jid); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_ENUM. * + * @thread_safety This function must only be called from the main thread. + * * @sa @ref gamepad * @sa @ref glfwUpdateGamepadMappings * @sa @ref glfwJoystickIsGamepad @@ -4922,8 +5296,6 @@ GLFWAPI int glfwGetGamepadState(int jid, GLFWgamepadstate* state); * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_PLATFORM_ERROR. * - * @remark @wayland Clipboard is currently unimplemented. - * * @pointer_lifetime The specified string is copied before this function * returns. * @@ -4949,10 +5321,8 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); * @return The contents of the clipboard as a UTF-8 encoded string, or `NULL` * if an [error](@ref error_handling) occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @remark @wayland Clipboard is currently unimplemented. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_FORMAT_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. * * @pointer_lifetime The returned string is allocated and freed by GLFW. You * should not free it yourself. It is valid until the next call to @ref @@ -4970,23 +5340,26 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); */ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); -/*! @brief Returns the value of the GLFW timer. +/*! @brief Returns the GLFW time. * - * This function returns the value of the GLFW timer. Unless the timer has - * been set using @ref glfwSetTime, the timer measures time elapsed since GLFW - * was initialized. + * This function returns the current GLFW time, in seconds. Unless the time + * has been set using @ref glfwSetTime it measures time elapsed since GLFW was + * initialized. + * + * This function and @ref glfwSetTime are helper functions on top of @ref + * glfwGetTimerFrequency and @ref glfwGetTimerValue. * * The resolution of the timer is system dependent, but is usually on the order * of a few micro- or nanoseconds. It uses the highest-resolution monotonic * time source on each supported platform. * - * @return The current value, in seconds, or zero if an + * @return The current time, in seconds, or zero if an * [error](@ref error_handling) occurred. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * * @thread_safety This function may be called from any thread. Reading and - * writing of the internal timer offset is not atomic, so it needs to be + * writing of the internal base time is not atomic, so it needs to be * externally synchronized with calls to @ref glfwSetTime. * * @sa @ref time @@ -4997,23 +5370,26 @@ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); */ GLFWAPI double glfwGetTime(void); -/*! @brief Sets the GLFW timer. +/*! @brief Sets the GLFW time. * - * This function sets the value of the GLFW timer. It then continues to count - * up from that value. The value must be a positive finite number less than - * or equal to 18446744073.0, which is approximately 584.5 years. + * This function sets the current GLFW time, in seconds. The value must be + * a positive finite number less than or equal to 18446744073.0, which is + * approximately 584.5 years. + * + * This function and @ref glfwGetTime are helper functions on top of @ref + * glfwGetTimerFrequency and @ref glfwGetTimerValue. * * @param[in] time The new value, in seconds. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_VALUE. * - * @remark The upper limit of the timer is calculated as + * @remark The upper limit of GLFW time is calculated as * floor((264 - 1) / 109) and is due to implementations * storing nanoseconds in 64 bits. The limit may be increased in the future. * * @thread_safety This function may be called from any thread. Reading and - * writing of the internal timer offset is not atomic, so it needs to be + * writing of the internal base time is not atomic, so it needs to be * externally synchronized with calls to @ref glfwGetTime. * * @sa @ref time @@ -5290,13 +5666,11 @@ GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname); * This function returns whether the Vulkan loader and any minimally functional * ICD have been found. * - * The availability of a Vulkan loader and even an ICD does not by itself - * guarantee that surface creation or even instance creation is possible. - * For example, on Fermi systems Nvidia will install an ICD that provides no - * actual Vulkan support. Call @ref glfwGetRequiredInstanceExtensions to check - * whether the extensions necessary for Vulkan surface creation are available - * and @ref glfwGetPhysicalDevicePresentationSupport to check whether a queue - * family of a physical device supports image presentation. + * The availability of a Vulkan loader and even an ICD does not by itself guarantee that + * surface creation or even instance creation is possible. Call @ref + * glfwGetRequiredInstanceExtensions to check whether the extensions necessary for Vulkan + * surface creation are available and @ref glfwGetPhysicalDevicePresentationSupport to + * check whether a queue family of a physical device supports image presentation. * * @return `GLFW_TRUE` if Vulkan is minimally available, or `GLFW_FALSE` * otherwise. @@ -5317,7 +5691,7 @@ GLFWAPI int glfwVulkanSupported(void); * * This function returns an array of names of Vulkan instance extensions required * by GLFW for creating Vulkan surfaces for GLFW windows. If successful, the - * list will always contains `VK_KHR_surface`, so if you don't require any + * list will always contain `VK_KHR_surface`, so if you don't require any * additional extensions you can pass this list directly to the * `VkInstanceCreateInfo` struct. * @@ -5342,9 +5716,6 @@ GLFWAPI int glfwVulkanSupported(void); * returned array, as it is an error to specify an extension more than once in * the `VkInstanceCreateInfo` struct. * - * @remark @macos This function currently only supports the - * `VK_MVK_macos_surface` extension from MoltenVK. - * * @pointer_lifetime The returned array is allocated and freed by GLFW. You * should not free it yourself. It is guaranteed to be valid only until the * library is terminated. @@ -5426,7 +5797,7 @@ GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* p * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. * * @remark @macos This function currently always returns `GLFW_TRUE`, as the - * `VK_MVK_macos_surface` extension does not provide + * `VK_MVK_macos_surface` and `VK_EXT_metal_surface` extensions do not provide * a `vkGetPhysicalDevice*PresentationSupport` type function. * * @thread_safety This function may be called from any thread. For @@ -5483,8 +5854,10 @@ GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhys * @ref glfwVulkanSupported and @ref glfwGetRequiredInstanceExtensions should * eliminate almost all occurrences of these errors. * - * @remark @macos This function currently only supports the - * `VK_MVK_macos_surface` extension from MoltenVK. + * @remark @macos GLFW prefers the `VK_EXT_metal_surface` extension, with the + * `VK_MVK_macos_surface` extension as a fallback. The name of the selected + * extension, if any, is included in the array returned by @ref + * glfwGetRequiredInstanceExtensions. * * @remark @macos This function creates and sets a `CAMetalLayer` instance for * the window content view, which is required for MoltenVK to function. @@ -5525,6 +5898,7 @@ GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window */ #ifndef GLAPIENTRY #define GLAPIENTRY APIENTRY + #define GLFW_GLAPIENTRY_DEFINED #endif /* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */ diff --git a/examples/others/external/include/GLFW/glfw3native.h b/examples/others/external/include/GLFW/glfw3native.h index 4372cb766..7be0227d5 100644 --- a/examples/others/external/include/GLFW/glfw3native.h +++ b/examples/others/external/include/GLFW/glfw3native.h @@ -3,7 +3,7 @@ * A library for OpenGL, window and input *------------------------------------------------------------------------ * Copyright (c) 2002-2006 Marcus Geelnard - * Copyright (c) 2006-2016 Camilla Löwy + * Copyright (c) 2006-2018 Camilla Löwy * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages @@ -62,7 +62,6 @@ extern "C" { * * `GLFW_EXPOSE_NATIVE_COCOA` * * `GLFW_EXPOSE_NATIVE_X11` * * `GLFW_EXPOSE_NATIVE_WAYLAND` - * * `GLFW_EXPOSE_NATIVE_MIR` * * The available context API macros are: * * `GLFW_EXPOSE_NATIVE_WGL` @@ -75,6 +74,16 @@ extern "C" { * and which platform-specific headers to include. It is then up your (by * definition platform-specific) code to handle which of these should be * defined. + * + * If you do not want the platform-specific headers to be included, define + * `GLFW_NATIVE_INCLUDE_NONE` before including the @ref glfw3native.h header. + * + * @code + * #define GLFW_EXPOSE_NATIVE_WIN32 + * #define GLFW_EXPOSE_NATIVE_WGL + * #define GLFW_NATIVE_INCLUDE_NONE + * #include + * @endcode */ @@ -82,46 +91,65 @@ extern "C" { * System headers and types *************************************************************************/ -#if defined(GLFW_EXPOSE_NATIVE_WIN32) - // This is a workaround for the fact that glfw3.h needs to export APIENTRY (for - // example to allow applications to correctly declare a GL_ARB_debug_output - // callback) but windows.h assumes no one will define APIENTRY before it does - #if defined(GLFW_APIENTRY_DEFINED) - #undef APIENTRY - #undef GLFW_APIENTRY_DEFINED - #endif - #include -#elif defined(GLFW_EXPOSE_NATIVE_COCOA) - #include - #if defined(__OBJC__) - #import - #else - typedef void* id; - #endif -#elif defined(GLFW_EXPOSE_NATIVE_X11) - #include - #include -#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND) - #include -#elif defined(GLFW_EXPOSE_NATIVE_MIR) - #include -#endif +#if !defined(GLFW_NATIVE_INCLUDE_NONE) -#if defined(GLFW_EXPOSE_NATIVE_WGL) - /* WGL is declared by windows.h */ -#endif -#if defined(GLFW_EXPOSE_NATIVE_NSGL) - /* NSGL is declared by Cocoa.h */ -#endif -#if defined(GLFW_EXPOSE_NATIVE_GLX) - #include -#endif -#if defined(GLFW_EXPOSE_NATIVE_EGL) - #include -#endif -#if defined(GLFW_EXPOSE_NATIVE_OSMESA) - #include -#endif + #if defined(GLFW_EXPOSE_NATIVE_WIN32) || defined(GLFW_EXPOSE_NATIVE_WGL) + /* This is a workaround for the fact that glfw3.h needs to export APIENTRY (for + * example to allow applications to correctly declare a GL_KHR_debug callback) + * but windows.h assumes no one will define APIENTRY before it does + */ + #if defined(GLFW_APIENTRY_DEFINED) + #undef APIENTRY + #undef GLFW_APIENTRY_DEFINED + #endif + #include + #elif defined(GLFW_EXPOSE_NATIVE_COCOA) || defined(GLFW_EXPOSE_NATIVE_NSGL) + #if defined(__OBJC__) + #import + #else + #include + #include + #endif + #elif defined(GLFW_EXPOSE_NATIVE_X11) || defined(GLFW_EXPOSE_NATIVE_GLX) + #include + #include + #elif defined(GLFW_EXPOSE_NATIVE_WAYLAND) + #include + #endif + + #if defined(GLFW_EXPOSE_NATIVE_WGL) + /* WGL is declared by windows.h */ + #endif + #if defined(GLFW_EXPOSE_NATIVE_NSGL) + /* NSGL is declared by Cocoa.h */ + #endif + #if defined(GLFW_EXPOSE_NATIVE_GLX) + /* This is a workaround for the fact that glfw3.h defines GLAPIENTRY because by + * default it also acts as an OpenGL header + * However, glx.h will include gl.h, which will define it unconditionally + */ + #if defined(GLFW_GLAPIENTRY_DEFINED) + #undef GLAPIENTRY + #undef GLFW_GLAPIENTRY_DEFINED + #endif + #include + #endif + #if defined(GLFW_EXPOSE_NATIVE_EGL) + #include + #endif + #if defined(GLFW_EXPOSE_NATIVE_OSMESA) + /* This is a workaround for the fact that glfw3.h defines GLAPIENTRY because by + * default it also acts as an OpenGL header + * However, osmesa.h will include gl.h, which will define it unconditionally + */ + #if defined(GLFW_GLAPIENTRY_DEFINED) + #undef GLAPIENTRY + #undef GLFW_GLAPIENTRY_DEFINED + #endif + #include + #endif + +#endif /*GLFW_NATIVE_INCLUDE_NONE*/ /************************************************************************* @@ -135,6 +163,8 @@ extern "C" { * of the specified monitor, or `NULL` if an [error](@ref error_handling) * occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -150,6 +180,8 @@ GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor); * `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -164,6 +196,16 @@ GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor); * @return The `HWND` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark The `HDC` associated with the window can be queried with the + * [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc) + * function. + * @code + * HDC dc = GetDC(glfwGetWin32Window(window)); + * @endcode + * This DC is private and does not need to be released. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -180,6 +222,17 @@ GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window); * @return The `HGLRC` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * + * @remark The `HDC` associated with the window can be queried with the + * [GetDC](https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getdc) + * function. + * @code + * HDC dc = GetDC(glfwGetWin32Window(window)); + * @endcode + * This DC is private and does not need to be released. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -196,6 +249,8 @@ GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window); * @return The `CGDirectDisplayID` of the specified monitor, or * `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -210,6 +265,8 @@ GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor); * @return The `NSWindow` of the specified window, or `nil` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -226,6 +283,9 @@ GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window); * @return The `NSOpenGLContext` of the specified window, or `nil` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -242,6 +302,8 @@ GLFWAPI id glfwGetNSGLContext(GLFWwindow* window); * @return The `Display` used by GLFW, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -256,6 +318,8 @@ GLFWAPI Display* glfwGetX11Display(void); * @return The `RRCrtc` of the specified monitor, or `None` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -270,6 +334,8 @@ GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor); * @return The `RROutput` of the specified monitor, or `None` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -284,6 +350,8 @@ GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor); * @return The `Window` of the specified window, or `None` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -350,6 +418,9 @@ GLFWAPI const char* glfwGetX11SelectionString(void); * @return The `GLXContext` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -364,6 +435,9 @@ GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window); * @return The `GLXWindow` of the specified window, or `None` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -380,6 +454,8 @@ GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window); * @return The `struct wl_display*` used by GLFW, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -394,6 +470,8 @@ GLFWAPI struct wl_display* glfwGetWaylandDisplay(void); * @return The `struct wl_output*` of the specified monitor, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -408,6 +486,8 @@ GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor); * @return The main `struct wl_surface*` of the specified window, or `NULL` if * an [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -418,56 +498,17 @@ GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor); GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window); #endif -#if defined(GLFW_EXPOSE_NATIVE_MIR) -/*! @brief Returns the `MirConnection*` used by GLFW. - * - * @return The `MirConnection*` used by GLFW, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI MirConnection* glfwGetMirDisplay(void); - -/*! @brief Returns the Mir output ID of the specified monitor. - * - * @return The Mir output ID of the specified monitor, or zero if an - * [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI int glfwGetMirMonitor(GLFWmonitor* monitor); - -/*! @brief Returns the `MirWindow*` of the specified window. - * - * @return The `MirWindow*` of the specified window, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI MirWindow* glfwGetMirWindow(GLFWwindow* window); -#endif - #if defined(GLFW_EXPOSE_NATIVE_EGL) /*! @brief Returns the `EGLDisplay` used by GLFW. * * @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark Because EGL is initialized on demand, this function will return + * `EGL_NO_DISPLAY` until the first context has been created via EGL. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -482,6 +523,9 @@ GLFWAPI EGLDisplay glfwGetEGLDisplay(void); * @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -496,6 +540,9 @@ GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window); * @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -519,6 +566,9 @@ GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window); * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -540,6 +590,9 @@ GLFWAPI int glfwGetOSMesaColorBuffer(GLFWwindow* window, int* width, int* height * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * @@ -554,6 +607,9 @@ GLFWAPI int glfwGetOSMesaDepthBuffer(GLFWwindow* window, int* width, int* height * @return The `OSMesaContext` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * + * @errors Possible errors include @ref GLFW_NO_WINDOW_CONTEXT and @ref + * GLFW_NOT_INITIALIZED. + * * @thread_safety This function may be called from any thread. Access is not * synchronized. * diff --git a/examples/others/external/lib/libglfw3.a b/examples/others/external/lib/libglfw3.a index 90e7a4759d032cb44de7e0551b3e9ed81cb9ddff..60f9e59f89276e07aac687c19f59c41103ace104 100644 GIT binary patch literal 290550 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

+i6tWWh__ZXAlCjk{JtVIuGWKs6N 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 P;Ag{nPtI*;uJHc>(O9DK literal 229671 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%0A0bd6;+2`j10!2y$Kb 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 z z^e#ndr?Ei#l*w%U@l0-suEbn)JlJtg0Ek$4w=PYg_;zWpodmKk8?6?a9moj$O-TtNe2wN}C;Kj4LL z(Jsy?Z?@R;h{g6T&EKTIknQ)T0-$jESw&u1Dsxma)EPuD8P_R4Orgr0rIf0gChnSY zW7#fH49c8&&tISB#9Je>@dF(A;=9>7Ujb31NFShq@#z8fV6dFgw)u(A_@WOG6`B<* za|xS-l{jPBoVs|JRsS0K5d!He`be_e_cAQ?P9*54`LWhwqNZOoV>df>; zgYjOU+20v=j9N+bn-K@ByL>Vecx@Aw;pG@TOA&*q=f=DVpmka z=G?v=;p_mzP5O>HXysF~%LWZXb}xq9s8iq$KHKRCApxaHQzul7t3dzf z2Nd~_LdrfwoE-xyhyMBJDS*bFd{1Z`3g4d*qFpzz^>em{@)pSC4J9pMWTaoxYyiof#*$akcJq|gsSk8rztHA!vn-LVKt3#0r3Wm4$TMBEnM96YB*%<7i~Z5`aAjyHt?Ihu{04UHut!?S1Lr0q@2`%Fu43pLy-ZEn@o9?)Ja ze(ol`ol7RKPBe1#`%o~9>ddGzmV6uj(Z;tuo&lE84>!=4I;5)3?F;Ae*poc^+10WU zYCMbW7@7(bb>ttiim#4s;H436MVNA4c%S)E)+!nQWCLV zz&`ocFIwntzQBt0w25A6$k`U0cUJh_h9N7=PVu8Z7e@P01Wsdc)%6#Ju<*NXUH$55 z45RNK589Eg>SbJQ*lee!{z|lNVPR;vvOn0g<$gBCKfRGMS$;*DD^gXgH zH<7;WJbpyt!U;>wvW*Bh4iwVEs^9OIJhQl^83tGV!e23L502u?NBbsn1`F=KfuemF zhW{k8{JPb}=k7{P6J>Nzx^d#wz(ly<40>zeHfxQCW}P89Bf!V_$PdxMDw3qLa{k_-uNVvMblKzzf?z&t2xWPiUzZ zKep#Tk3gMM%YR$T2&agzHd`d*!*s!QEW;2yM}kIG$nRW#-R6f2px+W1)ml85y-FHh zNdPtX>$U&w!PY}xf!A(^wn-xVnSt=st&boO>NhF(ZZ)zn>3CBEQcNp|s`gEU%m_lE z?iB*zoZQ$ox$EGIWzTc%N~k+-Bl^5Uk%2_azuOd(=AVXyfsYr8CyA87<2}C&qDi- zj*iDm;ggSuH|B+=I-T)cUld3z?FBHK<0=LCRClK@?B$7HZHDlXqfOsw-m0W2MTUAL z7PMlmtA`t`;}a7XtSvu`gor}&bU!V{#G2|2@2QQA6l~qZ{Tyntzc4mB>s;|oS;t#& z!Zt4y*$>GcS`?JTEj^L(tto3IF{EGm5uJD>M0APev<(emX_V!6`gzH~!aPeW4?vi~AxgNwz zYF6BD?Rkge-&f}gTZp00Ej%pfU!Z4#va2nR4*-&KUW^)wDW+CAnE<1v!pJ!52RbVZ zI%cWTJP8Yb(u9An&?q|$vp}xt-y;F0Pw|96j$|GV66Tm6?L5wIHZ4`;jedr?>lFGs z--=eTc<;RC|0Q7RUc-c-5^%3QGzij14`$+K1+=cD!aNVEk93Y!Q6?qk91IK3QB;#& zSM6;hU+A2tRz<6G-HwsRQ%f=4Q2)xpx=9Sljs5Bu?d%ystcXCU`>1=amp3mB&dxSt zRKOt~CIs9~%K}C(PtlFJxRE3NMOBaVm=FdKjKm;zDgb21mL|7Cd5vL%dub{ zQ(|42r8SfuffG)xS5<^Gw&xjNwzX&ngd2oy0^|9uX757FuNT5%9GLWXJKk;xSJ+sv zGlV5mH0Pb97S*cn_bnc=&!dauuIqkT&%qE#E!mQH?3x$Smt8ROr^&+Nv08B9j2T<^ zmvjN{d`ow!6D}HE!9E|$IRmiR2cqyh2Nn^j89r}fX5l$qsI}u}ko7jCCygbHxexX3 zk3mFmsAm6V`Ic1{(7??5>M=5PC8O3K`TFmyCFwjgabQ`{q@V!yPyUaowmG+dANxx> z@16f5-pS+==3k*{^L})5z1F`M{-b(dMgo~|yWq<^9%9Ii>A^Ok!VFO#pYB%$G zOYx&c(%Dx6vr`36e1+|hb0*J>kk}3_58z!lUoVk~-}DM!f1C}ZFIL4MtbDv$t1k=x zTx|;yGCDU%F}{M;PGPt;MDy)I+k~&ge_bG7w#>fVKC&m}w3Fr4?8c;QO|PDk{$WSb z@08)bT(1HFIdyhHw8C*}wXPwMyK|kO=t#|a4WJk9`^kbHG0C=zf>VrNCL7HD%0V7G ziYdL85j4Z1DJ1@Aw=%?pp@f}hSS%8!hN3wrNS5~Aes0Sg@l`{_yL zrWto@J}H(I*V77igns6@;>V%^tFkmxiq#gB)n`2plV;483`zND_;w62$|8{>vt}?_18aHrIS^)T-M=B`Kce}nqv2~QB~MmV7>7oZUR&%9=r)~7 z-y84`sVn=~V&F5>2ZeDD4=K6F{0>u9#Jz2inA?CdKW4@#EW(}eR;3FndU5~gO>DC< zS#sR(J9t=`yd3Msv#RKpu3x0?yG&R19M78m*g<;b7fIV8>Zg1J8U>}9=HlhNfVU%{ zBo*B4+CK6z6;QQ@g$=SeQI{BA{i<*~i=5+R*^Un>i*V4p3LtDxZM6AuXIcd%Dw|+x zB43Y|pSNL*5JW_!`3NeDSn_qJjvfKPRioyI!~tK#g&30eJLtwO)8N)X`>8f#=LsNK zY@)Zc=m9PJAaJe*3+4;-chH|nG1g^EwyXB8mJxAajlT*1M^|f^g>aV^9dwBq@0a?G zj+pRbpe&e=vh$puJue(?#e*cP0|a>jsk&KwY)`qcWOKd{IfN7fUck+lerD<~t}U%_ zQmsSi-kh+@!b<4b6m7f%4w~fb@yotBg8XVynzQvNecpN^7SP5zVmhOf2i9Z9qLwR@ zmpi!EE}F98tZ3H20F~>VGwQBH=oOX)(ehu z$I(Yx(bS<=aEJ-b=C!aQHzZmm14NrOq`cy{N6(!PXYXrh9m4zxk7z|_ z*M!|Nmh=$3CrsxZo;WuC79XBv`-65k7j1ep=tort#C( zRFsu;W#xiH@)2e~$Jt3K!r8&J!>|3flV(VE-ZmfAEOiqetOFqEwmHZ72;AU?*md65 zH5X|zTWtJV^)7Tlr9zo#HsZHTs&j9BVnH+ewy6}}Dxi$(%wGGb+NHEebwFX0fQ3Af z;+s%12bjC@p0Z$e-3Q;VP8lfSOv&Q$k{i4@$+xVhSm%0BYL#zSr0-HF!>xAF14DM1 zIxs&G)44k`Xg=KR00wl(wSE(|sX+|T!yErTn3Fj!`Bsw_AWMyveAQ<(9q7t?5j|Ht z$;XJjZkyIWZOMC29Hmm~<0v}&wA(9{GKEs4AKkfEV{MqFXAyw%Y_uoDCUwf$SE8N2 zhpyzYD;MNdnAT{kgN58R^{=g3yf}3%SdSV^2<>Y7h>2KiWh10>r$hgYaQM!G{uu&| z*0>wREk#C9#Jc-ag^RTA z2G~K*obfM#Ek3HR*HBJ6-C>>EtsOhA`HqUQTogtS8_i}Hj#=e~5EldWZFuf=J5zo} z{L!gE<(4KgT!ItJALe!U7N#4^I?3y6J%_`^h6y&nIlj;39joPN`is@;u8a*6*WF~U zkhK0{wOOX$L6^C3y999ohW^D)b?yV7%x&kXvS#Yp3ni}>XzOOHbCE7-txr97o|BfY zq`Q|d@MAp|i(kxzvp0#l&7(%bXL!c1Ofl;PlO&atz)0L?8fDdGqa-*r*Wi*}owzyF zhPP*sDP7))ksi?91*{7sNE69zQDz^0UD&8G5R5(Z0GA+xVXaSML9`!xT=?CfdAMk_ z93})HO65Fx*k{x#(gvzmV#(7de2(o8MGI*ems*vN-r-%(KsXJvjG=9R%S{G@wpm-b8Y)R|IFZzXNXCE4A8yCPKyhQzomFIkyNjRh!cLrDJ}Zb2pomyeF?Z2^~NX zof@?C%-jgG$P>eD7;-Ce!wXCKp|0KEeG{DI(3z9G5!QCTPO!GQU zFeS(2yw2*u{i9JfO{L~xVT&^&^4*7~1Ji)cx_~a~su}7P_BZg@uU`r?`|TPl_yoe# zAaTqV)#=3@`7!c)=ZWRKz!4TG3&*tr$M)s@!Kxr#mduwpnK|VbHwkY#MS$eMgU&PU+RNE4hZY)7sClE1IY!Wt%NP+HDe(=ArZW3PtTVC?)%( zoGf{2pA6~Qs%bYpWfh`&^9}Z|GySr>C8OPfJ5BJ$fZu#KwdO5(~gDdZ|qUuA2eW#HDGl9icR>Bt$2W1(rC*-Ge#M=Fh0)V9zy}l4{ieoDyyhDR)7-e`E`}&-kcDuxE z%Wugu7KcueFPCT=aQ2;JyaP>R8s$4}j+^Tf2ti5hdB#mZx1`&DL=`7mE}=K4a>H~Z z!B(^F)HES4T_H2t@rs`pSaKXLzGU?LGE%c9C4l#yev6b~RB?;x^xw!_r_KCEe9R9G zb@F@a$Y|Lp>#@4qiar2}00%|^RNU`jabaGOyPNi6?gK`KWi%bqiNtiOVZyi1V(Fpm zO@D8?s)t&4)TXC~<3*2hOL4#3xuuXCI>Ml(7F-giVsD9|RZq7S=o=Cyv51 zBQd)lu-y?maTU}5%@(Mb9c+^x#by0N&!HCz`M?OWa+=4km}XCAWm z`Q>pGa=jtaO6Ruj+9^V2KJWlWwA&XAQ$x&cbJUBg)4#HIHd!3#TVY3mSv!x3u8N{N z;VE`*G}nf<+H{Au<1ZTUP=D-SUaS3%Fm=bhR9Z<4Zwt9q-#Pu0_K#l&!1of~yi~}= z?@*l46X;tfk#-Xl3j=H&KwDnEE^ItwYNLi}!qIQE%OUytQ}m^_yBV&Rxa<8llMdK3 zh%5Tq`$j5xYqb6$oqwZ_EF%s~sEe5wRP?DNyjIQZfl6O(b_iFL~fQf{dY})co8p!g>leD0` zCzCFbFaUS4?V!>dXOqCuicj|t8fSwjczZNYKH(*Ale9vA?tnu)jXTF>m!Q!U@C(NW z=;eOO&RLS-7SVmR`K{T~qFl~2tyJ`HxPj^9dP3olG#L4~Uw*WpS1cIH_R>QU z{DXjJdvwN^dk8naMLq$Yx+>k4W z18;HO_o_?^R(1QL!;=%eVT*h%e^cYj%@ZxrM8=7TavIlTx7(1Q``%O$uJIR}jN7zu zL5O@67C#QRep3fJ{{V;?$<01653e%1RBT+SldcxOr;ZYJZwQH)6v2`Rsjr+(lm^%z z+mq*jD=@Bq5tOLE3QWUlHZkA|n%Ktmda!8iF==y9@5JiF8rfM*Y7uhDQ3@s+q2%0x zrNKA~^gorT2oJ~tp-NABVVm_DrH`)CyqGV)$mIj}3P{p9>t!{bhMp__ziIxXputz6 zX)E@roer94$cK&orwt>y(l#k@jg+_U&)_8vEyxKQjJ4M%SN z3Zf?tey;f3Gt2|r`ng^=e8V}j(kH8sEs%3L<1S1IpaV1G5zIm7q0d44aE3TA3!4KtMk{5y zH_upHs1yrUnyS`PtDrEZ>q6jK)6S1LtD-22)rY+$-K&#l_;qH?G*L{n^e8()DiW&{ zh^L}apG2{r!9t+cOSl<@x@7e5i_LXQB{L#`comjpd}mcGGCd{Fs?VZ-==R%{x}lS& zF7KtIn2yp@*D0t_g7At(U8w*SQJ?j)Xtl$(MJtMj7<92wF%-Fo>U7t_iC(BI#4XqH zbxjbR<{?dv%Pu+qGRas95i<~Smmmu})*cyj=oB*AHr>77Ue$0}UerNT;T*7O=p!VQ>RkZ$gN@t{toPPqiB`51RqPW^XLWD{WwZYLkDZt!?%@ zatsv26N$I?geL@~t@w9Pv(#&b7HBMATI5f#BYgeIM}0(Xxv>~H9_7Fuj0J^!PPZ=? zt_PlH=bbS0GgE5Y5g|Nv6ILu&LthpV_Aeu5N$y+qoi~#bX4xx8$l?i2;SXa)rGyXb zbbLd_{7Wr~xzf;X24xugC%ZAW%&=}#Dn6)Yl(MWfkuOOtc}p5vzcJPgH|ni6)eR=N zefNLou&{Umd5~(xjXn=;}$kfR;&SakTXC|lAt91gi4TA-C1v$~Vzt|$1uU4Sp; z!hSma1mtyMAqsiIueN+Mx!wHQ*-KK(XB?M#N zEJI!C5FWH(yqM5}Za6;zX_@QCh3mX;*ow}{sD1hif&W8nmFJ!RK?(W#nIBpKidS5v z&N-K8XK%wn(TudCfUlzXuGN1ICX}!EpK^h?8K$-W1xw_RmO8h`Cd&y?2Ed7#&-F%) z%4xihA`mUxbFKt=4I&t@zYM89h3Z@^L++E0fU&)2cJ)dBR9c_*)^b|nEH&Ph;j;!K z*0t;>|8CyXr4V>}aUUVTMyJ@b?+F#^n_Lo4^SU8>pILJ$VU< z5ts(y4os$%-Poml%KXi2!p@`UFJkGc6yIzs20qnDFseQh!&`tR(ie~*n)>)JK$&^y ztI();B?xaKcAoyJ>UjW@?8%ixp0#+`TLz(CTxik0x3H0;3mnT}76s-U`#6+4)>s>u z&4Q@7TY~xGTHADd)$61Q!WuB0c=vHuTG&BFE*~+nNQ1sE4f%FG_C77!~2C5$e_BDZG^% zsN5Cgb7xS`%p}v+qpH9YF;@Ef0oAU6MwMOEbpE9tkzGI*xY@3UBlGcL*R2Q|L+yh1 z8>4(UZffi9-p(u3z82KQA;o)+?a?TnJd)K<;MmS9AG>j!{|RVEX1FN`OKxazZiw>| z+oEl4H(37OQNGC5vgBC(;HUg_;_Wkii!(|&95VES7DwEovzz!{W7JJuf!CC{*2#+o(A;$g42->zGhm^xaJOxup#zi(*WyH8Z?lpdI)N|sL4%< ziWl1g*64L=qDnC(tww<_+E1i}qkW!3jnZ)oC-1^oZC+D~)wKp(hD|Pacblf}9C(9Fffe3m5;*^)#hPGY1-)Tar};2*vNe`JTPzTS)}IPa6odhq3{ierj3rgld8! zDD^j}ZhOyf3RM@^L@X9NYvnC*muIW}qn)Kc4=iAJ@5~g*KhQWKi1QolMeg48o%CW_ zes(P!S+vm7FLG+;(6F-{PjVHz1WYh(eLRb`s>|wFw0zT|4nQw z@mhg}p!t2EptEkw0?GZ!d7Pnd3J`agW0_hzfAsYfoKq5X?A9n)5-NnF$d z{`P5P|J*F(pi%nc{=INsW_sc9o0FRfs?80}tErR#Q1U`b$bm1MXEFsYYeXJanR240 zld=y?4uve?btAsOI>bR>_epfng{?sE6wYVnDYYOl+9=S<1 z5EUUSpSunb^8rfb0?KNPa@*c&xtm71zEEqu)HEVJAN^f1PlLfC@%-G11MV`#W~DkUdM zr!apNzbYG@9BLWp$siu=e5mE$&F^>C`fANNG)MD3z?8}2)=>5CX~I!PG) z8+!#xDMF7g?f25C)E4AocSf+M^%$UbOeT4y*)>n9wfeFW3Q6rTA?vnZ-Iqr8SoCgzJP zF7eh$?L3TB|tz zU@zT38NgbW)ACkK$<<>T`5WJ#huyA4i68Mod{fgyjFru;x#-L;JH5_^E>#d zlBOMyb|o)lTSX;O;J_`vF?|N^2fYO`hvJiPtTv^ovn7srgS}8ZPK;V2m4S^H7%Bd( z1}wkgCiFpPt5Q114xYtRdF!q=&-K~*9WVjwgn0rs{g46U=sne=V`RtWs2G1wIxH#M zw743ey-yGN9_QRZxqDGdMnhUigKL=D@bn$MYjo&xwD=|d*{=&otlUy)A>QoN-v!3J zYdDP96moGn0q$J403NQlceVw&X63PiK^f{u{H}OKmf@t$-hTeuR&xCet1OZI!&7n` z0O{n~%EzlXx@Fprz<`BVtvBE)N#Ix~3z;*{5f_@f zkb96TaGuQy8;#8LJ#4Ocyq|53j!;U?$0UYR=3Y`Hoh86bNEt-|s}SKIH9r`G1pCZ1 z>o4byyk$t&vq-1D)~DuM$Q;OQc~SfXKP6IKQ=}Hv%(L1Qv)`)Dex-G9?sTHr#A9M5 zS#OQH6UwP$P9|fO=)~I^X`gcnZ;QrOyc*yPQA&n1*KQ1r*(I4l zH`0b1aIRt`TBQ>$*HY@AtTieShQ7-0j@UF5e5^(qUaX%&a6=*wP^*lG*O01!MtvF4 z$^;pai{$K;lY-q?jB;;K;+sWLSCnA?yc)Z&E{oH7|DGttmfbOvb?GF_JCZl6HQ}~^ za;d+M+*ozO5tSOGPo=yb${I%|xP3sR#%hSEY8dm{7XX&V4FHpl7l;WEfp%NqhvL{{ zX`?eN_kCSEh>1C3!|Evz}F!r_(L$}uB6{%rzr^cZf^ zxlR<`lYP*+Tp8`belKeIMyk7>-#;hl&XgN(pdTE2owG_tO|sfIia|0?99BI7+3eX1 zNSYuWd)jy*!{YthWBa2v>iq7r3|O`AUB~zKc190>?cieyGtj>efat<^`?V^+&x)X) zfB}-r2Uh-cZb#SycoqxcE@|m56s#mLyR4V`C>L7uwhh!AHZuI5%_H_PscvCtHNpFc ze2!3F8Cj@Wtt;Q_(z!P3NM|KW|7&74c8lq*7?F_e5h2%&p}bcWXxOX)&=NYwVa8?u1`|NSwJMIbvdW@Vf&58a3_8KCOOJjKPU_+@ z>E9wgahWO{ul}R%`8=x5Wb1pEfIZl~gZ*IfDSCSj`977i#feG*cQ>1&pPYR#; zbeikPBH542%Rxw0*`6e=NfSPR-3Q2?GaQ@ zdM48D<~;iLS=7KFBB3H$1sUe;zeRVz`z$pYm_zibOge;M3jh|Zbzc?u!2sf(kw=I= z==JCVttDrx@l*-D&N!W}4*V>UUKJf$WMLr7DO#L4|G|AW4qK%6VHNJlA9b{ z3u7Qq*yD)lPR6aVaFKkKyGWpYOPAK-e6M4hnzZ@{5i9Tey?H7;YC)f`bEt1ceo1Hg zY3=850-Cd2Pw0XDL*A$tC$?FN1yf-Hi`R_b6Ix9bt?DITrd>EI88Mn z3xqa=<$~5%y$oxui|066yUI&scX4)qD&Lab?@2#E^>e5}`=A6)&wYA$icgS9MrP|j z{#2%#|1AZn%PtYSp?1aSUOn3|w))IP4)Jh&T*=T^m=zb*^4b4?xs%bEsl2E7>+f0T z+eF>#Q;xtE?;LIfck*lB?HYUBAULQMzuBW4bB(|Y4{S!Kw|Oz{+YH15^YC5MPu|eP zUpkzr{GE4o?lfdm9bSw6qyql>k@Xx?1*B!1o{P#g9xd{kd2Nt##0JC}1z!&MK1?G7 z?E|*ioDF|4TwjFu<(%6V@?@opW;sQ|3I53ouQN?KaS2l zp2`0I<2$g8%^c=1$Bj8v4s*yUY)+db)ZIblt~rECLXt|^Y|g_bw@7LZm5%r5r1PAo zkW)p`cMiGbmXJi@`hD)-e?98)@Q_`u>-xOkuh;X*L6FMMvl?DyJ$v>qtvFxJ!U*!p zKap|4D>@dr0hzPfc3~oPgm1Q$j(Ens(9 zM!`<_)Af~uxXZM}cFoBmW!Yu}9F0@15i~0Qzb1IdUgHhOOP|>mHxU2x#6K|>z;B4r zCsHIskjkSKr~s2(1m4t>;(@B$LimGpL*l(96w>bQAM@h`#V>#x>~*Bm81jQJ{-z53 z^$j`z=KjA^_{lTHXQ}cg2Sp#gllp7vm%o6mnkaEs_Yl?=RkTbmYwZD8^)5r?d{4dj z>7T4HQGl8KKcF`Zj}GPZNa*j=43c?MqdgYN0X&MM<7R>BOIi&amc6{GXWp?xS%u3#*>j z+k$;w(+@urwV6Hs)ibYldKC6l>#zeQzleJ;cB#PEjQUUL=02SH7wndlin0PrH=|8T zWoN{L9AJ1l?IcqSEOF{6mrlji_H?WjXo8o)4%pUXwUEkfQ%W1d%AfalE$|`H|W9ntd`*k7DqRdsU9oy3JGF>1MGcOdkpJEo7Nyv6_W%h(S?S05S9tHW?4!ax5<1SY8??o7Y*-e!y*j zDIF-$L0Q=00V9$<GFEx<^)?#4=6am?D-Oo^leX36AsfuetsI}xN-==!+sVLewsbf(7)m?o%n zzRLIYoy@ZcIwj$aHLs6rQ(h;eXysEwgQJk2=IDFF@(YSG`Ug1?anMlWF=chxYn(KI0{VBwOe(G@0+cX9IYTtWR&Pwk(I9!8u8P-EGoO$ zf&;`k565;_m#TEH?h~E4M~r9M1!o9c{Z&Ks3sLny;}06RmMVhz@NpxHH(}Ec(k>#m zoaA>)TAmyNMh&E&7b)85Rfjxf`x(SxJUBUd%uBzhPC2P>#QEM+lIEk^K-V~BB8^>2 zJfwKj;A)iwcIPK+2GL6drOo~)O3(j5EaDyRar6m{nzE6j%EpkL#KkIDjn)A`sAmJF zALpvR|3gLyHlNZvy%SRCP?W(nfg>d8zyyy17pbf%EJJ0egwcXVy9R6ug$F;&z zV;$t4v=+?Z0+2=hUQ=FQz2-$Ai)}ijiXVtdenoUG?#vxnmw=iU@Sg1;0`78}yO%>g z7%apLu^g5_JHZ0o?J6h*U>;O7g7?aX{9QNz0>t|1kQCvYzM85hPrKWNX0TQ>2aq2u zlrPR_oOGhw>2+V-NxnzdS5kGJL?sx4&W;BrFJupS<3;ip^uT6VXABDv1gbs{Ks9%f zCYs;|eAF6Eu-D);YTQ4yLhH48Om#>YQ!1V$c5f#QJ%N%7Rjiy7=b)N%q27*ana%@4 z?BbJjKVtkwhBkngm}>{A5&A<1hL9S+3*LN9UNHQKR6e|r2FpEnsa#*gaoVIZu+q$) za%9x#jMvj^4TdeFQ`Ap-ta4QU#tk$PW>WR$@V)6X?hNC5;tyU@i<)zc2%Q20l%FJZ z`oY)K=#r*Yz)YdT(Uih6!Fe=gQN(i$16S)UFRsX^zuuKh^{#(~@!dSAn|bz;s$pMKgF&o^I(%og^du zl?z=M_m?t!;FP_BDpC&?yp?jg_-ww9uXwHJp3nb#yIumq@uiETw;$pCS3!2?$U8&u`jEGuaGuuR0W zK7LCqQ%d8XzS~vII5-%9Z-N?9M7M@#3sLwL$fWeCVG0;yn}|c|SNTo|6;u3G8y9T! ze7#N^>!9ypg--rTW;)WIPZS_^HTUCG9Gf;JOHWh1Ky3SKy=Ok0B++rLZ^VVE+_Vs6 zKDx}CTrQxj$YL{ebN&~bb}p19Ie);HT84;XVNKLeo%K9GS7>?PCJ+9f|Ish6FI_j5Y&z&W+s|EM3Am4pqfq}no z1PdNq>4Bv@JEUOwjEI@jo@GP9k7vi%5fSHpu-g{Eqee#JSlb z?VDWm7*o3qOn2KB!m-MzYR3OktLw_)Z^z6h#&QYv&X*Re9?S20!u_WY3O3@v1NTZM z-1W|74st#@GxNjwPIUZn<0#QL1^5Zib5I!j00Z-ZkoHVn%gJ{U+7J`&GKuh%o%0KF zlh-vtE9s?ybE`SxgeQB67Pnb51#B}SyR&oo{y1YKBcuU+9#n3dFLq?@Vpf^NFz%as zJ8EgV4fHplw)db%)(8o@_M;MYerJ|R9{{=>`?a$%^)=Q01n_U|Bb^K1^kV;vb|Bt9*v4H6HS9-51oBaSDcTjw`*YU zv-UN3e=Z*q1Ba&WlrYjoQvNIzqeZQ3v8OzYlN27+o;!GOVQH!0qkEi3Tb5?(WMp_P z)(dkcB+a|$IoO0#B`tARktn?{NMrt|pa=;Hgs8Sc5amSLS~x>4W1l$PnUhR+&~PRP z>3B{?s)U;6?1>I-%4&kl8-u4Mhb-64gp=VaM`ltxx~5VQr{^Y=t7p?Ru2Xl1k5$QA zSI==mt^sE2B~bH42+j*TGoJ$A6tYe)LwAi**O6-PFtk28nf)UyR|@8{q&)^$-S8c1 z7fy>l4=|{qGi}jBW4U$$J%w1gW!(c#He{ww$0sQIAZ&+TEZ%>OHiO~aXaP7VtD}tR zv&j9sFd%Aj2Ei=4NXq+V_9Men=cX5 zOl{5xfnjkfo8t?6AW(I_N3MmM8udEy<1%mjkzh3LNL*EfI!gQxQCsyZ!J?OSft*X( zMEF~u{Bw<|L<`AFz@5<+4x#Ggz-0eoW{XGMy`Hy-A1oM=QC-aURo#5p_ z8fsr^h5Zj)K43BLMyGfHxTMVHF$kP>p>W*dUqNn{M@hj*Top zp*w`AM6=b3)`&UumKIZvuQ{MCOu&K*$rB%rvS&X~Z&7%vxZovqVgpVw!u4IqsnHthxu@%=BZ$(#@?~iltZd}XhHfG1rjh5GDh9Q z?v6qkpn||mkOy~Ds=UPow=G#kx^B)>FxH8g@M!72!)~!D?yDuPtz;~ic!VPN=cn5r zflrB2eoG~=1EDZI7didM*aX!9e^XRZUo9Q`h~Bo4wyC^(-#ZUGL(y~7ffUY$t+o+jDrmx);t5{aXNs~ib@D7S3hM>8O{)tyaGN0Z1TpR+Z;Dy^ z7M4@d(MZ1KF;4@Hn`#FQWIiwj>qWTXa3w!9+)s)UGsGn=Yzx#0GY2=;x|op+lL2jC zIb!Q*ExIK-m}sXm$Y~w3V$=+!#=tx1bjn`SwtrI?qx#rgIiplRWO1knhK;b7dpE~E zrRZH%>k+CDz~*;ns8Nt9Jf(@#G53fpu)HgSJ8$p7et~?mQO}E78<<)dJ9mg$H~pu= zj8Xst>lj*_qcl4NI16fwru>11_^Jvs-=OB8I6PrQA78x(U!-bc+{WRmOVKCoyFoM1 zg(MTAWz(q1njvWNkr49WxiNB<3rv&%1^O{O_X3eRngh4{g9==TfLBopY0o0>&%)!i zYpazl7S}SLXuaJ6)t=#UN@g1f`-7j)kplM~WYOa>!88i;+F0Cx0zMN8R_unF(`gAz zEg!7yKQyF=1hWNE-(Q&>zLgX_E%#e_os(pd{xpr`^+;IUj#*1S+9C zmcFZ;Y*ICt+{SNU_Mx%pnAV0SgJDoMIj{P}rM?Urt6@Xo*Y=fYi#y~VB6JJ}T;}Ut z;Ik0RTC z+zh2{zieeNe)2Q^R&0nHgjp?8j>8_S{>3#QC?>RdGw`ZM4o$gGcIkyB%HWKJfD~Nf; z!m`@*O#_eOu;5d0oyyvXXhM{bD%9%Ju5f&@(gd{T*5ppZ1~o>>OQ{lh`*17$oZe_p z`#eNjFb8WCIX5>%-HPI<7x6dkbQt*6nh`=yVS4N2S29glDEkLN8;SFiQ?<*On?HXB zqPo}ZgoII!tluuIlgBZB9xi%A-GfS?ks6(-Fc{5<_z8D5|LrU?3vO6E0dU%1Rti1{ z;F}vVNmQGP`1O!u7Y zx(*29mzDgzIvd|9c;IQczgOE-mx0&l)io%%SEnmLx2`tm9{Yll(IwpU_7r$m!U|_v z0bahh9dq-R0R4o0&)B?N0Es%kJTsp;I4-|mu>-bednd-_rq*hsC%|#dXYUPuj@=>v zNnrV1-$oR5P${r=@!V2=vbxu2K_AvNOQ>e$=jxT`wfQ~Ho1f#gQSxKe?D_Y`tR{n$ z*ouL2=~~MKOhj~5vNal2{QLrTv+ z_}ZNJ|1EqTAWxCCZi~>=?Cf_afYVrTrfEu!O7y1NRq$&bDXdENQGe##Mk}t6pjs^V zgH-1Fke6wN`W$X9ugLCVX1-?P1ZN@-9Y8Ntniu_XOMJqG*uw~qKN#Q|+LooxRHm_9~u6OCN)oFQ+{>q@0Fq|z`%N-`)qE0ty;H9G_i)nx6 z&Cnt&`A)#5?x_B4EY6OhG7@jM3t)|TaQBTxuT;ZnPq5iaSmU$edif)(f%|{8>XM?9 zM(!hWYm2P--%>|)p75Ri1K^zi0xG`;knE2O zP^!E^5B7zb+=oo$uB|w}Z`%hhYJmN^mVAaG`sK9k+bT{v;Ql9cp{3iM8QOw-Vd+#3Xxm*x_N(X?8i_OU~)QhHd z9ps0XqR8&UhVt6iOl|cP-h@MF>TT^fW2=<_&ksn%4$N!tO*1Wnk-BRMas6;F9N z69|h7h(2ZO0U=;u43f(sLoK+ik@cO%q#afRn)WX#Xvf=XKFS*w%o{#4OO~lW$hW!& zrE|?$CaSAYLF!{g2O4?13eSi7x9~iJRlI^Gm9U=@(tcXBhZ-d23&WB zg4J{BD4^)QQ6Yp{?=o@fRSza+mM)FYwt~uKB$iL4aTja~MHk>qC&*0Dx<(M5(-woH zOp<4L-lrPgDYSDz1D}0Hqtk72OA(PMuGU?c<;_(@RwU|(dFZ>X)~^zLBF+wsexprH z(mv2(VH)71->;$XJClxHw_}=y+^a)>s9RPZJylz5867G5*v_7yC8ro>b30qqOwAWZ zFlK_Y#2JJlD;He?JzgdX&MQ|E5YIQroi&w9RNpk8V8POX9Z1Usezds1Dak5))$NWA zn+$PeJI-&=MlKFWUKzf0EKG&ys_h)=rK`0 zijQHNGmi1>=wE&Eb#EW0&xCr9Z>^tm8UcZ{1-BOYU&kF7?D z49K5WEo9rm1;gFVE2PUcq1hR#qafah(s!!hbb?~k6HWBsa=qf|ki)FeNe;UiU4C|r za3~TGYhXR1gMOgc7pjc{ypDmw^|2rpOZdQuD-xhiqf(j;KNPMHX37oSpch<(Fn@Eg z@g1vrGK9g7C67(|I3fsPafq+7C?A9^4Kc1@J&vk7&F~EEVt-I+x@FL=}0`J zXzGOr{4G0SfkeK`xAGQF36#r|i<87V;8qt&i&D!G2z(Hb#Z8Gny7X zjIQg~ld?N^OoGiN^6+RwB))JUHRceZ-D5H||3q_!kdyVryrzQsU}-^C*kqa?VUUms zf9@q=$66N^03v`ReH&IWG6>NyW~#o9jDGIL?Hx&bj8-r5)rr3WUq*-T5^(DGcw|7y zE54pP!tBz8o~vX#5UONz5bQp;2&W>|sd@;~Hb?P;2vUa}r=R!eu^#BW6lx>32DFnh zoDf6nLfI%6S7F$k(LfI*RMWP4KlWW?2fm+$w!VWns)Ih^3fQyoWznIqxa(e;17BbU zCK#6n#b>t_!Jm)TcqJK7F7ETN)0SR<+g6bN!Y*1Y64{yqvVA)nVuI>Ds1b)W;sQHg z{tw5W1&CYSc8@iWPjBGZa8$@h)-(#W;}Q8cy5$(CHrv%R7KpR(h581~-A<#*KEzy| zONZ1n{{iF0OK=^{;5#O*&sA2*f7NUrq3z{ly*>cV$q@_nw#|6WQ(VCZr-G6$l5a61 zz0S5}MHu#u=Wt-4HU#WP z;n*8~hsilRr1R{<&oi;s{8{gO*I*YiJHz&&xxpJ*&u7&Y<8{0C>_&~k2Tm;^is=z!*aIo$BwTp* z38XJY`-_m{&$jz>)UH5vJ&5U_X2&hDf3n%H6Cxh47E@7Do2R1264f@5ytI~&`59)( zS2%^)Y>@o=S1OqE&EwFs#v4%GU&n!=>IxXw3nF=6N7yG^S^w_p@lu{}<%4X6oq?#V zT|3mynEYsknTKISkHSe0u!?8_jJKxzmCe4eW3}b&7xqQ8vT34wA-DFXha!n#|AwMGaZ?JSk=G=ra>T(y{Wr@e?rnwDsLKZMcqbWui2 z{F=S2-`=c%mQ%M}Zj(I~wDcB3YZ`7UZ&u=6NKG391ULen_D&> z6DQ}v4YWXmoK*AD71|A-LJeag|57#4TrT*| zRmu5?BR+k;wWPUCatS!=`_~n%T3OF`1$$<=z&>q;n*~H)Ym9UVUQNA!Bf1(fMoTb7 zBnWN$e049JkZTzf78_mK|*ktxH^xSH}loKPuYDd26x)50}R9k)6 z{s&6~vSFqRzXEseQ9SaZZtWh0+&}7_rE}T0IjXNG?KNK-{Z?*rw}(4v{b_h3WyeYK zqg-Nq`p?vqSY}~U@x)xKp|6_r9m7nsi+YUUP&uNv{AoRi>4>(xjLBbM-9zueWo{yh zVKIkpSK)o2K7C*SlksEws$n=+DRjJ74rT!)ff z;-DUcN*Oq%HeW-RJry+jUf#@2hj-vClGZXaY=-_0?k8s=hmO>*@R_ta{mI2uX3miX z_+Mbbqj)>OV>fps(jr52t6?HgK-8KCFya8jF@LBN7H=cokNBn#>fxj|zV`POvg1uC zvnwVJB&9myHuCjAEphgR3J`*WsbKWq3N@|B|25-nk%^v~LxW`0Lx1FPd(Aex#>OHc zu(jkERGT+&ttHx7umMCNpyB6v8N}Gu>8a^n(JaXoA8t`|ce*W^CDwL<8NY3fk3_8~ z0K3Dor15i@k#u5O--Ez^_M(5&?xwxqJC@h{R;4 z(R|i_kURW+3L^MsI*pHnawlS`T3d0q zHOrjAxI8Ydw3A)rZZnDs9IB}QLfhMYO%Jn)RVm5Q14de7~|;cPt5 z!JU85BAbM&nOdxJ6-G_*-ymW5zu6r~cVaG}aM>jXVj>vG5N~z|eQ#Zhw5xsgN+8kc z9>P|A;K$NR{s=845YwBUE_+4VT?rKw9jd*-$!P%l&(%&khMZ1%5uKrnEP(Y>C8iw` z2|an8y1D7E{&J~h^a>xd9Aa*2k86$>VlU1%P~7PoXDJD#MAqw@^vCM=@8_u2UqKFy&|hZj z?Qd8$GjfT)(O`tPZW4~udl2^uR6AYj^$O)KEtBqe(eLhq2)kj8J1f#R?ivQuokQPg zGl_D=9)(bWS;(9V+%ja1a)CS08PPoD7;#-K!O{Hz{V`Op*MZ4vf|9RYif><Aj}NR7aMd;;AN@ls zPE@}4otAITP|M6i7(Lax^)WsjUbrdrVa0EyVU0=9`zo$)ID*nlt%$-}#hmdJfY#QA zTF{5x22Uc1C(tK7RrEFD%y`@WBR{`OmlQiMsWn60POx54=f60S3*f?(!whn}udWeB z%*W%H*+@PwZ=4o0F}l6L3do#wW#+}6)AV?PfIF&eyqQXfG(bkakW4woOexgEP0Gg< zsSzg)pK426H_mruwH%8m==IXy+-`w+x8fgJ7s;EdV!V)K87L~XsSP(TtK~B4%F~8b zEpX;zFS|P@$?rq?bxk;WRmBA@Tk=U7$H#f?N z7!+Qlx~`$Z;*RIPiB#KBb0^U{u#==;{0eR;Y}kp$3p+WH&Vo()vaRF3H*!^(>S-;Z zNuk@g7RvZ2lFj46*MwR7^lbzg<`PtrZW_G7=9lm- z>p0+x?=m|-!Ze0BiQx?AC*^WO=^J;fjqM3Q_Oc^PUH|p?A^WbjZp-Z;SJcJ@+Ka?? z_NH^&BG$%sWm-Zj%=QzHs`nXl1S7QHczNNw-N{_XLKzEer zSUG(+LE7K(_)*1kyr|p#Uc&7qWZ2Cu5l%_ujyzjy``@}yiQ2WvFQmV9Qbyp%9XphQ z+=o(7-j}@(kvaV~Mw^X(liV!823EAjR6YqrG4(Tzg^<%Qk(y#JUorg9O~xKgc%b?<1XSshHbp|yFrZcSN3K1+fW zom+iFcY*!up`7a4N@fx$NRDkWoVk}*`X)`uCDo+I~|e@06{Uv>-A(N zZ0J~DvTQ!HQt(d3#A?-FhO(aE|IKpI{4IW*3Ew>=*pN>_$0gX&-%+TZhdpHHfDrd! zI%HTbj{=I*g|5b*Z*ZbM>xObg!AB3XKRH&@^84Tmqz#}me@TI3`6!HM)gA`>GYd1! zM1V#3P(0_V1|tHp2|GrtY{RB(bxgP<^NR@D2=z3o9i)19aO!A^o~9gX z{zqv6sMc)!2L;ct(i?$+!wA{mMWXVJTBnS(So2Ac&FJ-PM)Qnpu?2B zb;o+64>%)@F!L&jFycr{g-~&%1yKrHRt|t?y`A+<0m)h0cKG@d?QOcIqVR;!a(r?o z5+rtkEOcfn_tDF=a>duRkZK0d zv=A#^-xL(@g>Ta2i}3)}0KVFpO~kl*fE>Gbo--O-U5Ff}#kucLav?PZgke4HPw<`U z6y9ZMYHlLV9kfgQ0oCrNE)_Ex@-i=`hZ$@Bl%xyBX9FILVIA$AaaWtCzzVTKZC%Q? zszusb0`_wD<~YZWq#^|D!nv@Pxq*U9rs5X#>1fu@H|*!!H4-z8@+h3X!xH{Oj)I(n z-7cnwgdNFRYtta&WDGL$^f`KxD&>g|%KkH85*okr7uZmrM~A{k8&Sn;UT+(EwfSoW zx39o;{)XaDs)>Ig+xI7aXT9+5$!vKp^RTNKg>cb%#FFKplQxx43*eh6NX=Q&R$Lwg z(KJ^fZ7I5Wa5~F9uga_OUj4ua|7=DQksrnTcPAY*Cl4M10DSm1sqoWUu0@*^&qnQ?Y@SrmT;;~ zP^I@^6X)S0jrlJ3F8+dh1g&AHG3%X3GRjaNTMNIz6sWUHy$9Z~ z@1oars`S-1h!L6PQ0)@>2q;$W$X3(3ESpg{JwWfw@HRl> zHJ(XTBLCac(yGRrY{+>KpLhEkP}2C(4+~bAP(H3U z)QZz?H>$Y9-;A(;Qiv3a)J@{KKUAxA=w8CFt*~Iiz6@~UMN={5I%+k+tK+v?4#eb z!#ZuI`KlMdkCMJMd-7c4ptgrY(Ljg%1FY&<6)*7z4~c(KtGnid$#QsoAygvFc)LC( zO&XeKT2P@^R>s;zP_(uk4-H)+S-OJ=0l{FSN|~Xt>pIDIV}p|;_q1A$;({fq9ZsQS z&7exH-6{0W2;)|z`o?iuXeaAC6z(t~9?UglnDlX|oTuH%*Ny<#ign?ct;yB^Oj(mgnpkjiH{>h``;DfwDg=NJV z@5&AynY4oV;FV@eNCSx%pkygm3Hcc}qGumQ-+DkCp!`loC#)1$2dxoyTe1IwBUK*X z%PKVJMr!5O))z@yvZ=^zMDiZC4~g+2(tB#9>ao(ks1e$^F`E=-w{o&;!5w7&PUpQ# zcA}@kf!T1nvFY~vGQV~GR4&}@tX&YESixAi-X9lT&*d)%=G>c=!*us;>0uy2W&%DavSn@64Fm|X2j{3$!< zS2uP_e+fJ>5eA8o$l3y~>h=$5&5&256Vax4Cs0kxk}2w=NBl7EH8f zv{e~u!>@g%N1=HB3y&Y6_wZGP9>+H!@s5q@!p{l=1$Hk5s(2_n+syt^=z}Z1P~{i! z@c3URcz0^ns4JB&D;>B*RY5>DA$~zvzY$;mdbKa8?TQZdP|whttTrok=ftyXpQbedQ(+p_?es2z;+Z5I>QDDzf#%5OA#w-Fq{{yA=-m$cVaHm}T3uUh^|gWrq)(xyX#KrtuV4iH6$bvQ9=sVINg z43GSVmlDQW`a!shfA$S||C!<*v*>nvWV9&{{_F~mLoiqP!0&>ckc$?n>@yq3g2Sn3 zKBJt_>Bh$;!ySH7t_PMxh8)tifUa;7x3FB+!sD$qzf*hlHFf76t6o26zcqQaoi0bW zg$-L21t}sc?1y}{L zF5`{crP+TZP%e+kajRAuF7(e1Y`%Y7MbWUV8(N8kop2x%-;FFaNML0@U|kW^v`Bm2 zXQISjjGQM8ePPEMwQ0Vj+6?rrkE||L8h6ttpTa>#W)6%vG!h`?6Az%p_ZpV8?3>`t z=HM`TPag`}8riHN29t=TdYugOU$q)LpxOa6A>8|4Z_%w5VFOWnwxpdMtjKwL1U#Ta z=u;zKV22%dnaN%)O`h)VUo{J2sSMLIE^;@<6TmK|;N!OHEc;BN8{c;%&EO;yuU<`j z=Oq{SRB;c;qrZ#j#5~9qybtPCy7?Hl1asH|5mfUh=dvsV8~7f@+?=I_+aYOcAR z`%6-Vn|_!xjE=_*ZuTY`?OvMybT+8NJ z^tJ%Qp8w(0VTYe)s!kO{I}+5}JvdBcPo#%>-OB0`uD*E+jZFB53m*_gjgLpM#tF!i z|5dfDK-&N;ZKfrNeE5^cW$-NsAK=wKMR4XT`7Q9Jzq&oup70fWztErRsf4rw%=Zd* ziUs;yloRfP3mUS#D^N3_Y(}z&b9b?AMBjTXn|Fyqw>4#TR`9lK6}o0+$DsImh8ckU z(A5;`*bso+K?>QVd3Hr(PR%|ozHNe;G zb*j+eyb9-OVaV|@3QEK90X;d5aAUK*;>m-?0x;J%Y;JpZn)`)w3Vr<}MEMKpdoI&1 z-bcm%5+GUp9dyEJOm`3BRYsdWfXF^{71-a+SA1eHUd?^f2iuudxW9V{KN@yVYcQaw zXNxh7^OiM7=}-(?;T9@qQL$wQx|?9vpMr;pj+I=m&Cv6pPV*6-o(-rk+M_}gii4hT zpJa2f>)P3o-tL}Jo@8v*tpX_R)$I{WJZzC& z&BZU+Vf<#EexZJISDff0))rxrF-;4J5ryH-bT`T86?D3zJm;9^H30skauj7UhkC$M zWU!pHC>nL^!O^jP)f?~`@;94BP#*Nctx_9NgXV`>cQcUjD!GijRl zAWFJ+-MJEdaKKXUXwkR}DHzA5%zIDD|K`9m^`)_m6YgPvll0!9*tGzQ74Zve3e}z+ebfPo($-q>oaaG7dTJ7k+6r)CU2qu^rZpOvx^{&6oIk( zev6U3TS9rqe`&_KY0KdI+Y~dmOxp5K0le4AtSAc#KMq7SQpu=h*r>waV-Wl)d)`EZf(?j6CDO+KTx}=5|t?zfzSDjlXb5J^5v;#eGm8KrEMapXM<;! z?+M2`$9Suz#>jP6nX*K3B)ihw#FOOy!#FrsFQUFF*B9AdtF^l~jOVw~cAY(m$si(< zNE`Lr=?F=Q^Y$b7r?KMC|xdxl_R@& z?*`d`(fQ|0Th^Nme4u{+?ZkEgbKFTEHeY;jr}(UXv)*6WM?Yysg@WOT*K#!}wW%ukillPj+udj?ORjzT5N zFJ7UaV|g1GOAfhT9K=DJ3+zzaVNcG2VBWhh%fz^PtT7IMGU8NN(`C%bv7@{d<`I8c z9I}Ee|CDqwS(ff@bB=wpp>(FZLo#Vj{ue4Z%hQf^(0pmQJ4kMrO^IP`gY^{{T&Qo> z7C+XX$b?Gt@#8C~d&pRa7s=j%l!3-e@GVBBc7aruO)i&UAA?!6sb5tXI^H$=eN&F# zY-=K;n*Uw5K5AUIhJ?7Y40&DHWq3U*_M9}q0(3lT7VYAk-(66aT!kQ!K2cU^VI{M* z3Ocq-(>z(x?7YLEjCgmj6RK~-{E4{G2zjGy`?r~wN1~?n;o~TG3Ua0kk)dbMGP!-J z!)H#lM^PJdxM^a4@+r1)_7;6)u~@PZn-+$;$RS%fxsBv(vR(BzHxa~Gz?J%wOb1*O zlv*@famI;>3UUVUs9Ld9*OuDoLXw=fkZi-w=&^KGuBbjar8hLk^7-J~)iF!@+P|Lv z(2^nzq^GIsH-RwoWmMPUmDLt@&zGfwgEVwPX=_$TgBDp%(d^m8N|eN9LswP|scyMh zJ8;x%e^UUAZ0Y;IP~r&7jIX;oW!qw<~m zpN*tho~>x1WT+YSA-Xzp?p`5&@9tCEp-e$;gAJl?w-*Za9H|00W)N8v)jYAz_-t^K z#`*&5qu`mE)Z6ncEhI^HVm@^R7+dRmwJwr`t8(XmDd!yrZ#=p=orvoDoLJPSe6+*w zxB>Yjg4G!?4-Ly`aV>$w%LJck+Tk|LgjU2Os^}j=^itKWhP`)HF}rbR1Y?Udg;VfB zQuO6k4AP>t(xNTRlm@D);wt#fK5gN`8l!{9jXA8r>bQNayp{f{%Dicm$Vf>N4fu@=ASfs9E6N=TH_WUumM?5h-07i2ByZnvLS!dA7ewV zU6>PZY0~ZMy{{vaLag>)=c+O#EPItK^8pBI>n)X3D#!klI<%}{B206NC_&mGZ#@+i z@}upMPyVbQ%CdIt{@lH^q_b$Az-zq3)}1}vW}=|cc0;QGuwZk|jDN_g6^zA6GcsX% z)~U*2wvRPZ2ljER-z6IsT>5JcWwqm5>iuT<&tCV;a}-3ZiueBLgv-n%ImV0T5hi27 z$wYlubOXl~+nlA&Fn{Oa%u);t4j(s3d{7gLk5{Oj%kr_VK-nd@c-cQ~HylKn!rVTnZ(-5h z$b)Ok#9CWasuumW8Oeo`Dfa&2sVIrEljygDHgRO5TbQZb6C3@i{}$iNZ`cV(T+qrl z-9*2c>=}f9tlSp>8kkX7#=L8i&KAH^rZmCeaql4P{pNhM=v)+8P88X6M-ryx?i?{D z{EqpQ-16DfXf>PiMB(&b@GA^{N7}`FL3u_P4u5vwwo%J)eShJ4{CD{-JX{mGIo|9A z-E2?~p%dqK{tGL(?Be*e&!H04k^Opp!E_&G0AjrycMxIq0(R1tjgIg*&TDskLz_DMf8KbtN3UP=!c-)pXfwTx^>N^Nlnb1JztF; zlD&^_NN8ltVL_b@)$fUD4)=ba)!H_7ASSl;vw_|Xjos)mkau96Z#wK+NH#o$N?BBW zbtrf|`>T`9sIBgv6(>EGE#Zm&m$DXuS)g_Sd*-sS)ze?x0>3K&q z-@bAUy zKLsMq3)?IC^zGihZq5zubVC+z3JSdKET@fz{J;13vq z;q7y6sR8m|rk_69bjV*#(HNySL)T>3l)=xuif9%FHJla>ac|>W89%WDHBd4xg8kMi z`tTY-R`qz5hJVR!i61D~4`-P-RVw>nE<6T((#HxHK#tb%OldHfyD9HOSwz~}st5;# zU(FgWz%!jAIA7wQ~*ZtPp|b zrOS=rf$RGPk?H!RZRF4w+Tl~^KKyxRfELxUVV-oiCv`kAXRDIoDZxJBa2uN<0GTs> z@L6*%%{Mf2dRI)WcRpc&k8!%&-AHooAc|snnP&c$+g&SOSMc1B#U|l6C!O@@c;%Dd zQc=&4152n;&_WrXZMb@+{5*VkgDe2k?;F=$anf@Qyt@C-odSk{BONVG>_XqcitDj9 zwv9hCs42^sQus1NvLPV$ZG@lv7knkyEqk~d&U?ZUpsSQlftL|KiJeMTa8|?QUg~YY zZ0F@bODhGJ?qc?&Mc_}j$(QX?w=qZit1jTyB+b|L|6Vp8*iu1=XDDxL=dnNcC+Q;N zHw!qyf!q4dMn~4JgpY;Mzok%N|Iz4?FRa}rqO8R5Zq&?nr-Uk9pyVIzc_T%_l%1oHG*8|#PI6Z=CZY(~YTV;=y^ z*p=LP)=8=9+67-5#zLIjT*YbaY(9-)>=x}m3I<`9Fqwu=6#GN+nKX*(U9a}O=*ZB% zf=ankZj1iXE451#EwPmD!cNuC~x`jhhe2dx+faNm94_j&=3uhZH#1UAoq zW>3d1l)eS80X7%q{DwR&+f>5I1}gp7`&Y`5y$W!7I~DG?jNkB~6L?F<20JYos355> zBEBhx0|^F^b;OiC5m$NA)2ZSok1Ctw(H*Zf1$=6p*p+DG1ko;2$=mQTrU^QY=kIEZ z5|(bW-#gYLNbB>hV+Eh%GT`=@*;kY;VaM&Q9)VMzF6+Biz{}A%*B9c3f2g;AWo_i| z*wWK~zPg+pmb1b!px2L#M{@qj{z9l!pe$|KsS~J~UoHpiI4s*yUYz~c5EOi&9+L%LDZN`h2eI{eHckPs&3=IQK6d zV;m@xzgS@n4Tf8FKwPX!iVTp{%}`;*&i3 z_N81-hl4uuvDi6yEHFNV-(8Ez6yG9Vzt{Fu021gX%ZZfgsd6Jy;1wF6AEDjWV@DEt0Ia zviNxM5|KclYK^3i4hd^X*U!{1sNS>58XFw!71cVlE&e z6&Qjf>n&Et1nCU-6#G#WrgRpx235*y74_$=U(BQ5sM^3*$5PG6X1 zUGc#Qq_XW`Aa*@$_o(_2S;2nZm`Wtjoo__cdFYS$?9|OZp&Tt!&}JN8a`n>{ zy_z;Qr{Sac_p6w=0UfLCmmtK;WiENY4WQc9TxPp}Zrn5y92n?_iSGBZZjq?wd}_NM zAw`NXM#dIP zSyf+OPb5la8#x-C>Fv?bGN^W0=82tV+i^RvzC8EL2JJn|n!@;GANobFKnKN~^Q8C=W!GnFB2{g@ z`bKM5vIOX3SB?9q9J+(*@d3@QDT{rrko9?-(4TO^Yr-KsCGWrV$e)J8yC3^FRf15oYm_;>x$66Sodh>- zR~)1pWaYUCGje+O?w|oJN3*#$V@hS}({MH)vpHlGmDa2q#^diWWNr73 zxmyU`bKIM=TUyUes6(C#%z3_IUYtOvhb6cocHK8I-Ctd%7I42`NM5_eV&1OC#HV=9 zKU?FFp;+bGDCm8+NV)q~H4T#C`d&R}+Ky)^Pa85vAa3zG_hc=oJ@M_RsW`~h{a#sW z%>bv&=g#e~N3tz*ac@oz+pAli^LkEiwf5C7E5LcoCvcR)-`EXP3vs)wR)9g##E20$ zt%x}jtE8;5^FhstgshM0`YF;kvcar)EFN6p&U-7y3f*e`F3e_a;LSo0w8yI3p;u|V z>70OzB2URB@ESw%BpiCaPQ@wC5z$bqe}CXO3!n>LD_m~d{a3XsgE&+e8H?u6#2OU1 z01m_!yKhP`o~d(4m-*E(o~=MMG*2q@E_MOYjTUY!98B2C3|S{eqL%&24WL=DCIFv_ z4w96$bDs}|w5soq6tJBvH=*+PT1>RGqAaKXW28B4S1!QPG`(MGQmg`(S7lMdi^y%haRcBaLYNQN*KG@SPBU|UP@8TK$5ZVBw^nEy-k0Ccv_N5X<$z&_9ry}tY>Eh@dESW0Pv zvVM_EMJ~ml`n}J*mGk4|eA}BnuYgeI6=X2RBD3ES^*?1-kI`4cX^UDb=?F4>K74y- z0Le0~W5Q(fveNoO6`&{=ih0oWXe?h3-M^`46Mv{n=`me|c6{H0`S5X$boZi`Bz4r>v_W!ns@9AiydAP zW5RtmpTfxF9G`%+l{{Ca{G%jGCy>T&gq6QfJgUN6mT|8}K<^-XzF+x}_lV&#jedKZ>H%4BP!7g@hCMKfW_3*UJKvDNU!N?|cenhPxrc_-i)cJBMw`nO z)F*UYLvKa{Sc zX3xD6sPOrDgu3dN>_U)Ms>?g<`VmJF3l=CXo~T8PBcLNS>fNn|iGptCj^G@-a<2Z^ zWPAiSMJf@DRb+2eaRLwZT7(Q=i^OIr#=d}L4~=V6+>lqsjtyZe@pJc)*_t8Xg}wLx9sV`>N_*9 zkV1vbETyxZSFoW%F;x6Nrkf`nogh=qhsxDXHmkGK6zY4GI=BxU1cTSS)PuF1$>mx4 zcNMrw!)97Ez|Hn32V?Ws0a)96wVk=oH8_d$^L`fY z5*Hrfpy&=by6Ejx$9>z7=Wf`}$ua8sBLq|3zeRr=%ISQVtal!#vL9ovA<_BEG3}9+ zP{IA;s!%F?p7k=ByP4Rt4`lv-ln+-x^fY;Y&sM)co?yt%&57EGjmkFo)BU?O2`3W! z{Td9w=w4D~-^Q(<6ul;@=vitRpG5y1Y=Nl5WKDZ=ey}LL%kZ7~X4!W#`KDIPWx$sR zK6t5_2ThMx1o>YSCQG?$)xXa|?;Oc#P(FxgJ-0gBOxmdGw9?#V!qM5C>QMcCZp!aG zzU_D#ofDx14oJH6KRC(Ul8 zp#2oU+l-Hlf>rN2`Db=qu?gHEGwgK8EBpJE^39?l$T818FN-Aq8KtRCC!dUXywk0b zw2j|Lc2P#^C9KNImdFq3%MR;-J3A_^=QSBOh2Dr3%KUxJ+OHt$NL%$?XXsayHW*Q5 zBf&jm$3`p(V1DMPlj4SlEx1J>5X@+$ltCL8Pu>-1w6|1;tW-9fuL?XOLDI)=Oxgn9 z3iun%ZToajvAVH;fjqSAhuvqvP2IeLTJF=~z^^+4P zks)WP+&?Lw$Il340y3x?DW4eG{d7Gn-!p?DKA>aj#kZzjI{@T>ary6!@g1~}L;{c; z(j8Va0e=4r-78m!3w8L6d*t%>S8CCP(78w_P;w1-qPWJ=FRm0m0u-IM=WtIC@u%4) z9i%qxICvjxT?L`>vP4l@X{l{qq!ok-3D&haQa$Q&qa}h?BQd#7Z;Mz>_fFg>4Fdb^ zrhVRQVI0=!PH`I!HU-%Sj@Wu2<^xt_S}I7y@^g62l9Bm$jWI{f);0Tgv+I&vs4O(H z&zNwatS@~q;L5O?w$%aLko#=Ue7bKa>Xp*Y@1*p7(Y{4dzT5M)m}+gTDY#az)^(a<+UQ5W`vCt4&I|5TL=6}u-xar2m**gtb{tB z6gH)YxA0%;8I+@@`P`$hb|XDGFZM}V_7W_7CLN?Mx73#a4>H{vKFfqqu>@D1gOQbg z=huwskx$WFVYIPz=Mn<^%+_x-0DJ#0o4RLy*<8a3KAzZaS@TB4;#K` z=kcUPX@vGpC{Mo~Xe}OTl}qXK8Jf+`38h#4HIeTx=ms*)$iYDRh@{vyu|g0ts)=GH&~lpiW?1Lj>TdyS z9iKCaOQuMpR9Wble7NBDw&WjSLt!QROcmd3#)rTQMBWS?zZfF7r~-dxE3L!SD5 z%?UH#35VKjTlsrQASy{4RB`0*=>NOmP?O@l3M-t8gnxRf=pk;xRzoL`rOnRDY}dj; zEASTx$UrLZw=a;UO82Gu!1fFpx5JzcAly$3r_pz1W))W8#ocWscfox|F?aqi`NSno zks{T~E8s}NIU$ClJ+a`Cc@Tj(|3_V3<{@)4?3{~M2}#FC? zI)xjE^`*X&-I+u2a?6ky1NDWWz4BZx4&O`dNLRY2w)G+|N1cc!fFl?z7|M@>`xD~L ziu71pIKROdcTEma7=>-n$_UpfqO3+7(L0ekqDl)>M)zTpX__iNAt7v47{c39J!gYL zD}j>Nn7X_~3LrRC`&lIQ*Iw2M{t=OSzBsj|WM^()p>@IP*qr(OKj2pf=lFK88fp}a zMUH)2gnK>=tC`ORU-H-OrYBaiu#Owq;HLp3&)$ByT5J`Ely8 zOTUSD8ueFA=yUE(!_)WH>;(8Mw|)Ix?VtA}tPBhg#i4LSIFy3VvRL)Trh;F6X?5%t zW)m&=A+@OyiZnmovI?=SR3h@gxoHaKWK>$Z4Wm$h*rO4*-lCs-Kx5E|9qD%zqGM~a zmQZujn_H=o{Z|!1deVykmc7GUzsQe?sG3@7&Hs%8W3-G4HD9{)$zGZEihoz}eh$-W z;CxYMd5Kuq;L_Nrq8h*cdc73qVS&yTb36*i3_T~OTihg|V5bfgiA8&Q0*J<~xPvYH zE9w{bB?uyZIP-2p&NJ$L3}ae|dnaewFKbjx)Z{guJD6}pa!>30`?>m!DLRZ&?;kCL zb0q7^>UzJsu#S!>TT0zom9wo;w7kGAT%7{Ph2Nz9W@lB0v@UkG$F7^$pj`XB6V8RVWCh$h)%0ON-L!Aogep-=)`Xv9~r zmh|R=hZ{>^0+G@jT-U}mZd9?!;$_VeP%l!1NvI*i~vE!o0cc6t)*wWvm z0CQE>ww|wyQHOA}dh8rcqDn!3w#htb8XyWLPAM|d~S&(wxvet0DtshzLG)cMLDWfQWkdF zf5fY(EaoRWM12iCSIAy;b*QTxN5Dsq3|l{e5vD0t&uWaXvi*l-jM!*RKl@Jip> z@@>VJK1X_uTQKTn3Ww_3g5fom6j{2qrLltwC8;+19rRXSsghE+tA?NPfLcjs(Fmbe z6VNP2vJLNMlNiah16c>(BBSlWRCol*2+IR~!z~K*UzA94E2i0cp>MS_!6_ROfmg_u~slPrw6O4Un(qKU5(f!TxY;i-BpT~pI99` zB1=a+1EtmEoT3OPLl5{FO1o$M?u2);%m30!nxMGdipI|yOS6cF6u3*HsozPsDagfW zQQxR0%#~P&x$CYpK`q_+kS*oz@Pe(W-4!p7YU%QN`O@Nyed*=?!80YA;-Rc*XM*04 zp(v%e0F8e@VDVbG?`9X&*Z4V?_uiyd?(yoE%~Jd%VD?9?{TuU_^#l`rX1_u^B577-dn$N}X)VB7OFvD$6V(@kzQokkaf=2+`UFyYXiU7x zyZF*Qf~~fo!mLpE)_Vm;_||tGFfQ5`lIE*Us%;&mwUiNVD>tBYcop3BfndyD0gUxo z=GcU?!is$aZ~{8-9-?nOy-K;Kb45Lik@7L!cMS7Ih=6@AGL4(d8FdO_u5{XEms931o8{guX#c{eU8| zG`suBHR-8?S>5Jvc&o7Ghe+2XeZg%4l;{o{ikp%C+&Wx{U zP|xQY=(k}@J$vfGhK?xX%li&fvha|~IEZ9{omZ9{}1N2#!$X)jw+ z?`sUK5i&r78xzYNcUmjvLJG_PTXGenouP1PRUHTke`UYLBltqczxMStzU5o%yfKk= zE`~Yzxj(hgiC)UlcEdu7V8p8aIhKV!kK3eY^^<+mt5$ptr+*+?8g5o2rEl&hoArBI zFrWF!25j$wD=B~nqvtGSwrci>LV%|9g5E0 zm*inTrVl&{bu&8{kSxHkhaA`|j4{v=?b9yw zvdYMz^s(iDwh0t4g#3NpQy4s%8ExOxWDm~9(uYb#$d`QbAr+|F!CKIK{{g#i&WAqk zt$_9cx|pF8u;=CAPOUX}-|^TO(AG;>%m2W!;*63+HeBz-#igJyI=e2^>VOKMQI%Po zXq^159-t4vb|!x**ir`uulsOF8*{pI)DwZ2E?JIdkq-|Sl#*+H6^-iH?c~tlluPBa67)qkzD4MHOEGO!k(v(Kf@kzCc_#`3;Z<|M&Uxf zrX|RpzfjwvM{)8S38)Ae7j;Xi|I&8*LAKJIeh>n^|EE=@mk^m?;-jGCplgJvbD(~a zo(x*^;dXka7kfSOWZ+4T5+2#{UmudEbwoQ|Sj42hYD*7)rE2tm6;=HwMO&w>`7Cq; zHzaW+byC^7EQLpSoj}SH1@@! zuHwqDf*+n;tmY*}se=YavF$x=^2G3gV(_S33muH-w!Jd!voG#VbH`?XSt*Q28K?bZ zxC6Lk^(S)QMFmEXG)HT?2dLLp99M+ZXX>(GT)lZkJ|i0q!W@-<<`ofu=@!L3|5R8% zg57*wC@e+eO-}=u0Dxeplg+nXx!hagd!1_5sbNZ{AsbW-49mW>lToSlS!d zxwNA7%49+PJs511SE_zCiy7p`M?{aq{vr2&&_K)s#vk~K-xjJ~ec$^?^@nQMN;()x z;2$M?^`FXv1}w3;S%A<(9=mN=4!4Ytr1LEYGuz;rHXGz9^I90*BdjsT#4U|?V$%@) zg}}5v;a;WoF^#eG;Au0X%l193l_6&#M0`yQo5P)%Rw~2l9_J2Z>}0m6P!S>UOhSxJ ztHpbi$!@_p4B@gN<*sP8@Eyu;mrT&^0Yc)UZogxci^7)zTV?VWN^$7i)9tl7<>|d$ znTaE5Eo{==0>sT4r8tI0Y@7n<=Tp3czgP9S_yiZS-=v=^ikUI)sO)bwxWQ>DOA)Lm zJq?v$Ft1bsekpp#(b+clR)O5bv)?^u-HgN)Q@+O^#6$!;HNy0Yq3RTGsJHN>LaWS4 z@?eD)OjHgsP~Z+At=p;GXB!!aU}#soi8J@Ls$;M86}Bsc6VS*QOTW$A!+A0ypp+Uc zOncuY4H3GybTxSMBJ&Jc#pzpacYH;-f0}G!XTyizg^ys$DKJ>9xr(WK4PM&UQfqL$ ztz`1fRLlXT!Fz0zc&@>F%$lae$q}ygVoQ;xYBC7&bJov~hWBid@4r3PC;>O&OqlA! zhbnF3!`^q$&0Tr8)LruQ;!h#qCC%&{qiwOB1;dShly{!j5c)LW@+M@8`Vo}#ciI&< z1fqU7$2BZ24vO9$ApC#KX%x9EN7JmhAGUeFqGs!QeKv2pRly&8PIn`l9ctaWF~t^^ z1?dhCc??dO9`vG*JvZ8G%HGY?$2>+F+{%#i{*u551f&-?xKhw24Sm-8Xt1)H=ciP-Xi zw!9W4eAREkkqyQwxX(sbJ66bs*VS3Z3(MGZJ~yeL{I)M+*G@&_%eCA$E1e(YS!HJq zEy9|{wngn+z0@5Hsy165%eq$zcW<<-zLxc81U(q3%`ssicb@hyLsq#brM5=@A!_$FKxv(_vMR`Zdyv<9#l*(LoXsjpS2gfJ>EjF$aFJ2SmCvz zT;lV~4@T1jTfe`Mp!>$VjI_gBlgOsXeIUwv{-0hkA7VIa^dA|g*OWbPgeZYsFL3`;Q)mN zDMc5z?LuElt@M51bwc5TP9432pexT-c{|Kbb_$L#wmcwM=BjeW1V4}kE?&0N!_VaFbv`>SpzJUF?bQ)(z$6=6 zv-rlI&YDwc4XYd9=IbBpd0$7=NJQ@Wkj2bu_(hvQ+1#!7xubM_lm>fo?WE9cf)<+t zwJNM5$xb=CCRC6cHcp))zt3t+&IK%%>=_F(>EScHD=q7_ut(x^ghEM5m1455d+mMR#Wj$+Bh`>1@VZ`rHdQX{s70>t`j>tI z*F2@47KEY%n~$plqD+rbm)v|6wtep3|DLuqN%vH!5qP&&q<{ky$W^<8BE|MF0bK&u#jf~#x3bepI5cm*d|?2n^{{JuT3df@KbkSAl5dE zd2p`mQ9mP8y16l{HyzOgc{>W=EnsGDZaZ@8{1)ey*K0gi=s7Y*qu1x&;&62%zJ*I3 zQ9-$Tm_DX|r<6Yc=Ea(8B=fv*^!+pbrv#B0%w)iR78?}xPQ}HF-ee8{3S){tb$pew z);n%4u=s>yy72>b4?{^xwrEJDPuPHHsKPxx13<4s>6{u$>*s2XsxvxPhUWc7Ejl5SN#MULOIen#e;^N1V~*rvu)TRCuD=9Sv6|?7L`3Ua;}V~NW2VlObma+ySI@fKY?fBL!lI-!m}2Iwg$Ka0tJ>hlCj-j$A*>#78ysr_nRi(-FSb52KxcI z5IT84#Y9cry#AfSH`~^qcz+*aTmmSM11ysV%u#eM%*IJF$(DwrDYw6(zN;5GEnJK+ zab=Xg7lZJM=Y#4L;|FV8r=`;hLC5<0`mmxjj?A1$%n46^z$U02dWLk?%yPoj&iw_nlak5IXU}xF91VO1k2f;TC&RrYMY1@&% zm6mi07#j4kE>;Lv14pe^nQD9~_gn_*|DWy(^b4Qrow@k(FrtI>k?gdZlVT=yF!G;4 zT&!0vM?;b8dxF4(+Cm5vv`4*%ziM-WhE=F0iR0NM6<`@OB=|e3Uord~T^~)1K}zna zhhlWfp*JXbeg>`uiXd_zfhhPFrFCaO|0(@pRD`(gx?!PFU9-XAeyEzajgJ5uZLCj^ zC1^C2tE4do_}Ki@4FjrSA;xP|Se^w!REf_KkH?{^qTrA$=xfAXmF`oF+wd-dHNOW0 z9>gEf*E-+uTL0O_F{h3;>eXS(t5{2o?|4y$cN5xo6IMH`#ONi^e3H)b^#!0zwC&Uk zSz{+ACh^SQW+u0K&a|H^NN`)BUGyeYQBYaRhL1wV)@=2l@T$#B)C)GK49U6OTbT}) zR9*JkE-j{v9v0lK{Ke~UzD%PQ*q^4{o8{?B;ygcH*T>XR>_C`%3SbBORjNTc*Tb$q z@ao{|#?RW%(2Of1RHI7?#f(Jb$EiFJT48U}fkE#~uKq5(^u2{UWj;R7oiJYrRuYz} zB5sG4f~t#E!fl9J+hhV2ylTdvy6M9Z4&O0~*!!wIz4WyIx1I$hz3hu{LSx;Ziquw( zy{=8G3QRpOdwslkMaq2?4^>u9D0`D0ZzwsG5CMWXtF`wbfO*=72BE81Dp&{sj885! zo-kW-l)*wG2Z2{}=)x)(h^4TNVE&{8Kml{bPcl+MN=Nj!fj>##f;A;qS0fOfj8daN zz3w8-3btYfLQIY(u&sWM4rXR_TOo`gjiA@0I=E>vUe=P6^E!Pn9{MMn0|xb*3qp=E zzs#=fG=7SDlQSCTo-MuXpR9AUr@F|_qGS8}EvW=?G?k;}_G%h2irU0zez5Hf_;xPfP zuNU9~O1YOs-7S%3Mh~jLg`68nbPkNzl#Kk4%)3GN!m5VL#5|D~pi*7fOSYlF4se+l zp=Cd-N`G&KVX<(HQWM&(Lqd z!qmwBeS4nhtGePq-)%@Ap+5rMl@ldR=M*|rAN8!pJJhzTcBwEQ<>U4V1%WCd29!j# zNQUDBf@qo}h4siXp}Hj)MdJUU#Kcmyq_y8b(WxFM*Lw}3R7br<)2r%(%`Z^@*%k5URO>4I-S zRdoUY{ywF1j!0W4m%u={B!?)Nrkgn3f1rri{)^;Srx~0t3lt(~Hb-5S+vzIOl1b5g z=4Es}&(b`Yn0QmhCS+3$14e>(BoQ`sV0=sMs{QmgS@M*{UDZ~z?{?G^$wmx>30Ck_ zAc0&d!wtMp)c>|1^bTc;9}s5$ZIqStTHt(+tb=)aUU=W~7VH)tn$OaB{tJcOEUOce z(oD^XW)<`}9pKLY)V4Vys_>Nu$d&ukRa#4VnvdZ6n|jnSzK~c#1_!bElqTE5WmN!r zX9dV6cS`QlbG+c$D*r-nP*wNaqRantG3a<$t4w)MOTqI~R-=<6`P;5!ROHnaG6$Fa zj;ZUC22NrAS@!Ghqu%I$D6q;lL{26#BNP8QB0=86BR*Edxc)W|7DouRg`$A>lgOaSy)8eu~?tgZ`EN4sjLL9tA&0e)Qv|A_?% z7@ePJ#ZkVMmQdF+F|XN+J-?$+e^QYci7p+9Q9%vl`dq~I-;z7m(+h+J=Dr@k5E$ajNgZ712auh%oB95IXsCCd>=~R><26Rl_1u%YB&z> z8kfev$)XLr5|WQ;TtYr&6W*a0^LeSD2|)M$9VE3nl}2eX@V$C0CT%IGZUElhey*M< zc$qF_f($i>Z2P))Q90faeD%&%AdYF2O*E2~P@e-B$+XsRe5jJ;U2zt%F-zU)&O)#% z-c@qXmt$Y?862CJA0|jupy1U~Lp3)GM1^Kj8VH*bNJe)Qxyk>ehs|Y=BMxJeSn51iR#>YeHZ;S*Mz7awbjQF*dJBdRl|SK<&CC9 zb-!oJm2*3OBse6*m^^dWv}#8on+!0CuEcjn_Y^c(?HneGVs65ioi~GNov5>~c4{Vy zKPf2saIL5|;HzxpV46ew=ZBxEfq?opgPb^Lr{4q#^wdn&wazk!8Wn+Dgz;a^v5Yo( z<)LWg8#vwYT_oo0r}7i8QA}@&GLar1L>_dDQ$!859m(lheVh3MzBha#rGWF-BBw*D zr`j)H><>i3av_J`Ed@PfD>fAnwt%dLn8Z|hqNvNVhS@k9Bi#y;^TOBI{O|?Q9Q5NN z{ij%l$x(vO(j*)5Np@%1Z{2q`7W{S$5F)rv01gGQ1XHwF;6|$DL64Yq5#{?b8hghu zLx(0$*kvd`Eo8Zpq5pDLI`vPry+v)DDQ<~Y)J%kbC#9~LqGwc_jJ}pJGAl=z+7swu_vOl#n5AB z+0SujQ5HL3uL^p5z3sKE)Q_w+e2}nyEjr6}vjx4|?``Jm%~pxLTKxWI@JRdwXdeWk zc$--<*(jLSw0~19U(3GA_jhB|KKo^^^^7!}d5aEY?VO^aIeL;_mrGGaet_MU3Nf!P zP4De2Wk-0h=40j~m+#6=1O99P^Z1z2B|U&G>Xat>T`%v2A)XpD5SlINePTahs2ZZr zC9_QZ-~wazem=Y|5F~tdxCpanSm&RP>g=}e(k7%S_wNVn=7)qT^c&b z=x_(sc6|g8;{6lST z?(#jNn%AU|dv$T2WJsgHtiNS*7Rq$CkGsymVHi?Zs%D>||3kt9&lqxT#++kvZN~KN z8O2jH#wuHD$`g-}huQYQMPJF4bj-6hVRP7-ip3sH5&!`E3T+DFo$L%y5<~qmw3)Cw zjdPg$Xn~ZTJbVjzQ)>tl7G<0(%Qz1m@>*Dk30x^O4@_7zxS4};w%5S@!!lJ6{dK1h zQ;R`yrMxv1SNK2#(*fmc#O}m7a(PrK8v(A{CmK^#YvyQp#o4K-II#ZNM*Tgfn{RrN zh`zjuBTIxUZ%fIX4?m)B)2Z<5x?X#bvM%V-0|O2Sv6by9-N;n;IyW2yVczN2T^71b zw^20)GMP?yS$;ds;?g!l+w2iS2l zh-sQ)?B5;L$FwEg)6!ODLvSF+LS6-1BF1NZ-)0)7gp};{T1NgM2q!hNc~@p=us4tw zD^0@wQJfnqcPn;n2kI!XEmvW5(uL(WZ|{8;@t@PEM)y#J8?632@LW1x&-}P z%H7K5_{KTVKbB%YOSx`F5+>}y8cPlFN~R6+ZDz@f;oaiFLQOMfqCQ(7aCbavkrI*5 zF|jLd0)zXDzYE#!ur77NHN+5>+^k1 z-^(qwTiyPY!$7=x&*C>+xl-m^nspzB8JuGI0xj!p7F19|pt|!-lUo?z8Q!2jOP!pk z{?bxj>ooycKRn&0p*7}P-xBM25%Wy2?rzNqY6BqEz7ZeE*k@b^$@wUWl||t5v^9~s zhe{*6fv}@HHs%BY#b4*I>pGn{5VR!XF;*izCw@g2K(oA&yJ^3Q! z3g);gh;aC0uPAR?2Q#`Of5G@y$o-nVTg8bKOC7YS*Uvi*Ugbyqm@=R?ex5%$!@wyD zJq&xl^1iTrKgUq3OEw7olU&aKvEujawSS3pSvdH#ZOw<~<*W~LUWF9ScLEB+QH%vY z6PWNS;aeq;y=t4ZcOfSj1Uw)5i?2Xasx&$tW+Zrkdr?kq>i z5vnvQjTz-lpy!XSW{xe=BtA08yPym_Lc?~|@6Th^c?nqtAs_pK4FP64+U6 zU=jTE8Vs+7X+;16x>ZgNZ!k7{#QO?e%UKTE?W6nWKnOfH1y<5VpGNOA8G!z=CHb*w zYeLeBz_#F8xLMKuxjG&EwAwz7f&Wc*+l2k8Yjd=y%zQEP^Ge|ZG2->+SU*TKDK_r@c>z&$MZ!v3`N@bzA}2I8buGlJFqmgzl)NkQzxB z$R4d!H7lujOpm;vysgC457WdWFX*Uczkt_@#D1T;4ZY`#ewPQ+&U)U+YX2UCZnXxc85yqqG!tvpKwWM{fzL6b;T9D` zb#}04UEHF)Ufc6eKuoOh8p|BY{!FMx(sQvvgzyml__o+Z;-9YCx`A&WuhFl` z>$DV|q_f0)`zIKCOv!OV@0JenEI~ZaEuEaOAI(cK`A!{FdhKJ;cxm8FIIZasXebjK z0+)g+(uoO+=lz{!^J5qtD{$^np5-VtMP>K2LMRJ!?MPm;H`R$xMqT*+?q8V-l7N%w&L(z@^uYeG8hZTK}O-;T<itArYy@FD)~4}*C=XbjCS;S%TS zy=*lSmd0pUg;8`k0`bsci_o|E98aFm#>|!(%+@nf6zPK?tU%TrW-e5TNWE(SA7%sU zrN7jbH6}=L#LFytRUNS17>)C|d;;t;#IP&|u}!9qaT$eH4TKmYJpmnp{%-tP`mg|VPlE~J4HzPtUH;Ppr z>ZR_(QLv7v0%H(j`OF^4GLWahl6FKSCljjKIZ;%L`reiN87ue1C?)yyL^7Ft69#nb{Bt#G*uC2p;n80mZo2xdGaMM=W`MI zy{dzytG}JEZ;q=JRY?O;52=gl_mm9)0(^wr^UZ2DQcztT!N2%e*gM_ z8Nffhp!$zumfVV3hwz46Ncj&0cBz;r+AdOSxo{V4k{Xg`e8t{48qZ~>$?)9N9~zNl z*k(9R>n-LsB1E6pzRs%N)JnchDEi>(Z%648_q3_&9cTLnn1(F+8v+i*bx9t@b(wab z=s&}=BPPO4Zf;B0{xQkpTTh7E5(e7272@7OSW)Xl5aw3TbheI)uz3S72_Db%d*TH=GSsHn&*v#8q>d>!#5bw2&`kL=|V*JDG5dQTXj}QLj$PA z{Hox#s&G6dT1po!Ay=(gEH3yMCn`(R`{-Hi=1Cegm}2iYnQv;89k*c0fjB-zHqcvAqpa6kF(DAUyt!*ePwf#IT-!)=n{Qb7k*d7W=-WCj*FS4tON z=k>dAV4KejrezclATXw|rhEL&7{)jxii;2Jk#gU>pWLJq?|A{9{fK_t3=BB#hN&d| zAj2k`T0cG!g&}qz-3(MeY~uQ_C>CMh`m1dS=GDWD?rAeE;d<}P)ZfzII!3cA)Ok>6eZuw#ojXUATt{nU-Bg zgc{rj(ODLZysba7ysbM{5zUfL1R?Yx2bbE|5X)Hc4ayr2a8H53yE|(kR}F*pRgap5 zsS;20p5ClXAtdlUz{*0d#4uUIa&l}ViVyi6K{vUHQW#;0eEiADB}uH$E1AHyCVUf- z@R^+k>|af3U`0&X2)cx6?Vp^RLzzHUY!9*78nVp)J309XIC$Fx3Mt|~jaZk=V*8@e zLyo@{m(1NXhjQYbjlTL`-cy<`A!bjrjR%*i8hm;9>dC0#S;xRX>7VDC$7qc>K^&F> z`^0@EF|xbCI!e8Hs+Ar|Cu0-N*-efT3{chgCeQef6QQ+5U(&liF(uU-3zM2%ilGjq zU1NGS>OW9-S@Z%W7rpNN)YFsSEGB$@aNDbOS#i`A(wA^(-++PsPkVB?+7H!lH@+mC zT)kxTUCDAIR!@U8?dbS@bqq}^HH>T5qy#G&aS#oc(YHvhhm1O*_ZR*{yE?%(WuPsj zXTS=gZcI-EIp!_uT<=E2JI0-@@eU(ebqAvm*bfrG_UvZ zNqVN$#|$v{JnBu49of0ssY##w&@$mA(p5T9vk4Qap<Q0A9dN+!}vLQ@^RE;EB# zyf5a`XHuone*f3exrZ~||9^Zlwy`lovpJ5*p_ay+kHeZns*y@k_l6wGt&t=Zn+?OT z(Lqt>Q0dOycdEOjy4xIAQIxwVx}9?4mYY(kWWRU6uB$)0F4^Jpe!pJN=i{+T3ilzM z|6g-&4DlNDrskz&@`vfq!~{Crq!33I#p6iWjENhl&hskz(;#A!8p7D+XK19b2kq9& zn+bG9)+p2`nT~39I)YY>O{muF#xeROP4TgyZw}S|b#L&g0%wa@%5c#pSlHIDWgd&X z`HQNS@p4`!$ZKC3R``@vHV`L z-Z0#;0{_l-w$EvF-S^Sd@8*Ytb(P4acit?AhLw^rmqwsxY#CfG$38B{!O8VkfNLkU zMPs*bq@7r9oxktw=m+yw0_S+`R9lhl(n5#fzUg>kfosu2Jocs;kg07v2}8hwOryMF zP_c10CGQm30xgPVFl+Pi<#?ab+2#ffcqKySCeloD$1#n(u0z4n>091!l?3H}^&|ee zI^R|Q+QfiJR}%R9$X8yfL6TL#X1?&wKc#KUF39kwG1>sz`Ouz%&XF7|>L_TP{0eD_ zx+VA5*$yX9jT-Q~NViMf8GVX5`VHef|G>y4a{xA_zYvthIU4&B(gq#XF-_f0L~dCw z=?KN!&T1xhoxV+wL8UQEY57n??(5B`zD5oQ{Z`}mlo*K7IaF@6sj-XbE+D&(^{bz1 zd#Gvpa*;)QoF_^w#N#3(Cv<*0BLU96|KS(T!2nq6Z4TK(1+;R5!@}#B0(T<&M0P;w zu?C_5hr#vQ|*zbIbHtrUUt;ACdJmB0SebY7fqe-gL|btGC^pFp1DZ#JtL z0tnxgBD3czO)a7wH_!ddEsfi_-V_G8Uu&n@U(bYR%Gi&ack{6ZG-%{qQIi?JWZqAz zHu-epV{e>tK8Ci(U(nMn$;L|tq{p=bbRPX6{=qut7ZXij|RCf3eMf~ht|0r z-=5LJNoNN+9Yz$A*Q%KHz;`;%-okF`HxLAK*ra$hm zz6Xf}oBd6T)x-6)bc+#UhtjQ_Md)RHFH#hQ-MCtIvc%i(J15Q>fVb2&`Mn)ezLw!@ zTb%gkwlrnMgn!Rj87p9~EnSF_%I(vCSEY6k?9QF#@!WF8w8PU;5`B$wliN-~r}h*g zkJtSWZLxUUqo7c@*e@4x(Tb8V8GCVkWUq?%QQ!8*1ex0$EbZ1l|0eZ(UT^W_vT=b% zhc!X8_U80_ZmVXzSx+J5pkxBolOIC#L|r#j(vqZ9>1~s+C7QbmM7jSCCr|DV1%$7p zHF4G<|3Gca{*)NUZz%Guh1Zq|>KfUCZ!^B}fsh8%_%Au>$E1I%REz0mcxef){!7!#jtCez;4*e?5H(in8X8gK=0qml)&p{eHV1b`QsAQukJY8 zdEQME-Ch18y!Ywk(ee$#!`uU_Bw!DtXX+;UwCJmC-6%z2dGAdZLY2FEKnUyy3Ecu2 ze6QqvGq-zBDkOc!KJCI0m=1M+0lqhd)epn`twOgba(Z<1c6KiJaFe+9#{eb!A{7yPvAcl>iG^ih3TRvXHs zG9GLnlC<`~40qdAmz=R{{#HSE=1>>n5?hQa1NyYItH*xSC@YuC@V)YDut#3H71*j6 zwLEw}&gm27u&TniGziBnp=D6Z`D1>C;E4eq@|goF8yAsKreC8s=~VP;v(;<9kEh0~ z(i9z1mYmAepy)Z~an&+C{I?_0e1ecya-^SEZC)AL=5IGM$Q`*yH(~Ovy<{acDm2%1Yu|s3RC%@<;*dQD-K}f@zM7-Id51~@orlS zNOK`0gny6(lSi9U@@HWLe(#~6F49I@u8e0yiLdVK6c1}SqHsPem-A7RvVhy5KH+_r zV%h0>!^Cdf_CRWfHpz+8_peRnupa?9)1#GFV*P%$lB8yAHJ4v1@nI)Enh$6~t1ynJ z0XO*bpDl;wIbB#wy&UPJ@*QwzUHy-Cm!qEr{OPe<>MMhiL{-< z%bM0@1`)dYtMyw{#6^bzN9m^FoWyRm5uPfQPJv}}tDmO+CUDaNlEagh;6Bvu>*lSb zJ-5z)M|P~*WXI=A?i1VI+nZ!7)``0RX8up?)Gk;*tQpx(TPMvuMz+mp`{-!v?%*F7 zB~4xmufP@@l=R9k!9hFq18|GG*gG>*4UX0m3dZ5L)Szd-BfDI#qqq8_+u9*Ds^&*n zX4$Kx#PY}=Wj~P(052>Y8&SonLvR~Fkq#zwC|LB>n89pWe_K|N=dkaft)^>35Pjcj z?yFs*ao8-6sv(?81tR-y;YRpsJ??`tWb7Mf5SC%);!cjSP2oP8 zcq~+Pk(}6~}Q`?fI=SrlE!1eIvQaxT{l+mL;WOOr!C`dazq2bh`=8-3sKF z`ZBU(ogtS_w1`hw=_cHg# z+K+9(hm_X?8LRQNXcdo?0hamR#AWlW9{UytOtm%?jGR1NI45bxMmGsmiYMQtFZpxo zNB!}{;~~;4!k~ETOaCv%up4hAFWh0Usbs_3#BM#TXd3Xcf3NOda|~?-iRqE!c4aov zw2UXMMB5RJsCHy@#P8}24s#Yeyp@o&C01l8G1*~`*cQ%coLJqC{}QoiUS42F!8pRy zKI40~EA4=zC6I@8zg4J>jv0{D!aLt5UWFu^41_YsyCJ*iZGZWHYP*oveNz3fvCad| zNmaA^MP4SisfOkjBhc{qL~&=BY^|lN7WO|~uHiY-YK@!M9!*y8E~e1FaiU7>6+r?+ z$&#+A6}@tE+CsRVIr9s*Q8f*v^X{@f%4Q1osB8ZsHfo?isRYTSG_*;kW#R+eV)rS8q5`PGkttl-4sj!ghhCOML$)m5u#w))7yO2h3x2!}%$=kl9UXKrU7U z8QwtLzPkNcbhv4!FvZvXPfJTOEkyVS%G-@OUfss&K7YK~YMuW}X(HRoB+<&V%4jvN zle6TkAf*r{1-e9}AS=ek`yj#?_0!L+6 z>WST2)8ZW5jFnvH@X?q=qyA5dTo~zb?F{U#d+vGd^?Kq5LIr&0N7-vOZBf5`_rf%H z*00p8Zl$F28S4wpx&Aw9k6Ycc=A-w@aknowMw!pJJCo}Ru!E*dzokou(TR{fZroGS zV$vH0w*eC1TY9Tt{W1P0m}Uyu_yz9*3LfwDrLI>1^gR~nziV$2`e+(QtSbAQ(l|79 zOk5my_an&O(oV?vV8ApSoy9LaOOZMavM&aFup&qIC2{)vCf4kIN$Ey@edYA}bd1DtPtY%_?=)ViXfzk4!cx(6HcIzBAyiw(s+;W`2gudL3 zhxaHn7)}mxs2I_U=Ub z1fwQh6037P4EsLoWCGf>32M@!FC&97m_{B9MUs3TI!nv=LXbZf-bZXY-`mcV^PC`e3e-`- z8OB#vyJMk#_mcj>;FQ`vA~6IW#*yKXF?mF9Yzm-v; z6sEfP!KUlx_($o`EU%c#0Fgpj+BF4b^z?93$dQ`oJ?d7}BLvs<+k|T+4&~xQ;=qXh zgDH{6imH>v7cjA;f{i{dMeZtxC%^&|pxqts>f%bgXWFh!gAUGl4tph5{*2+kX)zqn zzBaRKDrpfri?e|cOWSPe_Ll`H2qUU$6yJDSeG{5Z1=!bvd6##zypbls{!U`G6hRp9 z;Z#r}IILu=Q;V@ijmphf#gql1Xp1>ak0uHHVEADsQl<#w*i8DC^Lfcjgwludcagq| zd*0Gn?g4Nt!VQ$FCAPZ{eC52#Mr5R|<^+0QHUxRuxQ^;1{_KA42oDx=P023AURO1f z^S*-^oqQ>_*gSnp=KH*LjCr{uQ;p+>ET)k6=q-^Po?75_616zLb;s(G1}s#;Nb{I+ z1nE!i`Bjz820dk}F{}it$rjy0s88BPG7{(qn6aDfgEZ6_L`PQH21a_GGG13Jo&155 zfbA=OvJW7PuNFxvk(TYfXe(=b^e5HTnlwP`G;EmUCfPa6OV1dQ`#XYu=g=GxH4@vj zM6IzIF;Cf#kH-0R27fS0^Y6Tj-75-Gg|9#TBZ2716tD2gzpPTl)%#f~30AN4mF)bD zj#3S;`*URxaEbX*(SN?m%xv%C&7BtP~g3^@pilA-G;K`N*njZhVX#w8lqqsNsuKojl z;l+J@{{d*q_fju?wY~c^Kuo4e^<`tbs>WBBv>@exfnX#P1KZENkktwCHbuGUe=r4C z9ESFXi@KAs+%UL~w6yE5Mfz5d<7)Y6ItZu^oVEI*rGm6+XxjalVkfv4_A%FZl&Vz) ze!B%_g*xfRzPdT$Ulwl9*TPC5x0gbd>ynQN@c)Dv?Nk3$L3pV?Tzcvw%U$jA zgl|+buSFJ`*BQaEI+mWq@FF1hpkOt-(bJ%wti}T8`IM8>etx10s@l~A`p><~Bgm^c zovla5Ztx6z_0ntztMb{ms>YHKuoYVbc)4iMNNMD`1?mMYA&SyWvI<@F_F9BPBqsMG z)pU~KW-I zQ;x{J8_2e{k^NosB^b|abr5d{Tux9VJ;3?IgO~GtId8j9}aYh5Qu>svT zB?s~)hr&PaufiVKy&I5SGbolxh;yn5W7r8WhB%pf=2u18mrZH5X+~BCE(>lfxz6a! z2T*@As5&5vYJ}sB|5V4WTQx6cEK%Z#kj4hHAv@Vzmz-8N*3hcq?!%fXl-i4e=mX55ALs$d;BdiYg2<`x*;aBgOwCk&AX1vxG7BK_OT1P4G*sU-8`rH)93lU0JJ?FG3h<_V4@%I+6bzE z@24Rz{>fPmRk}{6krTQ6GqJqSAmfer@O*vxL+QpRF+|-vV4v0#>Q+K}P+wKzgXbnY zkzik`Ptmd<05{j9ZJo0E6tK{Gf!jU$yg?nJd7$|nHb$+`cD>jDd+8!=g{fB>NB5fA zB#_B-d*8F))gQ~nj>I?Qn*W1FgksJ>{0!BqwuQEJv2kQ3p|ICPFu<;`ah<6Bw>?4U zCD%{uMB_@1i3TpRvWF8V$9B8vo1XV>Y0@aHx5;97CGmN9_vBK`?!QHq?9Iv60e5t! zw#9tX|MgRjlWUvPY69L1@_=qCG2khFO+I~^>m(k=#UooGD-SQ(RF3@5vA>Dhhr=XDi009d3rI^+z zxYc)u;V$8{Y&6YvBBuv?m~n!wcItas6Bl~e3TI!k$<*yBN1z{CiJi3({jgx(!>`8b zR4bzD7C3=Lhcm&m(&-P_8tAtF#n2Evjc3<5$~`TJzTnzEH6_wLY`g-|5mY~XOiR0GPLi4f0)rrhd_1ZKj*DwUO?CFx|wuX1sUcwL7x?;Ywg zGJa-Uve~rKQPrS!$8Q(8BkG@scEs)zY4=OdEET%sTCWD)ykNxUz7VMz3UK+m8`W$L zvHqfhONo_xDVqtBbpcjKN2B-U*g^Q@QtDXAc_-yMqjLxy@*9(;e>+dU~8fSj741qID z*aG{~SG%2xNCsEJ>Q9kK4;;$N-}DU&|MCcLFN(;a`WvB_A8X2LSYT;wX64DIYE3`J zH{V6=2JFil#=mgdL>=)?H%yvRVUDQS8GX{3u#=6Klv8y#{X)ba*H&srsITv4r%_U6 z-&B3Fc%s)e9yS|Y`NY5f{c>4~&p@_AJdLBF%AWr>XqG_=f`f)AzWwIdI;eccvq#~n)< zzvUoruVxO`JtB3l`7_bKYN%9UyvlSYoy7 z{ejlgyo->t?WtCYj5M{%VYxyt3I93ZFK!d}(b(R)POj2KbSj@}ku$bAy=C&}+(Kg9 z%<%=JGUqzX2m2d~r)2DSUP3fp_kvzzW!o@(qdvGqJnB^*ka*%aU+VHS-%9x*xpoYC zz0u_Y>;c;@v{q;}^dfHD?;dRo-?QAW;6B(~9;c!5;TOz4nQR3sYQdPuwNCp66GPd%R^J&TsXkzij%4 z>SupC4&HM8D`t_p9JH@N2LIIsaVQjVp;Y6OR%WaR$fqpfNuj>LTJ)cMrTaN^f1EtZ zuC{}49x?W029I6jmAlr?Dhxs!wSYGj14v2V=kR z6$lk~mtXK|om2bb>%6sm$vc-uK1D=QP}1f)*XTs{_>-dq-A-9a8jjk*Hl)^>Ac6ro zkFWQfeD!3&~uATcwRifKSx zhVQp~lG70i$y%V5@DW;C=3Dg66)WQ7+hC9jpgOH`$vkBF+Mlh+J0NN!qGOd zKpnV)^B;|O0C%0%qyodc!ExN?uUs>O!SM>{w#t|e zhf=3JGq*tx2Xse=>6v9ZK%YQqr=qkxEv9GkruU)k%-B*zC{pLU!%92NgxK$i&kz62 ze28{S$C!gblKhR{G$DeFte*S%$dhJoz0(_a zM5&z!IxSG_XFc$lii5O#iX6|C3^&qUP=(sDdfw$+lZ}a`igzh+EofFc*f@YqA1`+c ze$PORe*2oIDgxY)d-tPnZFmvAfC_2}-pmCCpjg}S&8*&qHNK&}jQtC7y5LEet_z1M z&0cG>s@V$bJ*mvj4Q4M_0Koc5K)*iNHqh3Fn2L|PiTny10;ONM`)2K;GzPuh=$OFp z;z2PwzuUb07x53S$wSUV%6MW+y*vI=yZYuL@UZ%#4hs7_PEq`C7Gq~Mo5qUbTwIP! z$DsRUYW z!8Q0o^5of`j5HVT`azB7-mmE7~kQ+w|D~S_- zQ_&l&Lk3tcia}$#MleTe3uUAq`f-KwN?cbHdT;bKXBuIco_U`K_+8)~b@pyYtKn|2 z07CvQS8cx<;qQv@w(Or|pLV=niiGhN+D@Os6A~d5OW_GmeBmdA+jJWd}>q)x+n$Z7r&EJ`NkILG+-S+wum^!4nrlt{h0;&v8}9p2jS@+xRiG9(I=dQx zrng#{J`aOLJuXZ))a>!?+cTt66})ZLxv0r5i27#kN4m1hz||q<=?%q1In-{8<+XsA z`UY)=m#JwZOukss0~j@SwEhX+PxBQok=A!YkgH<;M-|Unrw#DEXt}Oy6hTu9y(pM*>y`q{!dmk*n$H-n%Y81)4Rjx{j)@TrM)H3PxTDs&EwU$u{>91X zd*3@`OG(piP)iR^GY2WGI0VTdi?ghMD3U6EB`nyTH*Oz2wgQ$P+)cib!tle3Ek%hR z3BezY(tKbE-j*x0QdffHru_|LiFSZ1zUCJ(y5pT$r5+>lyuTx+zM@MBIn>zZnWpqB zoP#>PSVJluM-5fA-|D(zmi|Yx9V9 zK9Z_2&L-}^N~?G=Hn0N)x&iaP@vj4ZDb;umiz@wOxB)|{HSI@fUH`>b!PC4L@D+2! z$=EY~lsCr`fU08}aq_zOKh_n7_J_(B zs~rNj{D6YpN+UV+v9|IKw(HVv7#9ys`wKo0NDOOk<+cM}Qc9^&JkTIfEW@r?}`GvS6&MotIxWjLqOfH``44Mom;; zpXW{L9azrNV9JUw+0I`ZtDRLfsO3$7wS@60lt z|9E$w-L7fBZ~q!vomZrM#RMOgZbfE&gPJ}>eKaCD1#zD=)5zc|nCy;AJnR9p&jBZ3 zauR*p6MR;!4s^S6H4uerN<|oSv=k%k)KWXwHow5QeWEfk;sXfx6w6&05Bs0Pe>3$+YcRzvzt*FgsQBlKpS#+lcxi#CNEGm2LYZGz`L~|^Z8j0?THzYMn zAa_JT+QcWaHM9nG6Ul+aGl%1>9Glo61q&j+W)UEz|CKALOhY{~i=<$q)jEZ*{p2qb z{YqWJ2RW`3X+{KIdLo%L8yQ#%jONSYACJLc&WogTltj9iZyDk1qLO!T?wY@1q>CRi z&-_LFs{ba@>BA~M{V2EBzxXivsDqx_ejB%g3S>!@3i(8~_TbWFK@)sPr}Ca=r?<-y zqJz%#URub3cRapBba|aJUbkX+O`I11QjP#Axoy zka{o}1Gm1L2hwQhT)G=Mc_GjHI;U;T^_CvQF@tJ*yoFAH8M2+T9&}}rEnqs_%lY^X z%nES@bOxla`)8vqhXZrpFhfZ@%=5D>wTW*|cI0coPD3rRelE3C%X657dO9^~Aa*-| z4=;C~4rvxr^*$I!VDde@+<8WuuZ})#OTgIQQQ26uAsaO|>uYUG)LMDQy$8#EWuo?U zt@T|0d+Q@R-}y?|Uu$^zkOOXh;Q7aaVRbr@q)A#sZeXE{T5*mzZ{a-ue@lGAhnlt( zZlG`S8C-y7>pjlhcfQANhKdZV0&=rzf0}Ko_sP!lzyxkN07Y!^(oE%>2sqcoOQhXp zuPO36__fz6w|FN!R@=Mjo4D;?AeDT{&}7)C7gW*uwWzS$OU2L!(cpA`7izwH!=Q;5 zV3bxsYoZzf0sgMW(Enr{Fh7Hk5uZq{X3?Af9L)|)Lxf~fh|bjn>;-gOj{Jtku&Htk z99a#Hpa+d7r>E)!hNt-Vll5ciHBhMZrh)jjZ^XuFVkUEMXJkPRg=ybLI^A zeH#h;pm;)r0A=<4`&$^V9hOL%6#o~XZhA|tcr!dv&F?cT)Nbc@X?(qgABz|MrZ|3FJ{BOiV5aOE%63OG zApiJT!-3>59hexJDMRDCESfIdcbvldUxR{24@HiJwF^zGIvN9Zeyr(PXpo~B(L4Qn z)wf56{L+#Ww1K+Py?uOC z+?LdxGqCcZhP?nyo~blBI{p~rmarD7p#cq)zb9EXJB&9MOJ0Zh`;R*4RfP#1^n1zv zt7=c$4HP|ru6(S6hZf43L4M~HYV4@J{-8Vti0eep!8&kcvw{{>ErI@HbWFT2Sy4pJ z?|q$aM8DWSZ|I&yXY6(W6wa`jhH1iJGqnlGBfMGLZ_UfFPO#oI!N_PN}A z{VW@GD{YTD!&qx|r+W$|S1nt`rjf72a2o=_AnjJ6>Ciq-w|bra+*Zum+TlL)6{XHo{de`4j&WvOxBg9a ztzPybQmyXe$ zs6tJ6b$0hZ4wcrE=^txCOm{Sz%krF^wan91md7>TGIOJV(DBGEb1Tu*ep$#Q3vpAj zvS8lpOQC`@Bki+stLgMq`!H`9+je)?Pi!GJ1MZ~y(OrkDs$tLjUGc-!K7-5KVEZq_ zivJF4I}h%ZubMP(KQ-?$XWj8986^LgL%!*Z9&CrXi^itF*mxg-JuCo^d}#~A}#y=_zs%Vk%;miZc`zAnCw*~KUzlv$UV*-QOl?MQOGp1uxieX||i1yk57Y-dIt zP3ORWnio>fi}otJ|V@g_5V@CH;d24Bh^= zXF+EuNVbJtY)Oj_j`>_tx}GZ&;n zKX+U0U*IbjHi-AK4vX6W_U<#>2TY$~K40X$xml1ZcO`Wl{9<7L#2F}pxYP7TM%t3py@H2>T)DU4RvG$` z=5b6F{6!=9bC^Y1kfYj|Id1u?Zl4!&BXUdw=A=Qb%Y#;`%r`4?H#CXW#MoVtpqw5j zkF6}cUdD9?8`2{p*?QqQLmDMk$u~EkBZmdp+eBvy6qsXPMasz zF2n4YwL2-&2K`2_v_Y!0)A((^y3qmVI2bP>mCsByz^8#ffom6y5HRZHl=iLeZwXgj+}tQ z;nh^Ua+Q%lsw$gLh|6J7&4+yP8I{ulsDA*vj4>iSzJ|KP<*2aZ?ff%)#otS}SrL8HV(k_T| zRCp%j_EzODESpIijGSzETk2VXo2(i%)#zbn~@FB+&j|vKB<{U<$$`nvXX8n7A{CQElPufcsacHl) zNwLKi`bbDAm~{r3kQrqG@K#9lp9vi@Q!J8_~c(^P2uG2fi{^_$n%9SU}QI{K@Ntog2%mz9rN# zO~1Dq@FrWCl#BmD8GqKnBK7sMxEB4;{sS?xRs2kI!w;T2wtXY`FVdar zIA007O}g^GqEZk8CNeKloyAi*HzDFI*v0~~+~j?JvU$EyoF_Q7GCy|{?-he(LiOqxTe0$ zR z4)C38wT%L+imL8=2gG~(e-VvtnL*2k#44G$g{%|}Hoja9c}e@2FNjwMoosGs>)SvJst68X?ksIw=w+VJJxt* z4nGk-yQu2$R6C@uPeFLQ2^~;X_r_kXbT=6gghHxgZuRua=NjP>|4dx7X94z)_I&52 z)roEG=m9w0z53QI0ku_j3ORj%P?(A#LBEV{5zwdiOD}vC(cx>0JQxjG8h}^S(HGfFN5Ylo2Sd6reAsIm8@FY#(_l zycAn-8F7ZioQvsrYeC0s2Nsvn&3uB`NY3oET`Y{#Bn`x5Mp|6$=I9Uf0@j5`jFQct zbB#_Wvo#$J-)j0rD!3pmel-?;u{1AyA5b5Ztf;=Cl6sMS+ehC;k{f5Rw*u8c1#8lY z(daF|6ln{B=IfLy<`3DZzm^L;j=RNVPq*5BIBWx&%?9BZ*5TUxSXr$O?Y8kT7k&3q zJRDC1v#&u`$3Ns~cFAVU)mehL{Xbt#sKCVBDu4Z^l#Ud`j(3wrB4upQ6_$eBwgX0V zSHCM)aLk>U&0UVA5Ss%-Yg+e3OA5A_^g6KY9VFW}IJ@bg87mSoQF1yau|4K#PJ;jw zqnA8dAy!JRNc&L<${zkKAaHN2Me5LV@&U?GH9{pcrObNEEE%ve>r*ohs2iTr3(lquIb;!9d?PB7%8zs5t3Wm7>Upzl3eaP3t+DatQ|9o&w-xim# zDZZowHtLfvRp?4nuS%8p9XmPhVf{4_ZhI^j9!`X`w-I)GKV5>JroH5!E?L_{n;pHN z3&b+gOQYbcHX<+vkb%BGSuh_w$2D(8rDMsXA63*o6V~E*PETLJ8aBNo zB{xOz#;-1CLEZQAg`jx-OPDR#;Eb^#M1wj?!dB+ahH*jyBl!p8PGNVQBCdgS=p3oI z^$#$T3fj0h_vxpktIZ`(bkG3=TQ$zXMu}!HWy05aE>=z+W%2y3er;LOf0xWy2~%_7 zhpgJH{UEuJ7vsHVIJbI#*sx#&8GPB{A02UX_@*SkV-5@2(G17K#v~utF%jPC3F`ph zds+zWX(5@Tn^BXOD^zIpijJK0|AmXK*>5*p_Uo!gGbuDrHNcacAAwvAJk&#>eUb(m zULWn`bms^!P<|+n!MpO088@B9_6KwSRm2cly;*mt(u{QRh|V2U?|;gl1g>Ae+L&ouR|0{NBF7fpS+rO@<(Zr>&27tLUKcs5x9n$2=Z;x;4-7IGK~!i&+Rok$Q+ zoC}n`lMd-J2UZI%$TZS78he6c6edBOt5W6?F(4o$k_@TWfKd~lvgL5` zM|YS7?G%q0(B~CQTc4K79a%#k)ZXbYGA6)v#54C*r!tGV`*5e=fGQTQ?r5oD5H4_x zLHWjgRng%TpCg%*lsTJ87r3R--T@BPZf4f^U7_v|y8eCZ@n!&anO}WIK|)E{u0PCa zfw8>|s}{t_!Hxr)o-K>`?yqI;xY@9fVeMDNDL%Jl#4zoipM~&n<%&&zFNNASaVpYx zdw?nF+&c=4mj&5vw5QMVyQy{A7IeJMbt|=3{3#3|X75PG=>(VwOhs@$0*k%jX<#zi<;bU&PEEtv+iL@Bef9 z2~i7cA~&)%il~#{gIN3rfUu`hjj~vR(>BRM&rP~XmDndl;>G2TGi7oG&%k;*737=E zNb#+C2NGyo3pn@ish9{yF;x8Ca)&4MfpU{Ni4pn3=BZ2hNVo98u;PI6`LR<14eXza z1&1B)11fdAHBgh5i3VJ<|HZE?x?W(a+nFGXPwML#aWGA~BPpv9e6z1DxN+5NPov7l zW*uIM0G9-{!8XH$Hff)Ia_W{XB!aR815Yd{m(ZBchnwBz!2Ry@2=_^g3Rs(x8;$Y7 z!M{!yB^hMnQ`#ilRC{c|voCH`$oYQ?nFZL=!8S~lyKJFrZNcwNf^$F2yCIrBUt(VH zH+hq;UeYdgOQ5H%w;-{fay=0Ttb5Rr`1U6Moft>s-DV@3Ie&;iBI%;opdN7Y9A-=A z;nANJfBE&&GY~E=E)OlP#jUV%1jo`7#!1vAmgy}WC& z^j~R~Z+IK8Gux0xZjlKJS>DD7w7X7N8y~y*A8mJZUvC`!{h*?ElMA9ZRGhj1bRG$i z-!xCqH-ZZv)8sxVuQ~4m&b;j0+vfwu$9)H2y&^BQG@36vVdxm=I2Rj&Jp`(jN4sFld%3ODOnf!s4l0JY)qMN-R(OL`p@Ahm**}5y8?o74+m0<& zcXs(hq`Ssedq&lQiMJA(H4uA6PtTEM1#7H z1{lM#F7ILrryO@70^6aNlhh3=a~y#da)h?3zRVNlEB<9_;U0 zA-S~`NlpcWOjVenkph$|m}R`gSVlC>_{CW8zrI%YclYmnz!yP}_)DjF72ZN^ zu&<@{03)qAh$t0`K&YvFk4m2fW_29EbMl52&Y-KE@S5rVz+@=2+b8?V=h5Xs)q7=D zJ=_65|7WiUe`R1c!q=&hOB1Xd=H!dKs|C}eHr+lVT^e2ua;FlNocjA676kkW-n5l| I-Jg~Ff9qpl!2kdN From de748dfffefeba1ba9bcf0c90c538d32c9cb2020 Mon Sep 17 00:00:00 2001 From: Gamer-Kold <84700933+Gamer-Kold@users.noreply.github.com> Date: Sat, 6 May 2023 15:04:40 +0500 Subject: [PATCH 0062/1350] Fixed broken build.zig files. Now works with latest stable compiler (as of commit, latest is 0.10.1) (#3045) Co-authored-by: Talha Qamar --- build.zig | 2 +- src/build.zig | 16 ++++++---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/build.zig b/build.zig index 2bca6a1d4..042338a6c 100644 --- a/build.zig +++ b/build.zig @@ -1,6 +1,6 @@ const std = @import("std"); const raylib = @import("src/build.zig"); -pub fn build(b: *std.Build) void { +pub fn build(b: *std.build.Builder) void { raylib.build(b); } diff --git a/src/build.zig b/src/build.zig index b39f23ed8..e4c1e0c44 100644 --- a/src/build.zig +++ b/src/build.zig @@ -1,6 +1,6 @@ const std = @import("std"); -pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep { +pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build.LibExeObjStep { const raylib_flags = &[_][]const u8{ "-std=gnu99", "-D_GNU_SOURCE", @@ -8,11 +8,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 }; - const raylib = b.addStaticLibrary(.{ - .name = "raylib", - .target = target, - .optimize = optimize, - }); + const raylib = b.addStaticLibrary("raylib", null); raylib.linkLibC(); raylib.addIncludePath(srcdir ++ "/external/glfw/include"); @@ -103,7 +99,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built return raylib; } -pub fn build(b: *std.Build) void { +pub fn build(b: *std.build.Builder) void { // Standard target options allows the person running `zig build` to choose // what target to build for. Here we do not override the defaults, which // means any target is allowed, and the default is native. Other options @@ -112,10 +108,10 @@ pub fn build(b: *std.Build) void { // Standard optimization options allow the person running `zig build` to select // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not // set a preferred release mode, allowing the user to decide how to optimize. - const optimize = b.standardOptimizeOption(.{}); + // const optimize = b.standardReleaseOptions(); - const lib = addRaylib(b, target, optimize); - lib.installHeader("src/raylib.h", "raylib.h"); + const lib = addRaylib(b, target); + b.installFile("src/raylib.h", "raylib.h"); b.installArtifact(lib); } From 53b7b26c45867e8b373ae1f9ab302117d3ba047d Mon Sep 17 00:00:00 2001 From: Alfred Reinold Baudisch Date: Sun, 7 May 2023 10:33:14 +0200 Subject: [PATCH 0063/1350] Added ModelAnimation.name, initially with GLTF animation names loaded (#3044) --- examples/models/models_loading_gltf.c | 1 + src/raylib.h | 1 + src/rmodels.c | 3 +++ 3 files changed, 5 insertions(+) diff --git a/examples/models/models_loading_gltf.c b/examples/models/models_loading_gltf.c index d8b34efed..d5ebff237 100644 --- a/examples/models/models_loading_gltf.c +++ b/examples/models/models_loading_gltf.c @@ -86,6 +86,7 @@ int main(void) EndMode3D(); DrawText("Use the UP/DOWN arrow keys to switch animation", 10, 10, 20, GRAY); + DrawText(TextFormat("Animation: %s", anim.name), 10, GetScreenHeight() - 20, 10, DARKGRAY); EndDrawing(); //---------------------------------------------------------------------------------- diff --git a/src/raylib.h b/src/raylib.h index 6e5d023bf..2a5a2f63f 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -402,6 +402,7 @@ typedef struct ModelAnimation { int frameCount; // Number of animation frames BoneInfo *bones; // Bones information (skeleton) Transform **framePoses; // Poses array by frame + char name[32]; // Animation name } ModelAnimation; // Ray, ray for raycasting diff --git a/src/rmodels.c b/src/rmodels.c index 34352ec1b..1aa045df2 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5375,6 +5375,9 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in animDuration = (t > animDuration)? t : animDuration; } + strncpy(animations[i].name, animData.name, sizeof(animations[i].name)); + animations[i].name[sizeof(animations[i].name) - 1] = '\0'; + animations[i].frameCount = (int)(animDuration*1000.0f/GLTF_ANIMDELAY); animations[i].framePoses = RL_MALLOC(animations[i].frameCount*sizeof(Transform *)); From 05e71f990c3e5fdbc4f7a7888d3af534cc2bf113 Mon Sep 17 00:00:00 2001 From: Scott Helvick <4016577+shelvick@users.noreply.github.com> Date: Sun, 7 May 2023 11:21:47 -0700 Subject: [PATCH 0064/1350] Version bump for CL bindings (#3049) --- BINDINGS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BINDINGS.md b/BINDINGS.md index 8f293e78e..c66f6a34d 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -11,7 +11,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | Raylib-cs | **4.5** | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | Zlib | https://github.com/ChrisDill/Raylib-cs | | Raylib-CsLo | 4.2 | [C#](https://en.wikipedia.org/wiki/C_Sharp_(programming_language)) | MPL-2.0 | https://github.com/NotNotTech/Raylib-CsLo | | cl-raylib | 4.0 | [Common Lisp](https://common-lisp.net/) | MIT | https://github.com/longlene/cl-raylib | -| claylib/wrap | 4.2 | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | +| claylib/wrap | **4.5** | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | | chez-raylib | **auto** | [Chez Scheme](https://cisco.github.io/ChezScheme/) | GPLv3 | https://github.com/Yunoinsky/chez-raylib | | raylib-cr | **4.5-dev (7e7939e)** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | | ray-cyber | 4.5 | [Cyber](https://cyberscript.dev) | MIT | https://github.com/fubark/ray-cyber | @@ -80,7 +80,7 @@ These are utility wrappers for specific languages, they are not required to use | name | raylib version | language | license | repo | |:------------------:|:-------------: | :--------:|:-------:|:-------------------------------------------------------------| | raylib-cpp | **4.5** | [C++](https://en.wikipedia.org/wiki/C%2B%2B) | Zlib | https://github.com/robloach/raylib-cpp | -| claylib | **4.2** | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | +| claylib | **4.5** | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | ### Older or Unmaintained Language Bindings These are older raylib bindings that are more than 2 versions old or have not been maintained. From 204c6765bd12a63a0cab90ee536bdec321b80bee Mon Sep 17 00:00:00 2001 From: Shoozza Date: Sun, 7 May 2023 20:22:37 +0200 Subject: [PATCH 0065/1350] Fix vs code project (#3048) * Modify remove trailing whitespace * Fix invalid SetCameraMode call and missing argument to UpdateCamera for VSCode project --- projects/VSCode/Makefile | 8 +++---- projects/VSCode/Makefile.Android | 38 ++++++++++++++++---------------- projects/VSCode/main.c | 8 +++---- 3 files changed, 26 insertions(+), 28 deletions(-) diff --git a/projects/VSCode/Makefile b/projects/VSCode/Makefile index 9b8cbea1f..9ab042e8f 100644 --- a/projects/VSCode/Makefile +++ b/projects/VSCode/Makefile @@ -166,7 +166,7 @@ ifeq ($(PLATFORM),PLATFORM_RPI) endif ifeq ($(PLATFORM),PLATFORM_WEB) # HTML5 emscripten compiler - # WARNING: To compile to HTML5, code must be redesigned + # WARNING: To compile to HTML5, code must be redesigned # to use emscripten.h and emscripten_set_main_loop() CC = emcc endif @@ -301,12 +301,12 @@ ifeq ($(PLATFORM),PLATFORM_DESKTOP) # Libraries for Debian GNU/Linux desktop compiling # NOTE: Required packages: libegl1-mesa-dev LDLIBS = -lraylib -lGL -lm -lpthread -ldl -lrt - + # On X11 requires also below libraries LDLIBS += -lX11 # NOTE: It seems additional libraries are not required any more, latest GLFW just dlopen them #LDLIBS += -lXrandr -lXinerama -lXi -lXxf86vm -lXcursor - + # On Wayland windowing system, additional libraries requires ifeq ($(USE_WAYLAND_DISPLAY),TRUE) LDLIBS += -lwayland-client -lwayland-cursor -lwayland-egl -lxkbcommon @@ -358,7 +358,7 @@ OBJS ?= main.c # For Android platform we call a custom Makefile.Android ifeq ($(PLATFORM),PLATFORM_ANDROID) - MAKEFILE_PARAMS = -f Makefile.Android + MAKEFILE_PARAMS = -f Makefile.Android export PROJECT_NAME export SRC_DIR else diff --git a/projects/VSCode/Makefile.Android b/projects/VSCode/Makefile.Android index 29d437b1b..7e41ea52f 100644 --- a/projects/VSCode/Makefile.Android +++ b/projects/VSCode/Makefile.Android @@ -50,7 +50,7 @@ PROJECT_BUILD_PATH ?= android.$(PROJECT_NAME) PROJECT_RESOURCES_PATH ?= resources PROJECT_SOURCE_FILES ?= raylib_game.c -# Some source files are placed in directories, when compiling to some +# Some source files are placed in directories, when compiling to some # output directory other than source, that directory must pre-exist. # Here we get a list of required folders that need to be created on # code output folder $(PROJECT_BUILD_PATH)\obj to avoid GCC errors. @@ -77,7 +77,7 @@ RAYLIB_LIB_PATH = $(RAYLIB_PATH)\src # Shared libs must be added to APK if required # NOTE: Generated NativeLoader.java automatically load those libraries ifeq ($(RAYLIB_LIBTYPE),SHARED) - PROJECT_SHARED_LIBS = lib/$(ANDROID_ARCH_NAME)/libraylib.so + PROJECT_SHARED_LIBS = lib/$(ANDROID_ARCH_NAME)/libraylib.so endif # Compiler and archiver @@ -109,8 +109,8 @@ CFLAGS += -DANDROID -DPLATFORM_ANDROID -D__ANDROID_API__=$(ANDROID_API_VERSION) INCLUDE_PATHS = -I. -I$(RAYLIB_PATH)/src -I$(RAYLIB_PATH)/src/external/android/native_app_glue # Linker options -LDFLAGS = -Wl,-soname,lib$(PROJECT_LIBRARY_NAME).so -Wl,--exclude-libs,libatomic.a -LDFLAGS += -Wl,--build-id -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -Wl,--warn-shared-textrel -Wl,--fatal-warnings +LDFLAGS = -Wl,-soname,lib$(PROJECT_LIBRARY_NAME).so -Wl,--exclude-libs,libatomic.a +LDFLAGS += -Wl,--build-id -Wl,--no-undefined -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -Wl,--warn-shared-textrel -Wl,--fatal-warnings # Force linking of library module to define symbol LDFLAGS += -u ANativeActivity_onCreate # Library paths containing required libs @@ -141,7 +141,7 @@ all: create_temp_project_dirs \ # Create required temp directories for APK building create_temp_project_dirs: - if not exist $(PROJECT_BUILD_PATH) mkdir $(PROJECT_BUILD_PATH) + if not exist $(PROJECT_BUILD_PATH) mkdir $(PROJECT_BUILD_PATH) if not exist $(PROJECT_BUILD_PATH)\obj mkdir $(PROJECT_BUILD_PATH)\obj if not exist $(PROJECT_BUILD_PATH)\src mkdir $(PROJECT_BUILD_PATH)\src if not exist $(PROJECT_BUILD_PATH)\src\com mkdir $(PROJECT_BUILD_PATH)\src\com @@ -163,15 +163,15 @@ create_temp_project_dirs: define create_dir if not exist $(PROJECT_BUILD_PATH)\obj\$(1) mkdir $(PROJECT_BUILD_PATH)\obj\$(1) endef - + # Copy required shared libs for integration into APK # NOTE: If using shared libs they are loaded by generated NativeLoader.java copy_project_required_libs: ifeq ($(RAYLIB_LIBTYPE),SHARED) - copy /Y $(RAYLIB_LIB_PATH)\libraylib.so $(PROJECT_BUILD_PATH)\lib\$(ANDROID_ARCH_NAME)\libraylib.so + copy /Y $(RAYLIB_LIB_PATH)\libraylib.so $(PROJECT_BUILD_PATH)\lib\$(ANDROID_ARCH_NAME)\libraylib.so endif ifeq ($(RAYLIB_LIBTYPE),STATIC) - copy /Y $(RAYLIB_LIB_PATH)\libraylib.a $(PROJECT_BUILD_PATH)\lib\$(ANDROID_ARCH_NAME)\libraylib.a + copy /Y $(RAYLIB_LIB_PATH)\libraylib.a $(PROJECT_BUILD_PATH)\lib\$(ANDROID_ARCH_NAME)\libraylib.a endif # Copy project required resources: strings.xml, icon.png, assets @@ -195,10 +195,10 @@ generate_loader_script: ifeq ($(RAYLIB_LIBTYPE),SHARED) @echo System.loadLibrary("raylib"); >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java endif - @echo System.loadLibrary("$(PROJECT_LIBRARY_NAME)"); >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java + @echo System.loadLibrary("$(PROJECT_LIBRARY_NAME)"); >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java @echo } >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java @echo } >> $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java - + # Generate AndroidManifest.xml with all the required options # NOTE: Probably not the bet way to generate this file... but it works. generate_android_manifest: @@ -225,7 +225,7 @@ generate_android_manifest: # Generate storekey for APK signing: $(PROJECT_NAME).keystore # NOTE: Configure here your Distinguished Names (-dname) if required! -generate_apk_keystore: +generate_apk_keystore: if not exist $(PROJECT_BUILD_PATH)/$(PROJECT_NAME).keystore $(JAVA_HOME)/bin/keytool -genkeypair -validity 1000 -dname "CN=$(APP_COMPANY_NAME),O=Android,C=ES" -keystore $(PROJECT_BUILD_PATH)/$(PROJECT_NAME).keystore -storepass $(APP_KEYSTORE_PASS) -keypass $(APP_KEYSTORE_PASS) -alias $(PROJECT_NAME)Key -keyalg RSA # Config project package and resource using AndroidManifest.xml and res/values/strings.xml @@ -238,16 +238,16 @@ compile_native_app_glue: $(CC) -c $(RAYLIB_PATH)/src/external/android/native_app_glue/android_native_app_glue.c -o $(PROJECT_BUILD_PATH)/obj/native_app_glue.o $(CFLAGS) $(AR) rcs $(PROJECT_BUILD_PATH)/obj/libnative_app_glue.a $(PROJECT_BUILD_PATH)/obj/native_app_glue.o -# Compile project code into a shared library: lib/lib$(PROJECT_LIBRARY_NAME).so +# Compile project code into a shared library: lib/lib$(PROJECT_LIBRARY_NAME).so compile_project_code: $(OBJS) $(CC) -o $(PROJECT_BUILD_PATH)/lib/$(ANDROID_ARCH_NAME)/lib$(PROJECT_LIBRARY_NAME).so $(OBJS) -shared $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) # Compile all .c files required into object (.o) files # NOTE: Those files will be linked into a shared library $(PROJECT_BUILD_PATH)/obj/%.o:%.c - $(CC) -c $^ -o $@ $(INCLUDE_PATHS) $(CFLAGS) --sysroot=$(ANDROID_TOOLCHAIN)/sysroot - -# Compile project .java code into .class (Java bytecode) + $(CC) -c $^ -o $@ $(INCLUDE_PATHS) $(CFLAGS) --sysroot=$(ANDROID_TOOLCHAIN)/sysroot + +# Compile project .java code into .class (Java bytecode) compile_project_class: $(JAVA_HOME)/bin/javac -verbose -source 1.7 -target 1.7 -d $(PROJECT_BUILD_PATH)/obj -bootclasspath $(JAVA_HOME)/jre/lib/rt.jar -classpath $(ANDROID_HOME)/platforms/android-$(ANDROID_API_VERSION)/android.jar;$(PROJECT_BUILD_PATH)/obj -sourcepath $(PROJECT_BUILD_PATH)/src $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/R.java $(PROJECT_BUILD_PATH)/src/com/$(APP_COMPANY_NAME)/$(APP_PRODUCT_NAME)/NativeLoader.java @@ -263,11 +263,11 @@ create_project_apk_package: $(ANDROID_BUILD_TOOLS)/aapt package -f -M $(PROJECT_BUILD_PATH)/AndroidManifest.xml -S $(PROJECT_BUILD_PATH)/res -A $(PROJECT_BUILD_PATH)/assets -I $(ANDROID_HOME)/platforms/android-$(ANDROID_API_VERSION)/android.jar -F $(PROJECT_BUILD_PATH)/bin/$(PROJECT_NAME).unsigned.apk $(PROJECT_BUILD_PATH)/bin cd $(PROJECT_BUILD_PATH) && $(ANDROID_BUILD_TOOLS)/aapt add bin/$(PROJECT_NAME).unsigned.apk lib/$(ANDROID_ARCH_NAME)/lib$(PROJECT_LIBRARY_NAME).so $(PROJECT_SHARED_LIBS) -# Create signed APK package using generated Key: bin/$(PROJECT_NAME).signed.apk +# Create signed APK package using generated Key: bin/$(PROJECT_NAME).signed.apk sign_project_apk_package: $(JAVA_HOME)/bin/jarsigner -keystore $(PROJECT_BUILD_PATH)/$(PROJECT_NAME).keystore -storepass $(APP_KEYSTORE_PASS) -keypass $(APP_KEYSTORE_PASS) -signedjar $(PROJECT_BUILD_PATH)/bin/$(PROJECT_NAME).signed.apk $(PROJECT_BUILD_PATH)/bin/$(PROJECT_NAME).unsigned.apk $(PROJECT_NAME)Key -# Create zip-aligned APK package: $(PROJECT_NAME).apk +# Create zip-aligned APK package: $(PROJECT_NAME).apk zipalign_project_apk_package: $(ANDROID_BUILD_TOOLS)/zipalign -f 4 $(PROJECT_BUILD_PATH)/bin/$(PROJECT_NAME).signed.apk $(PROJECT_NAME).apk @@ -275,7 +275,7 @@ zipalign_project_apk_package: # NOTE: Use -e (emulator) or -d (device) parameters if required install: $(ANDROID_PLATFORM_TOOLS)/adb install --abi $(ANDROID_ARCH_NAME) -rds $(PROJECT_NAME).apk - + # Check supported ABI for the device (armeabi-v7a, arm64-v8a, x86, x86_64) check_device_abi: $(ANDROID_PLATFORM_TOOLS)/adb shell getprop ro.product.cpu.abi @@ -284,7 +284,7 @@ check_device_abi: logcat: $(ANDROID_PLATFORM_TOOLS)/adb logcat -c $(ANDROID_PLATFORM_TOOLS)/adb logcat raylib:V *:S - + # Install and monitorize $(PROJECT_NAME).apk to default emulator/device deploy: $(ANDROID_PLATFORM_TOOLS)/adb install -r $(PROJECT_NAME).apk diff --git a/projects/VSCode/main.c b/projects/VSCode/main.c index 0794672d0..f37b3b28d 100644 --- a/projects/VSCode/main.c +++ b/projects/VSCode/main.c @@ -39,7 +39,7 @@ static void UpdateDrawFrame(void); // Update and draw one frame //---------------------------------------------------------------------------------- // Main entry point //---------------------------------------------------------------------------------- -int main() +int main() { // Initialization //-------------------------------------------------------------------------------------- @@ -53,8 +53,6 @@ int main() camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; camera.fovy = 60.0f; camera.projection = CAMERA_PERSPECTIVE; - - SetCameraMode(camera, CAMERA_ORBITAL); //-------------------------------------------------------------------------------------- @@ -84,7 +82,7 @@ static void UpdateDrawFrame(void) { // Update //---------------------------------------------------------------------------------- - UpdateCamera(&camera); + UpdateCamera(&camera, CAMERA_ORBITAL); //---------------------------------------------------------------------------------- // Draw @@ -107,4 +105,4 @@ static void UpdateDrawFrame(void) EndDrawing(); //---------------------------------------------------------------------------------- -} \ No newline at end of file +} From 81e2c970950cc9a671b270376065ec6012c28c88 Mon Sep 17 00:00:00 2001 From: Kenta <106167071+Its-Kenta@users.noreply.github.com> Date: Mon, 8 May 2023 15:00:21 +0100 Subject: [PATCH 0066/1350] Update BINDINGS.md (#3050) Update C3 binding to use Raylib version 4.5 --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index c66f6a34d..035a44406 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -15,7 +15,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | chez-raylib | **auto** | [Chez Scheme](https://cisco.github.io/ChezScheme/) | GPLv3 | https://github.com/Yunoinsky/chez-raylib | | raylib-cr | **4.5-dev (7e7939e)** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | | ray-cyber | 4.5 | [Cyber](https://cyberscript.dev) | MIT | https://github.com/fubark/ray-cyber | -| raylib-c3 | **4.5-dev** | [C3](https://c3-lang.org/) | MIT | https://github.com/Its-Kenta/Raylib-C3 | +| raylib-c3 | **4.5** | [C3](https://c3-lang.org/) | MIT | https://github.com/Its-Kenta/Raylib-C3 | | dart-raylib | 4.0 | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | | bindbc-raylib3 | 4.0 | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | | dray | 4.2 | [D](https://dlang.org/) | Apache-2.0 | https://github.com/redthing1/dray | From fe595d60f7c15d93e03a5c82c90620dddafa7fc7 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 8 May 2023 18:57:36 +0200 Subject: [PATCH 0067/1350] Remove trailing spaces --- src/external/rl_gputex.h | 2 +- src/raymath.h | 4 ++-- src/utils.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/external/rl_gputex.h b/src/external/rl_gputex.h index f7cc00ecf..fa39fe29c 100644 --- a/src/external/rl_gputex.h +++ b/src/external/rl_gputex.h @@ -9,7 +9,7 @@ * * Note that some file formats (DDS, PVR, KTX) also support uncompressed data storage. * In those cases data is loaded uncompressed and format is returned. -* +* * TODO: * - Implement raylib function: rlGetGlTextureFormats(), required by rl_save_ktx_to_memory() * - Review rl_load_ktx_from_memory() to support KTX v2.2 specs diff --git a/src/raymath.h b/src/raymath.h index fbb970486..47728ff69 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -3,10 +3,10 @@ * raymath v1.5 - Math functions to work with Vector2, Vector3, Matrix and Quaternions * * CONVENTIONS: -* - Matrix structure is defined as row-major (memory layout) but parameters naming AND all +* - Matrix structure is defined as row-major (memory layout) but parameters naming AND all * math operations performed by the library consider the structure as it was column-major * It is like transposed versions of the matrices are used for all the maths -* It benefits some functions making them cache-friendly and also avoids matrix +* It benefits some functions making them cache-friendly and also avoids matrix * transpositions sometimes required by OpenGL * Example: In memory order, row0 is [m0 m4 m8 m12] but in semantic math row0 is [m0 m1 m2 m3] * - Functions are always self-contained, no function use another raymath function inside, diff --git a/src/utils.c b/src/utils.c index 4175fb693..01ca235fa 100644 --- a/src/utils.c +++ b/src/utils.c @@ -298,7 +298,7 @@ bool ExportDataAsCode(const unsigned char *data, unsigned int size, const char * char varFileName[256] = { 0 }; strcpy(varFileName, GetFileNameWithoutExt(fileName)); for (int i = 0; varFileName[i] != '\0'; i++) if ((varFileName[i] >= 'a') && (varFileName[i] <= 'z')) { varFileName[i] = varFileName[i] - 32; } - + byteCount += sprintf(txtData + byteCount, "#define %s_DATA_SIZE %i\n\n", varFileName, size); byteCount += sprintf(txtData + byteCount, "static unsigned char %s_DATA[%s_DATA_SIZE] = { ", varFileName, varFileName); From 152262dbfca8904e0250662dcee3985f57d7f9a3 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 10 May 2023 12:48:17 +0200 Subject: [PATCH 0068/1350] Update cgltf.h --- src/external/cgltf.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/external/cgltf.h b/src/external/cgltf.h index a4d9c72de..d2a909777 100644 --- a/src/external/cgltf.h +++ b/src/external/cgltf.h @@ -65,7 +65,8 @@ * * `cgltf_num_components` is a tiny utility that tells you the dimensionality of * a certain accessor type. This can be used before `cgltf_accessor_unpack_floats` to help allocate - * the necessary amount of memory. + * the necessary amount of memory. `cgltf_component_size` and `cgltf_calc_size` exist for + * similar purposes. * * `cgltf_accessor_read_float` reads a certain element from a non-sparse accessor and converts it to * floating point, assuming that `cgltf_load_buffers` has already been called. The passed-in element @@ -837,6 +838,8 @@ cgltf_bool cgltf_accessor_read_uint(const cgltf_accessor* accessor, cgltf_size i cgltf_size cgltf_accessor_read_index(const cgltf_accessor* accessor, cgltf_size index); cgltf_size cgltf_num_components(cgltf_type type); +cgltf_size cgltf_component_size(cgltf_component_type component_type); +cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type); cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count); @@ -1488,8 +1491,6 @@ cgltf_result cgltf_load_buffers(const cgltf_options* options, cgltf_data* data, return cgltf_result_success; } -static cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type); - static cgltf_size cgltf_calc_index_bound(cgltf_buffer_view* buffer_view, cgltf_size offset, cgltf_component_type component_type, cgltf_size count) { char* data = (char*)buffer_view->buffer->data + offset + buffer_view->offset; @@ -2217,7 +2218,7 @@ static cgltf_size cgltf_component_read_index(const void* in, cgltf_component_typ case cgltf_component_type_r_32u: return *((const uint32_t*) in); case cgltf_component_type_r_32f: - return (cgltf_size)*((const float*) in); + return (cgltf_size)((cgltf_ssize)*((const float*) in)); case cgltf_component_type_r_8u: return *((const uint8_t*) in); default: @@ -2253,8 +2254,6 @@ static cgltf_float cgltf_component_read_float(const void* in, cgltf_component_ty return (cgltf_float)cgltf_component_read_integer(in, component_type); } -static cgltf_size cgltf_component_size(cgltf_component_type component_type); - static cgltf_bool cgltf_element_read_float(const uint8_t* element, cgltf_type type, cgltf_component_type component_type, cgltf_bool normalized, cgltf_float* out, cgltf_size element_size) { cgltf_size num_components = cgltf_num_components(type); @@ -5965,7 +5964,7 @@ cgltf_size cgltf_num_components(cgltf_type type) { } } -static cgltf_size cgltf_component_size(cgltf_component_type component_type) { +cgltf_size cgltf_component_size(cgltf_component_type component_type) { switch (component_type) { case cgltf_component_type_r_8: @@ -5983,7 +5982,7 @@ static cgltf_size cgltf_component_size(cgltf_component_type component_type) { } } -static cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type) +cgltf_size cgltf_calc_size(cgltf_type type, cgltf_component_type component_type) { cgltf_size component_size = cgltf_component_size(component_type); if (type == cgltf_type_mat2 && component_size == 1) From af4b97a301c39ca8df628b1889b8f6acd393a0f5 Mon Sep 17 00:00:00 2001 From: manuel5975p Date: Wed, 10 May 2023 19:19:59 +0200 Subject: [PATCH 0069/1350] Update GetCollisionRec (#3052) * Update rshapes.c Add a much more efficient GetCollisionRec implementation * Update GetCollisionRec Replace macros with ternary operators --- src/rshapes.c | 85 +++++++++++++-------------------------------------- 1 file changed, 22 insertions(+), 63 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index 45cf6ac67..f7546cdf4 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -1756,70 +1756,29 @@ bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2, int threshol } // Get collision rectangle for two rectangles collision -Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2) -{ - Rectangle rec = { 0, 0, 0, 0 }; - - if (CheckCollisionRecs(rec1, rec2)) - { - float dxx = fabsf(rec1.x - rec2.x); - float dyy = fabsf(rec1.y - rec2.y); - - if (rec1.x <= rec2.x) - { - if (rec1.y <= rec2.y) - { - rec.x = rec2.x; - rec.y = rec2.y; - rec.width = rec1.width - dxx; - rec.height = rec1.height - dyy; - } - else - { - rec.x = rec2.x; - rec.y = rec1.y; - rec.width = rec1.width - dxx; - rec.height = rec2.height - dyy; - } - } - else - { - if (rec1.y <= rec2.y) - { - rec.x = rec1.x; - rec.y = rec2.y; - rec.width = rec2.width - dxx; - rec.height = rec1.height - dyy; - } - else - { - rec.x = rec1.x; - rec.y = rec1.y; - rec.width = rec2.width - dxx; - rec.height = rec2.height - dyy; - } - } - - if (rec1.width > rec2.width) - { - if (rec.width >= rec2.width) rec.width = rec2.width; - } - else - { - if (rec.width >= rec1.width) rec.width = rec1.width; - } - - if (rec1.height > rec2.height) - { - if (rec.height >= rec2.height) rec.height = rec2.height; - } - else - { - if (rec.height >= rec1.height) rec.height = rec1.height; - } +Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2){ + Rectangle overlap; + float left = ((rec1.x) > (rec2.x) ? (rec1.x) : (rec2.x)); + float right1 = rec1.x + rec1.width; + float right2 = rec2.x + rec2.width; + float right = ((right1) < (right2) ? (right1) : (right2)); + float top = ((rec1.y) > (rec2.y) ? (rec1.y) : (rec2.y)); + float bottom1 = rec1.y + rec1.height; + float bottom2 = rec2.y + rec2.height; + float bottom = ((bottom1) < (bottom2) ? (bottom1) : (bottom2)); + if (left < right && top < bottom){ + overlap.x = (left); + overlap.y = (top); + overlap.width = (right - left ); + overlap.height = (bottom - top); } - - return rec; + else{ + overlap.x = 0; + overlap.y = 0; + overlap.width = 0; + overlap.height = 0; + } + return overlap; } //---------------------------------------------------------------------------------- From 452e3b494cd6bceaab290d0f5f23bff9c7887a1b Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 10 May 2023 19:25:12 +0200 Subject: [PATCH 0070/1350] REVIEWED: `GetCollisionRec()` --- src/rshapes.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index f7546cdf4..278886423 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -1756,28 +1756,27 @@ bool CheckCollisionPointLine(Vector2 point, Vector2 p1, Vector2 p2, int threshol } // Get collision rectangle for two rectangles collision -Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2){ - Rectangle overlap; - float left = ((rec1.x) > (rec2.x) ? (rec1.x) : (rec2.x)); +Rectangle GetCollisionRec(Rectangle rec1, Rectangle rec2) +{ + Rectangle overlap = { 0 }; + + float left = (rec1.x > rec2.x)? rec1.x : rec2.x; float right1 = rec1.x + rec1.width; float right2 = rec2.x + rec2.width; - float right = ((right1) < (right2) ? (right1) : (right2)); - float top = ((rec1.y) > (rec2.y) ? (rec1.y) : (rec2.y)); + float right = (right1 < right2)? right1 : right2; + float top = (rec1.y > rec2.y)? rec1.y : rec2.y; float bottom1 = rec1.y + rec1.height; float bottom2 = rec2.y + rec2.height; - float bottom = ((bottom1) < (bottom2) ? (bottom1) : (bottom2)); - if (left < right && top < bottom){ - overlap.x = (left); - overlap.y = (top); - overlap.width = (right - left ); - overlap.height = (bottom - top); - } - else{ - overlap.x = 0; - overlap.y = 0; - overlap.width = 0; - overlap.height = 0; + float bottom = (bottom1 < bottom2)? bottom1 : bottom2; + + if ((left < right) && (top < bottom)) + { + overlap.x = left; + overlap.y = top; + overlap.width = right - left; + overlap.height = bottom - top; } + return overlap; } From 5978358e5877b0033584334c5bf6ec7d8cf22ae8 Mon Sep 17 00:00:00 2001 From: JupiterRider <60042618+JupiterRider@users.noreply.github.com> Date: Thu, 11 May 2023 19:48:53 +0200 Subject: [PATCH 0071/1350] Update BINDINGS.md (#3053) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 035a44406..a10e5860f 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -25,7 +25,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-factor | 4.0 | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | | raylib-freebasic | **4.5** | [FreeBASIC](https://www.freebasic.net/) | MIT | https://github.com/WIITD/raylib-freebasic | | raylib for Pascal | **4.5** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | Modified Zlib | https://github.com/tinyBigGAMES/raylib | -| raylib-go | 4.2 | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | +| raylib-go | 4.5 | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | | raylib-guile | **auto** | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | | gforth-raylib | 3.5 | [Gforth](https://gforth.org/) | MIT | https://github.com/ArnautDaniel/gforth-raylib | | h-raylib | **4.5** | [Haskell](https://haskell.org/) | Apache-2.0 | https://github.com/Anut-py/h-raylib | From cc17a7656c04e114ca67771f89b0fb8f493f8a1f Mon Sep 17 00:00:00 2001 From: Michael Scherbakow Date: Sun, 14 May 2023 21:14:16 +0200 Subject: [PATCH 0072/1350] Update build.zig be be able to build with current zig master (#3064) --- src/build.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/build.zig b/src/build.zig index e4c1e0c44..58cf84fec 100644 --- a/src/build.zig +++ b/src/build.zig @@ -8,7 +8,11 @@ pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build. "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 }; - const raylib = b.addStaticLibrary("raylib", null); + const raylib = b.addStaticLibrary(std.Build.StaticLibraryOptions{ + .name = "raylib", + .target = target, + .optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseSafe }), + }); raylib.linkLibC(); raylib.addIncludePath(srcdir ++ "/external/glfw/include"); From 818312683ee92efde550f7eaa88fcceb773eaf0b Mon Sep 17 00:00:00 2001 From: lesleyrs <19632758+lesleyrs@users.noreply.github.com> Date: Sun, 14 May 2023 21:15:32 +0200 Subject: [PATCH 0073/1350] update cmake example project (#3062) * update cmake example project * off is the correct one --- projects/CMake/CMakeLists.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/projects/CMake/CMakeLists.txt b/projects/CMake/CMakeLists.txt index cfd924fba..cc606a4a2 100644 --- a/projects/CMake/CMakeLists.txt +++ b/projects/CMake/CMakeLists.txt @@ -5,12 +5,13 @@ project(example) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Dependencies -set(RAYLIB_VERSION 4.2.0) +set(RAYLIB_VERSION 4.5.0) find_package(raylib ${RAYLIB_VERSION} QUIET) # QUIET or REQUIRED if (NOT raylib_FOUND) # If there's none, fetch and build raylib include(FetchContent) FetchContent_Declare( raylib + DOWNLOAD_EXTRACT_TIMESTAMP OFF URL https://github.com/raysan5/raylib/archive/refs/tags/${RAYLIB_VERSION}.tar.gz ) FetchContent_GetProperties(raylib) From 26a3536958e9d4d641edbff0530c41625871b6be Mon Sep 17 00:00:00 2001 From: hamyy Date: Mon, 15 May 2023 05:20:43 +1000 Subject: [PATCH 0074/1350] GetCurrentMonitor() bugfix (#3058) * GetCurrentMonitor() bugfix * GetCurrentMonitor() bugfix --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 476ea8e2f..881739c73 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1785,7 +1785,7 @@ int GetCurrentMonitor(void) monitor = monitors[i]; glfwGetMonitorWorkarea(monitor, &mx, &my, &width, &height); - if (x >= mx && x <= (mx + width) && y >= my && y <= (my + height)) + if (x >= mx && x < (mx + width) && y >= my && y < (my + height)) { index = i; break; From 6b92d71ea1c4e3072b26f25e7b8bd1d1aa8e781f Mon Sep 17 00:00:00 2001 From: Gamer-Kold <84700933+Gamer-Kold@users.noreply.github.com> Date: Mon, 15 May 2023 14:23:36 +0500 Subject: [PATCH 0075/1350] Reverted commits that deprecated the build.zig files, and added a note to all of them stating version of zig they were using (#3060) * Revert "Fixed broken build.zig files. Now works with latest stable compiler (as of commit, latest is 0.10.1) (#3045)" This reverts commit de748dfffefeba1ba9bcf0c90c538d32c9cb2020 so that zig build script works with master branch of zig. * Added a note to build.zig files that denotes what version of zig they have been tested with. * Standardised the note in the build.zig files --- build.zig | 3 ++- examples/build.zig | 1 + src/build.zig | 15 ++++++++------- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/build.zig b/build.zig index 042338a6c..12c0513f6 100644 --- a/build.zig +++ b/build.zig @@ -1,6 +1,7 @@ const std = @import("std"); const raylib = @import("src/build.zig"); -pub fn build(b: *std.build.Builder) void { +// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +pub fn build(b: *std.Build) void { raylib.build(b); } diff --git a/examples/build.zig b/examples/build.zig index f94a3b4bf..6e13ab3da 100644 --- a/examples/build.zig +++ b/examples/build.zig @@ -1,6 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); +// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 fn add_module(comptime module: []const u8, b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) !*std.Build.Step { if (target.getOsTag() == .emscripten) { @panic("Emscripten building via Zig unsupported"); diff --git a/src/build.zig b/src/build.zig index 58cf84fec..48e93c326 100644 --- a/src/build.zig +++ b/src/build.zig @@ -1,6 +1,7 @@ const std = @import("std"); -pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build.LibExeObjStep { +// This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 +pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep { const raylib_flags = &[_][]const u8{ "-std=gnu99", "-D_GNU_SOURCE", @@ -8,10 +9,10 @@ pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build. "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 }; - const raylib = b.addStaticLibrary(std.Build.StaticLibraryOptions{ + const raylib = b.addStaticLibrary(.{ .name = "raylib", .target = target, - .optimize = b.standardOptimizeOption(.{ .preferred_optimize_mode = .ReleaseSafe }), + .optimize = optimize, }); raylib.linkLibC(); @@ -103,7 +104,7 @@ pub fn addRaylib(b: *std.build.Builder, target: std.zig.CrossTarget) *std.build. return raylib; } -pub fn build(b: *std.build.Builder) void { +pub fn build(b: *std.Build) void { // Standard target options allows the person running `zig build` to choose // what target to build for. Here we do not override the defaults, which // means any target is allowed, and the default is native. Other options @@ -112,10 +113,10 @@ pub fn build(b: *std.build.Builder) void { // Standard optimization options allow the person running `zig build` to select // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not // set a preferred release mode, allowing the user to decide how to optimize. - // const optimize = b.standardReleaseOptions(); + const optimize = b.standardOptimizeOption(.{}); - const lib = addRaylib(b, target); - b.installFile("src/raylib.h", "raylib.h"); + const lib = addRaylib(b, target, optimize); + lib.installHeader("src/raylib.h", "raylib.h"); b.installArtifact(lib); } From 3438325e7d657e2eadd71226c147f9f0f6ce5f55 Mon Sep 17 00:00:00 2001 From: Crynux <43756150+crynux@users.noreply.github.com> Date: Mon, 15 May 2023 03:24:37 -0600 Subject: [PATCH 0076/1350] Update rmodels.c; free fileData for LoadModelAnimationsGLTF (#3065) fileData wasn't freed for LoadModelAnimationsGLTF causing a memory leak. Added UnloadFileData line, freeing it. --- src/rmodels.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index 1aa045df2..c4073eed3 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5434,7 +5434,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in cgltf_free(data); } - + UnloadFileData(fileData); return animations; } #endif From 3f8ef4e05f1bb4ca61c43dbcb87f9a9bc0d26f0d Mon Sep 17 00:00:00 2001 From: Pixel Phobic <65147646+PixelPhobicGames@users.noreply.github.com> Date: Mon, 15 May 2023 02:25:44 -0700 Subject: [PATCH 0077/1350] Updated (rcamera) To Add Analog Stick Cam Controls (#3066) I Noticed While Writing Some code for My Game im Making that there Isn't an Easy way to control the Camera With The Analog Sticks on Controller. I Added a Couple Lines to the UpdateCamera Function :) --- src/rcamera.h | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/rcamera.h b/src/rcamera.h index 0b9b8076a..22a468cfb 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -447,14 +447,28 @@ void UpdateCamera(Camera *camera, int mode) if (IsKeyDown(KEY_Q)) CameraRoll(camera, -CAMERA_ROTATION_SPEED); if (IsKeyDown(KEY_E)) CameraRoll(camera, CAMERA_ROTATION_SPEED); - CameraYaw(camera, -mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); - CameraPitch(camera, -mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); - // Camera movement - if (IsKeyDown(KEY_W)) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); - if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); - if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); - if (IsKeyDown(KEY_D)) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + + if (!IsGamepadAvailable(0)){ + CameraYaw(camera, -mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); + CameraPitch(camera, -mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); + + if (IsKeyDown(KEY_W)) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); + if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); + if (IsKeyDown(KEY_D)) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + } + else { + // Simple Controller Support + CameraYaw(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_X) * 2)*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); + CameraPitch(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_Y) * 2)*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); + + if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) <= -0.75f) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) <= -0.75f) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); + if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) >= 0.75f) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); + if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) >= 0.75f) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + } + //if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, CAMERA_MOVE_SPEED); //if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -CAMERA_MOVE_SPEED); } From e17cf9ecd7e770e349a74211bc61170f4d23f8e8 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 15 May 2023 11:30:31 +0200 Subject: [PATCH 0078/1350] REVIEWED: Formating --- src/rcamera.h | 10 ++++++---- src/rcore.c | 5 ++++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/rcamera.h b/src/rcamera.h index 22a468cfb..c1b048e01 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -448,8 +448,9 @@ void UpdateCamera(Camera *camera, int mode) if (IsKeyDown(KEY_E)) CameraRoll(camera, CAMERA_ROTATION_SPEED); // Camera movement - - if (!IsGamepadAvailable(0)){ + if (!IsGamepadAvailable(0)) + { + // Mouse/Keyboard support CameraYaw(camera, -mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); CameraPitch(camera, -mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); @@ -458,8 +459,9 @@ void UpdateCamera(Camera *camera, int mode) if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); if (IsKeyDown(KEY_D)) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); } - else { - // Simple Controller Support + else + { + // Gamepad controller support CameraYaw(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_X) * 2)*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); CameraPitch(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_Y) * 2)*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); diff --git a/src/rcore.c b/src/rcore.c index 881739c73..31a06f7ce 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1785,7 +1785,10 @@ int GetCurrentMonitor(void) monitor = monitors[i]; glfwGetMonitorWorkarea(monitor, &mx, &my, &width, &height); - if (x >= mx && x < (mx + width) && y >= my && y < (my + height)) + if ((x >= mx) && + (x < (mx + width)) && + (y >= my) && + (y < (my + height))) { index = i; break; From 675efbda3b19aa656bc935308013f0af7466c894 Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Tue, 16 May 2023 09:00:44 +0000 Subject: [PATCH 0079/1350] Fix Android app freeze after calling CloseWindow() (#3067) Fixed that the Android application was not closed properly after calling `CloseWindow()` and continued to run. --- src/rcore.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 31a06f7ce..b41322257 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -718,6 +718,23 @@ void android_main(struct android_app *app) // NOTE: Return codes != 0 are skipped (void)main(1, (char *[]) { arg0, NULL }); + + // Finish native activity + ANativeActivity_finish(CORE.Android.app->activity); + + // Android ALooper_pollAll() variables + int pollResult = 0; + int pollEvents = 0; + + // Wait for app events to close + while (!CORE.Android.app->destroyRequested) { + while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) { + if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); + } + } + + // WARNING: Check for deallocation and ensure no other processes are running from the application. + exit(0); // Closes the application completely without going through Java } // NOTE: Add this to header (if apps really need it) @@ -5731,8 +5748,9 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) case APP_CMD_STOP: break; case APP_CMD_DESTROY: { - // TODO: Finish activity? - //ANativeActivity_finish(CORE.Android.app->activity); + // NOTE 1: Call ANativeActivity_finish again to free resources unconditionally. + // NOTE 2: You can deallocate other things that are NativeActivity related here. + ANativeActivity_finish(CORE.Android.app->activity); } break; case APP_CMD_CONFIG_CHANGED: { From c3f049fd740fca6a1cb2eb78c90fb9f20c6ecb34 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 16 May 2023 11:02:00 +0200 Subject: [PATCH 0080/1350] review formatting --- src/rcore.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index b41322257..127abcf38 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -727,14 +727,16 @@ void android_main(struct android_app *app) int pollEvents = 0; // Wait for app events to close - while (!CORE.Android.app->destroyRequested) { - while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void**)&CORE.Android.source)) >= 0) { + while (!CORE.Android.app->destroyRequested) + { + while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void **)&CORE.Android.source)) >= 0) + { if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); } } - // WARNING: Check for deallocation and ensure no other processes are running from the application. - exit(0); // Closes the application completely without going through Java + // WARNING: Check for deallocation and ensure no other processes are running from the application + exit(0); // Closes the application completely without going through Java } // NOTE: Add this to header (if apps really need it) From 3a3e672804d7c0efb429d45b22554b357c0dc11d Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 17 May 2023 11:54:32 +0200 Subject: [PATCH 0081/1350] UPDATE: miniaudio v0.11.12 --> v0.11.16 --- src/external/miniaudio.h | 3964 ++++++++++++++++++++++---------------- 1 file changed, 2338 insertions(+), 1626 deletions(-) diff --git a/src/external/miniaudio.h b/src/external/miniaudio.h index 74d584153..35eaafdcf 100644 --- a/src/external/miniaudio.h +++ b/src/external/miniaudio.h @@ -1,6 +1,6 @@ /* Audio playback and capture library. Choice of public domain or MIT-0. See license statements at the end of this file. -miniaudio - v0.11.12 - TBD +miniaudio - v0.11.16 - 2023-05-15 David Reid - mackron@gmail.com @@ -398,13 +398,13 @@ the be started and/or stopped at a specific time. This can be done with the foll ``` The start/stop time needs to be specified based on the absolute timer which is controlled by the -engine. The current global time time in PCM frames can be retrieved with `ma_engine_get_time()`. -The engine's global time can be changed with `ma_engine_set_time()` for synchronization purposes if -required. Note that scheduling a start time still requires an explicit call to `ma_sound_start()` -before anything will play: +engine. The current global time time in PCM frames can be retrieved with +`ma_engine_get_time_in_pcm_frames()`. The engine's global time can be changed with +`ma_engine_set_time_in_pcm_frames()` for synchronization purposes if required. Note that scheduling +a start time still requires an explicit call to `ma_sound_start()` before anything will play: ```c - ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2); + ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2); ma_sound_start(&sound); ``` @@ -462,6 +462,9 @@ dependencies. See below for platform-specific details. Note that GCC and Clang require `-msse2`, `-mavx2`, etc. for SIMD optimizations. +If you get errors about undefined references to `__sync_val_compare_and_swap_8`, `__atomic_load_8`, +etc. you need to link with `-latomic`. + 2.1. Windows ------------ @@ -946,7 +949,7 @@ base object (`ma_data_source_base`): // Retrieve the length in PCM frames here. Return MA_NOT_IMPLEMENTED and set *pLength to 0 if there is no notion of a length or if the length is unknown. } - static g_my_data_source_vtable = + static ma_data_source_vtable g_my_data_source_vtable = { my_data_source_read, my_data_source_seek, @@ -1286,6 +1289,10 @@ file paths which means there's a small chance you might encounter a name collisi issue, you'll need to use a different name for one of the colliding file paths, or just not load from files and instead load from a data source. +You can use `ma_sound_init_copy()` to initialize a copy of another sound. Note, however, that this +only works for sounds that were initialized with `ma_sound_init_from_file()` and without the +`MA_SOUND_FLAG_STREAM` flag. + When you initialize a sound, if you specify a sound group the sound will be attached to that group automatically. If you set it to NULL, it will be automatically attached to the engine's endpoint. If you would instead rather leave the sound unattached by default, you can can specify the @@ -1425,19 +1432,19 @@ can be useful to schedule a sound to start or stop: ```c // Start the sound in 1 second from now. - ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 1)); + ma_sound_set_start_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 1)); // Stop the sound in 2 seconds from now. - ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time(&engine) + (ma_engine_get_sample_rate(&engine) * 2)); + ma_sound_set_stop_time_in_pcm_frames(&sound, ma_engine_get_time_in_pcm_frames(&engine) + (ma_engine_get_sample_rate(&engine) * 2)); ``` Note that scheduling a start time still requires an explicit call to `ma_sound_start()` before anything will play. The time is specified in global time which is controlled by the engine. You can get the engine's -current time with `ma_engine_get_time()`. The engine's global time is incremented automatically as -audio data is read, but it can be reset with `ma_engine_set_time()` in case it needs to be -resynchronized for some reason. +current time with `ma_engine_get_time_in_pcm_frames()`. The engine's global time is incremented +automatically as audio data is read, but it can be reset with `ma_engine_set_time_in_pcm_frames()` +in case it needs to be resynchronized for some reason. To determine whether or not a sound is currently playing, use `ma_sound_is_playing()`. This will take the scheduled start and stop times into account. @@ -1446,7 +1453,25 @@ Whether or not a sound should loop can be controlled with `ma_sound_set_looping( be looping by default. Use `ma_sound_is_looping()` to determine whether or not a sound is looping. Use `ma_sound_at_end()` to determine whether or not a sound is currently at the end. For a looping -sound this should never return true. +sound this should never return true. Alternatively, you can configure a callback that will be fired +when the sound reaches the end. Note that the callback is fired from the audio thread which means +you cannot be uninitializing sound from the callback. To set the callback you can use +`ma_sound_set_end_callback()`. Alternatively, if you're using `ma_sound_init_ex()`, you can pass it +into the config like so: + + ```c + soundConfig.endCallback = my_end_callback; + soundConfig.pEndCallbackUserData = pMyEndCallbackUserData; + ``` + +The end callback is declared like so: + + ```c + void my_end_callback(void* pUserData, ma_sound* pSound) + { + ... + } + ``` Internally a sound wraps around a data source. Some APIs exist to control the underlying data source, mainly for convenience: @@ -3697,7 +3722,7 @@ extern "C" { #define MA_VERSION_MAJOR 0 #define MA_VERSION_MINOR 11 -#define MA_VERSION_REVISION 12 +#define MA_VERSION_REVISION 16 #define MA_VERSION_STRING MA_XSTRINGIFY(MA_VERSION_MAJOR) "." MA_XSTRINGIFY(MA_VERSION_MINOR) "." MA_XSTRINGIFY(MA_VERSION_REVISION) #if defined(_MSC_VER) && !defined(__clang__) @@ -3777,7 +3802,18 @@ typedef double ma_double; typedef void* ma_handle; typedef void* ma_ptr; -typedef void (* ma_proc)(void); + +/* +ma_proc is annoying because when compiling with GCC we get pendantic warnings about converting +between `void*` and `void (*)()`. We can't use `void (*)()` with MSVC however, because we'll get +warning C4191 about "type cast between incompatible function types". To work around this I'm going +to use a different data type depending on the compiler. +*/ +#if defined(__GNUC__) +typedef void (*ma_proc)(void); +#else +typedef void* ma_proc; +#endif #if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED) typedef ma_uint16 wchar_t; @@ -3796,7 +3832,7 @@ typedef ma_uint16 wchar_t; /* Platform/backend detection. */ -#ifdef _WIN32 +#if defined(_WIN32) || defined(__COSMOPOLITAN__) #define MA_WIN32 #if defined(MA_FORCE_UWP) || (defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_PC_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PC_APP) || (defined(WINAPI_FAMILY_PHONE_APP) && WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP))) #define MA_WIN32_UWP @@ -3805,7 +3841,8 @@ typedef ma_uint16 wchar_t; #else #define MA_WIN32_DESKTOP #endif -#else +#endif +#if !defined(_WIN32) /* If it's not Win32, assume POSIX. */ #define MA_POSIX /* @@ -3826,31 +3863,39 @@ typedef ma_uint16 wchar_t; typedef union ma_pthread_cond_t { char __data[48]; ma_uint64 __alignment; } ma_pthread_cond_t; #endif - #ifdef __unix__ + #if defined(__unix__) #define MA_UNIX - #ifdef __ORBIS__ - #define MA_ORBIS - #elif defined(__PROSPERO__) - #define MA_PROSPERO - #elif defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) - #define MA_BSD - #endif #endif - #ifdef __linux__ + #if defined(__linux__) #define MA_LINUX #endif - #ifdef __APPLE__ + #if defined(__APPLE__) #define MA_APPLE #endif - #ifdef __ANDROID__ + #if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) + #define MA_BSD + #endif + #if defined(__ANDROID__) #define MA_ANDROID #endif - #ifdef __EMSCRIPTEN__ + #if defined(__EMSCRIPTEN__) #define MA_EMSCRIPTEN #endif + #if defined(__ORBIS__) + #define MA_ORBIS + #endif + #if defined(__PROSPERO__) + #define MA_PROSPERO + #endif #if defined(__NX__) #define MA_NX #endif + #if defined(__BEOS__) || defined(__HAIKU__) + #define MA_BEOS + #endif + #if defined(__HAIKU__) + #define MA_HAIKU + #endif #endif #if defined(__has_c_attribute) @@ -3927,6 +3972,21 @@ typedef ma_uint16 wchar_t; /* SIMD alignment in bytes. Currently set to 32 bytes in preparation for future AVX optimizations. */ #define MA_SIMD_ALIGNMENT 32 +/* +Special wchar_t type to ensure any structures in the public sections that reference it have a +consistent size across all platforms. + +On Windows, wchar_t is 2 bytes, whereas everywhere else it's 4 bytes. Since Windows likes to use +wchar_t for it's IDs, we need a special explicitly sized wchar type that is always 2 bytes on all +platforms. +*/ +#if !defined(MA_POSIX) && defined(MA_WIN32) +typedef wchar_t ma_wchar_win32; +#else +typedef ma_uint16 ma_wchar_win32; +#endif + + /* Logging Levels @@ -3973,7 +4033,7 @@ versions of Visual Studio, which I've confirmed with at least VC6. */ #if !defined(_MSC_VER) && defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) #include - #define MA_ATOMIC(alignment, type) alignas(alignment) type + #define MA_ATOMIC(alignment, type) _Alignas(alignment) type #else #if defined(__GNUC__) /* GCC-style compilers. */ @@ -4304,61 +4364,57 @@ MA_ATOMIC_SAFE_TYPE_DECL(32, 4, bool32) typedef ma_uint32 ma_spinlock; #ifndef MA_NO_THREADING -/* Thread priorities should be ordered such that the default priority of the worker thread is 0. */ -typedef enum -{ - ma_thread_priority_idle = -5, - ma_thread_priority_lowest = -4, - ma_thread_priority_low = -3, - ma_thread_priority_normal = -2, - ma_thread_priority_high = -1, - ma_thread_priority_highest = 0, - ma_thread_priority_realtime = 1, - ma_thread_priority_default = 0 -} ma_thread_priority; + /* Thread priorities should be ordered such that the default priority of the worker thread is 0. */ + typedef enum + { + ma_thread_priority_idle = -5, + ma_thread_priority_lowest = -4, + ma_thread_priority_low = -3, + ma_thread_priority_normal = -2, + ma_thread_priority_high = -1, + ma_thread_priority_highest = 0, + ma_thread_priority_realtime = 1, + ma_thread_priority_default = 0 + } ma_thread_priority; -#if defined(MA_WIN32) -typedef ma_handle ma_thread; -#endif -#if defined(MA_POSIX) -typedef ma_pthread_t ma_thread; -#endif + #if defined(MA_POSIX) + typedef ma_pthread_t ma_thread; + #elif defined(MA_WIN32) + typedef ma_handle ma_thread; + #endif -#if defined(MA_WIN32) -typedef ma_handle ma_mutex; -#endif -#if defined(MA_POSIX) -typedef ma_pthread_mutex_t ma_mutex; -#endif + #if defined(MA_POSIX) + typedef ma_pthread_mutex_t ma_mutex; + #elif defined(MA_WIN32) + typedef ma_handle ma_mutex; + #endif -#if defined(MA_WIN32) -typedef ma_handle ma_event; -#endif -#if defined(MA_POSIX) -typedef struct -{ - ma_uint32 value; - ma_pthread_mutex_t lock; - ma_pthread_cond_t cond; -} ma_event; -#endif /* MA_POSIX */ + #if defined(MA_POSIX) + typedef struct + { + ma_uint32 value; + ma_pthread_mutex_t lock; + ma_pthread_cond_t cond; + } ma_event; + #elif defined(MA_WIN32) + typedef ma_handle ma_event; + #endif -#if defined(MA_WIN32) -typedef ma_handle ma_semaphore; -#endif -#if defined(MA_POSIX) -typedef struct -{ - int value; - ma_pthread_mutex_t lock; - ma_pthread_cond_t cond; -} ma_semaphore; -#endif /* MA_POSIX */ + #if defined(MA_POSIX) + typedef struct + { + int value; + ma_pthread_mutex_t lock; + ma_pthread_cond_t cond; + } ma_semaphore; + #elif defined(MA_WIN32) + typedef ma_handle ma_semaphore; + #endif #else -/* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ -#ifndef MA_NO_DEVICE_IO -#error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; -#endif + /* MA_NO_THREADING is set which means threading is disabled. Threading is required by some API families. If any of these are enabled we need to throw an error. */ + #ifndef MA_NO_DEVICE_IO + #error "MA_NO_THREADING cannot be used without MA_NO_DEVICE_IO"; + #endif #endif /* MA_NO_THREADING */ @@ -5096,6 +5152,7 @@ typedef struct float coneOuterGain; float dopplerFactor; /* Set to 0 to disable doppler effect. */ float directionalAttenuationFactor; /* Set to 0 to disable directional attenuation. */ + float minSpatializationChannelGain; /* The minimal scaling factor to apply to channel gains when accounting for the direction of the sound relative to the listener. Must be in the range of 0..1. Smaller values means more aggressive directional panning, larger values means more subtle directional panning. */ ma_uint32 gainSmoothTimeInFrames; /* When the gain of a channel changes during spatialization, the transition will be linearly interpolated over this number of frames. */ } ma_spatializer_config; @@ -5125,6 +5182,7 @@ typedef struct ma_atomic_vec3f direction; ma_atomic_vec3f velocity; /* For doppler effect. */ float dopplerPitch; /* Will be updated by ma_spatializer_process_pcm_frames() and can be used by higher level functions to apply a pitch shift for doppler effect. */ + float minSpatializationChannelGain; ma_gainer gainer; /* For smooth gain transitions. */ float* pNewChannelGainsOut; /* An offset of _pHeap. Used by ma_spatializer_process_pcm_frames() to store new channel gains. The number of elements in this array is equal to config.channelsOut. */ @@ -5685,6 +5743,197 @@ MA_API ma_uint64 ma_convert_frames(void* pOut, ma_uint64 frameCountOut, ma_forma MA_API ma_uint64 ma_convert_frames_ex(void* pOut, ma_uint64 frameCountOut, const void* pIn, ma_uint64 frameCountIn, const ma_data_converter_config* pConfig); +/************************************************************************************************************************************************************ + +Data Source + +************************************************************************************************************************************************************/ +typedef void ma_data_source; + +#define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001 + +typedef struct +{ + ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); + ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex); + ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); + ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor); + ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength); + ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping); + ma_uint32 flags; +} ma_data_source_vtable; + +typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource); + +typedef struct +{ + const ma_data_source_vtable* vtable; +} ma_data_source_config; + +MA_API ma_data_source_config ma_data_source_config_init(void); + + +typedef struct +{ + const ma_data_source_vtable* vtable; + ma_uint64 rangeBegInFrames; + ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */ + ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */ + ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */ + ma_data_source* pCurrent; /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */ + ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */ + ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */ + MA_ATOMIC(4, ma_bool32) isLooping; +} ma_data_source_base; + +MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource); +MA_API void ma_data_source_uninit(ma_data_source* pDataSource); +MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ +MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */ +MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex); +MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); +MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor); +MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */ +MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor); +MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength); +MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping); +MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource); +MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames); +MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames); +MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames); +MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames); +MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource); +MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource); +MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource); +MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource); +MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext); +MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource); + + +typedef struct +{ + ma_data_source_base ds; + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint64 cursor; + ma_uint64 sizeInFrames; + const void* pData; +} ma_audio_buffer_ref; + +MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef); +MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef); +MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames); +MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); +MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex); +MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount); +MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ +MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef); +MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor); +MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength); +MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames); + + + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_uint32 sampleRate; + ma_uint64 sizeInFrames; + const void* pData; /* If set to NULL, will allocate a block of memory for you. */ + ma_allocation_callbacks allocationCallbacks; +} ma_audio_buffer_config; + +MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks); + +typedef struct +{ + ma_audio_buffer_ref ref; + ma_allocation_callbacks allocationCallbacks; + ma_bool32 ownsData; /* Used to control whether or not miniaudio owns the data buffer. If set to true, pData will be freed in ma_audio_buffer_uninit(). */ + ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */ +} ma_audio_buffer; + +MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); +MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); +MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer); /* Always copies the data. Doesn't make sense to use this otherwise. Use ma_audio_buffer_uninit_and_free() to uninit. */ +MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer); +MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer); +MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); +MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex); +MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount); +MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ +MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer); +MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor); +MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength); +MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames); + + +/* +Paged Audio Buffer +================== +A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It +can be used for cases where audio data is streamed in asynchronously while allowing data to be read +at the same time. + +This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across +simultaneously across different threads, however only one thread at a time can append, and only one +thread at a time can read and seek. +*/ +typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page; +struct ma_paged_audio_buffer_page +{ + MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext; + ma_uint64 sizeInFrames; + ma_uint8 pAudioData[1]; +}; + +typedef struct +{ + ma_format format; + ma_uint32 channels; + ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */ + MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */ +} ma_paged_audio_buffer_data; + +MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData); +MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData); +MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData); +MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength); +MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage); +MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks); +MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage); +MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks); + + +typedef struct +{ + ma_paged_audio_buffer_data* pData; /* Must not be null. */ +} ma_paged_audio_buffer_config; + +MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData); + + +typedef struct +{ + ma_data_source_base ds; + ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */ + ma_paged_audio_buffer_page* pCurrent; + ma_uint64 relativeCursor; /* Relative to the current page. */ + ma_uint64 absoluteCursor; +} ma_paged_audio_buffer; + +MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer); +MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer); +MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Returns MA_AT_END if no more pages available. */ +MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex); +MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor); +MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength); + + + /************************************************************************************************************************************************************ Ring Buffer @@ -5724,9 +5973,11 @@ MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pB typedef struct { + ma_data_source_base ds; ma_rb rb; ma_format format; ma_uint32 channels; + ma_uint32 sampleRate; /* Not required for the ring buffer itself, but useful for associating the data with some sample rate, particularly for data sources. */ } ma_pcm_rb; MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint32 subbufferSizeInFrames, ma_uint32 subbufferCount, ma_uint32 subbufferStrideInFrames, void* pOptionalPreallocatedBuffer, const ma_allocation_callbacks* pAllocationCallbacks, ma_pcm_rb* pRB); @@ -5746,6 +5997,10 @@ MA_API ma_uint32 ma_pcm_rb_get_subbuffer_size(ma_pcm_rb* pRB); MA_API ma_uint32 ma_pcm_rb_get_subbuffer_stride(ma_pcm_rb* pRB); MA_API ma_uint32 ma_pcm_rb_get_subbuffer_offset(ma_pcm_rb* pRB, ma_uint32 subbufferIndex); MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferIndex, void* pBuffer); +MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB); +MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB); +MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB); +MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate); /* @@ -6256,15 +6511,20 @@ This section contains the APIs for device playback and capture. Here is where yo /* Some backends are only supported on certain platforms. */ #if defined(MA_WIN32) #define MA_SUPPORT_WASAPI - #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */ + + #if defined(MA_WIN32_DESKTOP) /* DirectSound and WinMM backends are only supported on desktops. */ #define MA_SUPPORT_DSOUND #define MA_SUPPORT_WINMM - #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */ + + /* Don't enable JACK here if compiling with Cosmopolitan. It'll be enabled in the Linux section below. */ + #if !defined(__COSMOPOLITAN__) + #define MA_SUPPORT_JACK /* JACK is technically supported on Windows, but I don't know how many people use it in practice... */ + #endif #endif #endif #if defined(MA_UNIX) && !defined(MA_ORBIS) && !defined(MA_PROSPERO) #if defined(MA_LINUX) - #if !defined(MA_ANDROID) /* ALSA is not supported on Android. */ + #if !defined(MA_ANDROID) && !defined(__COSMOPOLITAN__) /* ALSA is not supported on Android. */ #define MA_SUPPORT_ALSA #endif #endif @@ -6697,7 +6957,7 @@ typedef union typedef union { - wchar_t wasapi[64]; /* WASAPI uses a wchar_t string for identification. */ + ma_wchar_win32 wasapi[64]; /* WASAPI uses a wchar_t string for identification. */ ma_uint8 dsound[16]; /* DirectSound uses a GUID for identification. */ /*UINT_PTR*/ ma_uint32 winmm; /* When creating a device, WinMM expects a Win32 UINT_PTR for device identification. In practice it's actually just a UINT. */ char alsa[256]; /* ALSA uses a name string for identification. */ @@ -7040,7 +7300,7 @@ struct ma_context ma_uint32 commandCount; ma_context_command__wasapi commands[4]; ma_handle hAvrt; - ma_proc AvSetMmThreadCharacteristicsW; + ma_proc AvSetMmThreadCharacteristicsA; ma_proc AvRevertMmThreadcharacteristics; ma_handle hMMDevapi; ma_proc ActivateAudioInterfaceAsync; @@ -7384,7 +7644,7 @@ struct ma_context union { -#ifdef MA_WIN32 +#if defined(MA_WIN32) struct { /*HMODULE*/ ma_handle hOle32DLL; @@ -9501,195 +9761,6 @@ This will run on an optimized path when the volume is equal to 1. MA_API ma_result ma_mix_pcm_frames_f32(float* pDst, const float* pSrc, ma_uint64 frameCount, ma_uint32 channels, float volume); -/************************************************************************************************** - -Data Source - -**************************************************************************************************/ -typedef void ma_data_source; - -#define MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT 0x00000001 - -typedef struct -{ - ma_result (* onRead)(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); - ma_result (* onSeek)(ma_data_source* pDataSource, ma_uint64 frameIndex); - ma_result (* onGetDataFormat)(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); - ma_result (* onGetCursor)(ma_data_source* pDataSource, ma_uint64* pCursor); - ma_result (* onGetLength)(ma_data_source* pDataSource, ma_uint64* pLength); - ma_result (* onSetLooping)(ma_data_source* pDataSource, ma_bool32 isLooping); - ma_uint32 flags; -} ma_data_source_vtable; - -typedef ma_data_source* (* ma_data_source_get_next_proc)(ma_data_source* pDataSource); - -typedef struct -{ - const ma_data_source_vtable* vtable; -} ma_data_source_config; - -MA_API ma_data_source_config ma_data_source_config_init(void); - - -typedef struct -{ - const ma_data_source_vtable* vtable; - ma_uint64 rangeBegInFrames; - ma_uint64 rangeEndInFrames; /* Set to -1 for unranged (default). */ - ma_uint64 loopBegInFrames; /* Relative to rangeBegInFrames. */ - ma_uint64 loopEndInFrames; /* Relative to rangeBegInFrames. Set to -1 for the end of the range. */ - ma_data_source* pCurrent; /* When non-NULL, the data source being initialized will act as a proxy and will route all operations to pCurrent. Used in conjunction with pNext/onGetNext for seamless chaining. */ - ma_data_source* pNext; /* When set to NULL, onGetNext will be used. */ - ma_data_source_get_next_proc onGetNext; /* Will be used when pNext is NULL. If both are NULL, no next will be used. */ - MA_ATOMIC(4, ma_bool32) isLooping; -} ma_data_source_base; - -MA_API ma_result ma_data_source_init(const ma_data_source_config* pConfig, ma_data_source* pDataSource); -MA_API void ma_data_source_uninit(ma_data_source* pDataSource); -MA_API ma_result ma_data_source_read_pcm_frames(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Must support pFramesOut = NULL in which case a forward seek should be performed. */ -MA_API ma_result ma_data_source_seek_pcm_frames(ma_data_source* pDataSource, ma_uint64 frameCount, ma_uint64* pFramesSeeked); /* Can only seek forward. Equivalent to ma_data_source_read_pcm_frames(pDataSource, NULL, frameCount, &framesRead); */ -MA_API ma_result ma_data_source_seek_to_pcm_frame(ma_data_source* pDataSource, ma_uint64 frameIndex); -MA_API ma_result ma_data_source_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap); -MA_API ma_result ma_data_source_get_cursor_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pCursor); -MA_API ma_result ma_data_source_get_length_in_pcm_frames(ma_data_source* pDataSource, ma_uint64* pLength); /* Returns MA_NOT_IMPLEMENTED if the length is unknown or cannot be determined. Decoders can return this. */ -MA_API ma_result ma_data_source_get_cursor_in_seconds(ma_data_source* pDataSource, float* pCursor); -MA_API ma_result ma_data_source_get_length_in_seconds(ma_data_source* pDataSource, float* pLength); -MA_API ma_result ma_data_source_set_looping(ma_data_source* pDataSource, ma_bool32 isLooping); -MA_API ma_bool32 ma_data_source_is_looping(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_range_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 rangeBegInFrames, ma_uint64 rangeEndInFrames); -MA_API void ma_data_source_get_range_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pRangeBegInFrames, ma_uint64* pRangeEndInFrames); -MA_API ma_result ma_data_source_set_loop_point_in_pcm_frames(ma_data_source* pDataSource, ma_uint64 loopBegInFrames, ma_uint64 loopEndInFrames); -MA_API void ma_data_source_get_loop_point_in_pcm_frames(const ma_data_source* pDataSource, ma_uint64* pLoopBegInFrames, ma_uint64* pLoopEndInFrames); -MA_API ma_result ma_data_source_set_current(ma_data_source* pDataSource, ma_data_source* pCurrentDataSource); -MA_API ma_data_source* ma_data_source_get_current(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_next(ma_data_source* pDataSource, ma_data_source* pNextDataSource); -MA_API ma_data_source* ma_data_source_get_next(const ma_data_source* pDataSource); -MA_API ma_result ma_data_source_set_next_callback(ma_data_source* pDataSource, ma_data_source_get_next_proc onGetNext); -MA_API ma_data_source_get_next_proc ma_data_source_get_next_callback(const ma_data_source* pDataSource); - - -typedef struct -{ - ma_data_source_base ds; - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 cursor; - ma_uint64 sizeInFrames; - const void* pData; -} ma_audio_buffer_ref; - -MA_API ma_result ma_audio_buffer_ref_init(ma_format format, ma_uint32 channels, const void* pData, ma_uint64 sizeInFrames, ma_audio_buffer_ref* pAudioBufferRef); -MA_API void ma_audio_buffer_ref_uninit(ma_audio_buffer_ref* pAudioBufferRef); -MA_API ma_result ma_audio_buffer_ref_set_data(ma_audio_buffer_ref* pAudioBufferRef, const void* pData, ma_uint64 sizeInFrames); -MA_API ma_uint64 ma_audio_buffer_ref_read_pcm_frames(ma_audio_buffer_ref* pAudioBufferRef, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); -MA_API ma_result ma_audio_buffer_ref_seek_to_pcm_frame(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameIndex); -MA_API ma_result ma_audio_buffer_ref_map(ma_audio_buffer_ref* pAudioBufferRef, void** ppFramesOut, ma_uint64* pFrameCount); -MA_API ma_result ma_audio_buffer_ref_unmap(ma_audio_buffer_ref* pAudioBufferRef, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ -MA_API ma_bool32 ma_audio_buffer_ref_at_end(const ma_audio_buffer_ref* pAudioBufferRef); -MA_API ma_result ma_audio_buffer_ref_get_cursor_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pCursor); -MA_API ma_result ma_audio_buffer_ref_get_length_in_pcm_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pLength); -MA_API ma_result ma_audio_buffer_ref_get_available_frames(const ma_audio_buffer_ref* pAudioBufferRef, ma_uint64* pAvailableFrames); - - - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_uint32 sampleRate; - ma_uint64 sizeInFrames; - const void* pData; /* If set to NULL, will allocate a block of memory for you. */ - ma_allocation_callbacks allocationCallbacks; -} ma_audio_buffer_config; - -MA_API ma_audio_buffer_config ma_audio_buffer_config_init(ma_format format, ma_uint32 channels, ma_uint64 sizeInFrames, const void* pData, const ma_allocation_callbacks* pAllocationCallbacks); - -typedef struct -{ - ma_audio_buffer_ref ref; - ma_allocation_callbacks allocationCallbacks; - ma_bool32 ownsData; /* Used to control whether or not miniaudio owns the data buffer. If set to true, pData will be freed in ma_audio_buffer_uninit(). */ - ma_uint8 _pExtraData[1]; /* For allocating a buffer with the memory located directly after the other memory of the structure. */ -} ma_audio_buffer; - -MA_API ma_result ma_audio_buffer_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_init_copy(const ma_audio_buffer_config* pConfig, ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_alloc_and_init(const ma_audio_buffer_config* pConfig, ma_audio_buffer** ppAudioBuffer); /* Always copies the data. Doesn't make sense to use this otherwise. Use ma_audio_buffer_uninit_and_free() to uninit. */ -MA_API void ma_audio_buffer_uninit(ma_audio_buffer* pAudioBuffer); -MA_API void ma_audio_buffer_uninit_and_free(ma_audio_buffer* pAudioBuffer); -MA_API ma_uint64 ma_audio_buffer_read_pcm_frames(ma_audio_buffer* pAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_bool32 loop); -MA_API ma_result ma_audio_buffer_seek_to_pcm_frame(ma_audio_buffer* pAudioBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_audio_buffer_map(ma_audio_buffer* pAudioBuffer, void** ppFramesOut, ma_uint64* pFrameCount); -MA_API ma_result ma_audio_buffer_unmap(ma_audio_buffer* pAudioBuffer, ma_uint64 frameCount); /* Returns MA_AT_END if the end has been reached. This should be considered successful. */ -MA_API ma_bool32 ma_audio_buffer_at_end(const ma_audio_buffer* pAudioBuffer); -MA_API ma_result ma_audio_buffer_get_cursor_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pCursor); -MA_API ma_result ma_audio_buffer_get_length_in_pcm_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pLength); -MA_API ma_result ma_audio_buffer_get_available_frames(const ma_audio_buffer* pAudioBuffer, ma_uint64* pAvailableFrames); - - -/* -Paged Audio Buffer -================== -A paged audio buffer is made up of a linked list of pages. It's expandable, but not shrinkable. It -can be used for cases where audio data is streamed in asynchronously while allowing data to be read -at the same time. - -This is lock-free, but not 100% thread safe. You can append a page and read from the buffer across -simultaneously across different threads, however only one thread at a time can append, and only one -thread at a time can read and seek. -*/ -typedef struct ma_paged_audio_buffer_page ma_paged_audio_buffer_page; -struct ma_paged_audio_buffer_page -{ - MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pNext; - ma_uint64 sizeInFrames; - ma_uint8 pAudioData[1]; -}; - -typedef struct -{ - ma_format format; - ma_uint32 channels; - ma_paged_audio_buffer_page head; /* Dummy head for the lock-free algorithm. Always has a size of 0. */ - MA_ATOMIC(MA_SIZEOF_PTR, ma_paged_audio_buffer_page*) pTail; /* Never null. Initially set to &head. */ -} ma_paged_audio_buffer_data; - -MA_API ma_result ma_paged_audio_buffer_data_init(ma_format format, ma_uint32 channels, ma_paged_audio_buffer_data* pData); -MA_API void ma_paged_audio_buffer_data_uninit(ma_paged_audio_buffer_data* pData, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_head(ma_paged_audio_buffer_data* pData); -MA_API ma_paged_audio_buffer_page* ma_paged_audio_buffer_data_get_tail(ma_paged_audio_buffer_data* pData); -MA_API ma_result ma_paged_audio_buffer_data_get_length_in_pcm_frames(ma_paged_audio_buffer_data* pData, ma_uint64* pLength); -MA_API ma_result ma_paged_audio_buffer_data_allocate_page(ma_paged_audio_buffer_data* pData, ma_uint64 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks, ma_paged_audio_buffer_page** ppPage); -MA_API ma_result ma_paged_audio_buffer_data_free_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage, const ma_allocation_callbacks* pAllocationCallbacks); -MA_API ma_result ma_paged_audio_buffer_data_append_page(ma_paged_audio_buffer_data* pData, ma_paged_audio_buffer_page* pPage); -MA_API ma_result ma_paged_audio_buffer_data_allocate_and_append_page(ma_paged_audio_buffer_data* pData, ma_uint32 pageSizeInFrames, const void* pInitialData, const ma_allocation_callbacks* pAllocationCallbacks); - - -typedef struct -{ - ma_paged_audio_buffer_data* pData; /* Must not be null. */ -} ma_paged_audio_buffer_config; - -MA_API ma_paged_audio_buffer_config ma_paged_audio_buffer_config_init(ma_paged_audio_buffer_data* pData); - - -typedef struct -{ - ma_data_source_base ds; - ma_paged_audio_buffer_data* pData; /* Audio data is read from here. Cannot be null. */ - ma_paged_audio_buffer_page* pCurrent; - ma_uint64 relativeCursor; /* Relative to the current page. */ - ma_uint64 absoluteCursor; -} ma_paged_audio_buffer; - -MA_API ma_result ma_paged_audio_buffer_init(const ma_paged_audio_buffer_config* pConfig, ma_paged_audio_buffer* pPagedAudioBuffer); -MA_API void ma_paged_audio_buffer_uninit(ma_paged_audio_buffer* pPagedAudioBuffer); -MA_API ma_result ma_paged_audio_buffer_read_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead); /* Returns MA_AT_END if no more pages available. */ -MA_API ma_result ma_paged_audio_buffer_seek_to_pcm_frame(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64 frameIndex); -MA_API ma_result ma_paged_audio_buffer_get_cursor_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pCursor); -MA_API ma_result ma_paged_audio_buffer_get_length_in_pcm_frames(ma_paged_audio_buffer* pPagedAudioBuffer, ma_uint64* pLength); - /************************************************************************************************************************************************************ @@ -10314,6 +10385,7 @@ typedef struct ma_uint32 decodedChannels; /* The decoded channel count to use. Set to 0 (default) to use the file's native channel count. */ ma_uint32 decodedSampleRate; /* the decoded sample rate to use. Set to 0 (default) to use the file's native sample rate. */ ma_uint32 jobThreadCount; /* Set to 0 if you want to self-manage your job threads. Defaults to 1. */ + size_t jobThreadStackSize; ma_uint32 jobQueueCapacity; /* The maximum number of jobs that can fit in the queue at a time. Defaults to MA_JOB_TYPE_RESOURCE_MANAGER_QUEUE_CAPACITY. Cannot be zero. */ ma_uint32 flags; ma_vfs* pVFS; /* Can be NULL in which case defaults will be used. */ @@ -10900,13 +10972,17 @@ typedef struct ma_sound ma_sound; /* Sound flags. */ typedef enum { + /* Resource manager flags. */ MA_SOUND_FLAG_STREAM = 0x00000001, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_STREAM */ MA_SOUND_FLAG_DECODE = 0x00000002, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_DECODE */ MA_SOUND_FLAG_ASYNC = 0x00000004, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_ASYNC */ MA_SOUND_FLAG_WAIT_INIT = 0x00000008, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT */ - MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00000010, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ - MA_SOUND_FLAG_NO_PITCH = 0x00000020, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */ - MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00000040 /* Disable spatialization. */ + MA_SOUND_FLAG_UNKNOWN_LENGTH = 0x00000010, /* MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH */ + + /* ma_sound specific flags. */ + MA_SOUND_FLAG_NO_DEFAULT_ATTACHMENT = 0x00001000, /* Do not attach to the endpoint by default. Useful for when setting up nodes in a complex graph system. */ + MA_SOUND_FLAG_NO_PITCH = 0x00002000, /* Disable pitch shifting with ma_sound_set_pitch() and ma_sound_group_set_pitch(). This is an optimization. */ + MA_SOUND_FLAG_NO_SPATIALIZATION = 0x00004000 /* Disable spatialization. */ } ma_sound_flags; #ifndef MA_ENGINE_MAX_LISTENERS @@ -10928,6 +11004,7 @@ typedef struct ma_uint32 channelsIn; ma_uint32 channelsOut; ma_uint32 sampleRate; /* Only used when the type is set to ma_engine_node_type_sound. */ + ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */ ma_mono_expansion_mode monoExpansionMode; ma_bool8 isPitchDisabled; /* Pitching can be explicitly disabled with MA_SOUND_FLAG_NO_PITCH to optimize processing. */ ma_bool8 isSpatializationDisabled; /* Spatialization can be explicitly disabled with MA_SOUND_FLAG_NO_SPATIALIZATION. */ @@ -10943,11 +11020,14 @@ typedef struct ma_node_base baseNode; /* Must be the first member for compatiblity with the ma_node API. */ ma_engine* pEngine; /* A pointer to the engine. Set based on the value from the config. */ ma_uint32 sampleRate; /* The sample rate of the input data. For sounds backed by a data source, this will be the data source's sample rate. Otherwise it'll be the engine's sample rate. */ + ma_uint32 volumeSmoothTimeInPCMFrames; ma_mono_expansion_mode monoExpansionMode; ma_fader fader; ma_linear_resampler resampler; /* For pitch shift. */ ma_spatializer spatializer; ma_panner panner; + ma_gainer volumeGainer; /* This will only be used if volumeSmoothTimeInPCMFrames is > 0. */ + ma_atomic_float volume; /* Defaults to 1. */ MA_ATOMIC(4, float) pitch; float oldPitch; /* For determining whether or not the resampler needs to be updated to reflect the new pitch. The resampler will be updated on the mixing thread. */ float oldDopplerPitch; /* For determining whether or not the resampler needs to be updated to take a new doppler pitch into account. */ @@ -10968,6 +11048,9 @@ MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocati #define MA_SOUND_SOURCE_CHANNEL_COUNT 0xFFFFFFFF +/* Callback for when a sound reaches the end. */ +typedef void (* ma_sound_end_proc)(void* pUserData, ma_sound* pSound); + typedef struct { const char* pFilePath; /* Set this to load from the resource manager. */ @@ -10979,13 +11062,18 @@ typedef struct ma_uint32 channelsOut; /* Set this to 0 (default) to use the engine's channel count. Set to MA_SOUND_SOURCE_CHANNEL_COUNT to use the data source's channel count (only used if using a data source as input). */ ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ ma_uint32 flags; /* A combination of MA_SOUND_FLAG_* flags. */ + ma_uint32 volumeSmoothTimeInPCMFrames; /* The number of frames to smooth over volume changes. Defaults to 0 in which case no smoothing is used. */ ma_uint64 initialSeekPointInPCMFrames; /* Initializes the sound such that it's seeked to this location by default. */ ma_uint64 rangeBegInPCMFrames; ma_uint64 rangeEndInPCMFrames; ma_uint64 loopPointBegInPCMFrames; ma_uint64 loopPointEndInPCMFrames; ma_bool32 isLooping; + ma_sound_end_proc endCallback; /* Fired when the sound reaches the end. Will be fired from the audio thread. Do not restart, uninitialize or otherwise change the state of the sound from here. Instead fire an event or set a variable to indicate to a different thread to change the start of the sound. Will not be fired in response to a scheduled stop with ma_sound_set_stop_time_*(). */ + void* pEndCallbackUserData; +#ifndef MA_NO_RESOURCE_MANAGER ma_resource_manager_pipeline_notifications initNotifications; +#endif ma_fence* pDoneFence; /* Deprecated. Use initNotifications instead. Released when the resource manager has finished decoding the entire sound. Not used with streams. */ } ma_sound_config; @@ -10998,6 +11086,8 @@ struct ma_sound ma_data_source* pDataSource; MA_ATOMIC(8, ma_uint64) seekTarget; /* The PCM frame index to seek to in the mixing thread. Set to (~(ma_uint64)0) to not perform any seeking. */ MA_ATOMIC(4, ma_bool32) atEnd; + ma_sound_end_proc endCallback; + void* pEndCallbackUserData; ma_bool8 ownsDataSource; /* @@ -11028,27 +11118,28 @@ MA_API ma_sound_group_config ma_sound_group_config_init_2(ma_engine* pEngine); typedef struct { #if !defined(MA_NO_RESOURCE_MANAGER) - ma_resource_manager* pResourceManager; /* Can be null in which case a resource manager will be created for you. */ + ma_resource_manager* pResourceManager; /* Can be null in which case a resource manager will be created for you. */ #endif #if !defined(MA_NO_DEVICE_IO) ma_context* pContext; - ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */ - ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */ + ma_device* pDevice; /* If set, the caller is responsible for calling ma_engine_data_callback() in the device's data callback. */ + ma_device_id* pPlaybackDeviceID; /* The ID of the playback device to use with the default listener. */ ma_device_notification_proc notificationCallback; #endif - ma_log* pLog; /* When set to NULL, will use the context's log. */ - ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */ - ma_uint32 channels; /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */ - ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */ - ma_uint32 periodSizeInFrames; /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/ - ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */ - ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */ - ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */ + ma_log* pLog; /* When set to NULL, will use the context's log. */ + ma_uint32 listenerCount; /* Must be between 1 and MA_ENGINE_MAX_LISTENERS. */ + ma_uint32 channels; /* The number of channels to use when mixing and spatializing. When set to 0, will use the native channel count of the device. */ + ma_uint32 sampleRate; /* The sample rate. When set to 0 will use the native channel count of the device. */ + ma_uint32 periodSizeInFrames; /* If set to something other than 0, updates will always be exactly this size. The underlying device may be a different size, but from the perspective of the mixer that won't matter.*/ + ma_uint32 periodSizeInMilliseconds; /* Used if periodSizeInFrames is unset. */ + ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. If set to 0, will use gainSmoothTimeInMilliseconds. */ + ma_uint32 gainSmoothTimeInMilliseconds; /* When set to 0, gainSmoothTimeInFrames will be used. If both are set to 0, a default value will be used. */ + ma_uint32 defaultVolumeSmoothTimeInPCMFrames; /* Defaults to 0. Controls the default amount of smoothing to apply to volume changes to sounds. High values means more smoothing at the expense of high latency (will take longer to reach the new volume). */ ma_allocation_callbacks allocationCallbacks; - ma_bool32 noAutoStart; /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */ - ma_bool32 noDevice; /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */ - ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ - ma_vfs* pResourceManagerVFS; /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */ + ma_bool32 noAutoStart; /* When set to true, requires an explicit call to ma_engine_start(). This is false by default, meaning the engine will be started automatically in ma_engine_init(). */ + ma_bool32 noDevice; /* When set to true, don't create a default device. ma_engine_read_pcm_frames() can be called manually to read data. */ + ma_mono_expansion_mode monoExpansionMode; /* Controls how the mono channel should be expanded to other channels when spatialization is disabled on a sound. */ + ma_vfs* pResourceManagerVFS; /* A pointer to a pre-allocated VFS object to use with the resource manager. This is ignored if pResourceManager is not NULL. */ } ma_engine_config; MA_API ma_engine_config ma_engine_config_init(void); @@ -11074,6 +11165,7 @@ struct ma_engine ma_sound_inlined* pInlinedSoundHead; /* The first inlined sound. Inlined sounds are tracked in a linked list. */ MA_ATOMIC(4, ma_uint32) inlinedSoundCount; /* The total number of allocated inlined sound objects. Used for debugging. */ ma_uint32 gainSmoothTimeInFrames; /* The number of frames to interpolate the gain of spatialized sounds across. */ + ma_uint32 defaultVolumeSmoothTimeInPCMFrames; ma_mono_expansion_mode monoExpansionMode; }; @@ -11087,8 +11179,12 @@ MA_API ma_resource_manager* ma_engine_get_resource_manager(ma_engine* pEngine); MA_API ma_device* ma_engine_get_device(ma_engine* pEngine); MA_API ma_log* ma_engine_get_log(ma_engine* pEngine); MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine); -MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine); -MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime); +MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine); +MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine); +MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime); +MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime); +MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine); /* Deprecated. Use ma_engine_get_time_in_pcm_frames(). Will be removed in version 0.12. */ +MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime); /* Deprecated. Use ma_engine_set_time_in_pcm_frames(). Will be removed in version 0.12. */ MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine); MA_API ma_uint32 ma_engine_get_sample_rate(const ma_engine* pEngine); @@ -11187,6 +11283,7 @@ MA_API ma_result ma_sound_get_cursor_in_pcm_frames(ma_sound* pSound, ma_uint64* MA_API ma_result ma_sound_get_length_in_pcm_frames(ma_sound* pSound, ma_uint64* pLength); MA_API ma_result ma_sound_get_cursor_in_seconds(ma_sound* pSound, float* pCursor); MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength); +MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData); MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup); MA_API ma_result ma_sound_group_init_ex(ma_engine* pEngine, const ma_sound_group_config* pConfig, ma_sound_group* pGroup); @@ -11271,8 +11368,10 @@ IMPLEMENTATION #define miniaudio_c #include -#include /* For INT_MAX */ -#include /* sin(), etc. */ +#include /* For INT_MAX */ +#include /* sin(), etc. */ +#include /* For malloc(), free(), wcstombs(). */ +#include /* For memset() */ #include #include @@ -11284,18 +11383,33 @@ IMPLEMENTATION #include /* For _controlfp_s constants */ #endif -#ifdef MA_WIN32 -#include -#else -#include /* For malloc(), free(), wcstombs(). */ -#include /* For memset() */ +#if defined(MA_WIN32) + #include + + /* + There's a possibility that WIN32_LEAN_AND_MEAN has been defined which will exclude some symbols + such as STGM_READ and CLSCTL_ALL. We need to check these and define them ourselves if they're + unavailable. + */ + #ifndef STGM_READ + #define STGM_READ 0x00000000L + #endif + #ifndef CLSCTX_ALL + #define CLSCTX_ALL 23 + #endif + + /* IUnknown is used by both the WASAPI and DirectSound backends. It easier to just declare our version here. */ + typedef struct ma_IUnknown ma_IUnknown; +#endif + +#if !defined(MA_WIN32) #include #include /* select() (used for ma_sleep()). */ #include #endif #ifdef MA_NX -#include /* For nanosleep() */ +#include /* For nanosleep() */ #endif #include /* For fstat(), etc. */ @@ -11304,6 +11418,7 @@ IMPLEMENTATION #include #endif + #if !defined(MA_64BIT) && !defined(MA_32BIT) #ifdef _WIN32 #ifdef _WIN64 @@ -11343,7 +11458,7 @@ IMPLEMENTATION #endif /* Intrinsics Support */ -#if defined(MA_X64) || defined(MA_X86) +#if (defined(MA_X64) || defined(MA_X86)) && !defined(__COSMOPOLITAN__) #if defined(_MSC_VER) && !defined(__clang__) /* MSVC. */ #if _MSC_VER >= 1400 && !defined(MA_NO_SSE2) /* 2005 */ @@ -11718,7 +11833,7 @@ static MA_INLINE void ma_sleep(ma_uint32 milliseconds) } #endif -static MA_INLINE void ma_yield() +static MA_INLINE void ma_yield(void) { #if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) /* x86/x64 */ @@ -11753,7 +11868,7 @@ static MA_INLINE void ma_yield() #define MA_MM_DENORMALS_ZERO_MASK 0x0040 #define MA_MM_FLUSH_ZERO_MASK 0x8000 -static MA_INLINE unsigned int ma_disable_denormals() +static MA_INLINE unsigned int ma_disable_denormals(void) { unsigned int prevState; @@ -11780,7 +11895,7 @@ static MA_INLINE unsigned int ma_disable_denormals() } #elif defined(MA_X86) || defined(MA_X64) { - #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ + #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ { prevState = _mm_getcsr(); _mm_setcsr(prevState | MA_MM_DENORMALS_ZERO_MASK | MA_MM_FLUSH_ZERO_MASK); @@ -11820,7 +11935,7 @@ static MA_INLINE void ma_restore_denormals(unsigned int prevState) } #elif defined(MA_X86) || defined(MA_X64) { - #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ + #if defined(__SSE2__) && !(defined(__TINYC__) || defined(__WATCOMC__) || defined(__COSMOPOLITAN__)) /* <-- Add compilers that lack support for _mm_getcsr() and _mm_setcsr() to this list. */ { _mm_setcsr(prevState); } @@ -12008,35 +12123,17 @@ Standard Library Stuff ******************************************************************************/ #ifndef MA_ASSERT -#ifdef MA_WIN32 -#define MA_ASSERT(condition) assert(condition) -#else -#define MA_ASSERT(condition) assert(condition) -#endif +#define MA_ASSERT(condition) assert(condition) #endif #ifndef MA_MALLOC -#ifdef MA_WIN32 -#define MA_MALLOC(sz) HeapAlloc(GetProcessHeap(), 0, (sz)) -#else -#define MA_MALLOC(sz) malloc((sz)) +#define MA_MALLOC(sz) malloc((sz)) #endif -#endif - #ifndef MA_REALLOC -#ifdef MA_WIN32 -#define MA_REALLOC(p, sz) (((sz) > 0) ? ((p) ? HeapReAlloc(GetProcessHeap(), 0, (p), (sz)) : HeapAlloc(GetProcessHeap(), 0, (sz))) : ((VOID*)(size_t)(HeapFree(GetProcessHeap(), 0, (p)) & 0))) -#else -#define MA_REALLOC(p, sz) realloc((p), (sz)) +#define MA_REALLOC(p, sz) realloc((p), (sz)) #endif -#endif - #ifndef MA_FREE -#ifdef MA_WIN32 -#define MA_FREE(p) HeapFree(GetProcessHeap(), 0, (p)) -#else -#define MA_FREE(p) free((p)) -#endif +#define MA_FREE(p) free((p)) #endif static MA_INLINE void ma_zero_memory_default(void* p, size_t sz) @@ -12046,45 +12143,32 @@ static MA_INLINE void ma_zero_memory_default(void* p, size_t sz) return; } -#ifdef MA_WIN32 - ZeroMemory(p, sz); -#else if (sz > 0) { memset(p, 0, sz); } -#endif } + #ifndef MA_ZERO_MEMORY -#define MA_ZERO_MEMORY(p, sz) ma_zero_memory_default((p), (sz)) +#define MA_ZERO_MEMORY(p, sz) ma_zero_memory_default((p), (sz)) #endif - #ifndef MA_COPY_MEMORY -#ifdef MA_WIN32 -#define MA_COPY_MEMORY(dst, src, sz) CopyMemory((dst), (src), (sz)) -#else -#define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) +#define MA_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz)) #endif -#endif - #ifndef MA_MOVE_MEMORY -#ifdef MA_WIN32 -#define MA_MOVE_MEMORY(dst, src, sz) MoveMemory((dst), (src), (sz)) -#else -#define MA_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) -#endif +#define MA_MOVE_MEMORY(dst, src, sz) memmove((dst), (src), (sz)) #endif -#define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p))) +#define MA_ZERO_OBJECT(p) MA_ZERO_MEMORY((p), sizeof(*(p))) -#define ma_countof(x) (sizeof(x) / sizeof(x[0])) -#define ma_max(x, y) (((x) > (y)) ? (x) : (y)) -#define ma_min(x, y) (((x) < (y)) ? (x) : (y)) -#define ma_abs(x) (((x) > 0) ? (x) : -(x)) -#define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi))) -#define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) -#define ma_align(x, a) ((x + (a-1)) & ~(a-1)) -#define ma_align_64(x) ma_align(x, 8) +#define ma_countof(x) (sizeof(x) / sizeof(x[0])) +#define ma_max(x, y) (((x) > (y)) ? (x) : (y)) +#define ma_min(x, y) (((x) < (y)) ? (x) : (y)) +#define ma_abs(x) (((x) > 0) ? (x) : -(x)) +#define ma_clamp(x, lo, hi) (ma_max(lo, ma_min(x, hi))) +#define ma_offset_ptr(p, offset) (((ma_uint8*)(p)) + (offset)) +#define ma_align(x, a) ((x + (a-1)) & ~(a-1)) +#define ma_align_64(x) ma_align(x, 8) #define ma_buffer_frame_capacity(buffer, channels, format) (sizeof(buffer) / ma_get_bytes_per_sample(format) / (channels)) @@ -12535,406 +12619,408 @@ MA_API wchar_t* ma_copy_string_w(const wchar_t* src, const ma_allocation_callbac } + #include static ma_result ma_result_from_errno(int e) { - switch (e) - { - case 0: return MA_SUCCESS; - #ifdef EPERM - case EPERM: return MA_INVALID_OPERATION; - #endif - #ifdef ENOENT - case ENOENT: return MA_DOES_NOT_EXIST; - #endif - #ifdef ESRCH - case ESRCH: return MA_DOES_NOT_EXIST; - #endif - #ifdef EINTR - case EINTR: return MA_INTERRUPT; - #endif - #ifdef EIO - case EIO: return MA_IO_ERROR; - #endif - #ifdef ENXIO - case ENXIO: return MA_DOES_NOT_EXIST; - #endif - #ifdef E2BIG - case E2BIG: return MA_INVALID_ARGS; - #endif - #ifdef ENOEXEC - case ENOEXEC: return MA_INVALID_FILE; - #endif - #ifdef EBADF - case EBADF: return MA_INVALID_FILE; - #endif - #ifdef ECHILD - case ECHILD: return MA_ERROR; - #endif - #ifdef EAGAIN - case EAGAIN: return MA_UNAVAILABLE; - #endif - #ifdef ENOMEM - case ENOMEM: return MA_OUT_OF_MEMORY; - #endif - #ifdef EACCES - case EACCES: return MA_ACCESS_DENIED; - #endif - #ifdef EFAULT - case EFAULT: return MA_BAD_ADDRESS; - #endif - #ifdef ENOTBLK - case ENOTBLK: return MA_ERROR; - #endif - #ifdef EBUSY - case EBUSY: return MA_BUSY; - #endif - #ifdef EEXIST - case EEXIST: return MA_ALREADY_EXISTS; - #endif - #ifdef EXDEV - case EXDEV: return MA_ERROR; - #endif - #ifdef ENODEV - case ENODEV: return MA_DOES_NOT_EXIST; - #endif - #ifdef ENOTDIR - case ENOTDIR: return MA_NOT_DIRECTORY; - #endif - #ifdef EISDIR - case EISDIR: return MA_IS_DIRECTORY; - #endif - #ifdef EINVAL - case EINVAL: return MA_INVALID_ARGS; - #endif - #ifdef ENFILE - case ENFILE: return MA_TOO_MANY_OPEN_FILES; - #endif - #ifdef EMFILE - case EMFILE: return MA_TOO_MANY_OPEN_FILES; - #endif - #ifdef ENOTTY - case ENOTTY: return MA_INVALID_OPERATION; - #endif - #ifdef ETXTBSY - case ETXTBSY: return MA_BUSY; - #endif - #ifdef EFBIG - case EFBIG: return MA_TOO_BIG; - #endif - #ifdef ENOSPC - case ENOSPC: return MA_NO_SPACE; - #endif - #ifdef ESPIPE - case ESPIPE: return MA_BAD_SEEK; - #endif - #ifdef EROFS - case EROFS: return MA_ACCESS_DENIED; - #endif - #ifdef EMLINK - case EMLINK: return MA_TOO_MANY_LINKS; - #endif - #ifdef EPIPE - case EPIPE: return MA_BAD_PIPE; - #endif - #ifdef EDOM - case EDOM: return MA_OUT_OF_RANGE; - #endif - #ifdef ERANGE - case ERANGE: return MA_OUT_OF_RANGE; - #endif - #ifdef EDEADLK - case EDEADLK: return MA_DEADLOCK; - #endif - #ifdef ENAMETOOLONG - case ENAMETOOLONG: return MA_PATH_TOO_LONG; - #endif - #ifdef ENOLCK - case ENOLCK: return MA_ERROR; - #endif - #ifdef ENOSYS - case ENOSYS: return MA_NOT_IMPLEMENTED; - #endif - #ifdef ENOTEMPTY - case ENOTEMPTY: return MA_DIRECTORY_NOT_EMPTY; - #endif - #ifdef ELOOP - case ELOOP: return MA_TOO_MANY_LINKS; - #endif - #ifdef ENOMSG - case ENOMSG: return MA_NO_MESSAGE; - #endif - #ifdef EIDRM - case EIDRM: return MA_ERROR; - #endif - #ifdef ECHRNG - case ECHRNG: return MA_ERROR; - #endif - #ifdef EL2NSYNC - case EL2NSYNC: return MA_ERROR; - #endif - #ifdef EL3HLT - case EL3HLT: return MA_ERROR; - #endif - #ifdef EL3RST - case EL3RST: return MA_ERROR; - #endif - #ifdef ELNRNG - case ELNRNG: return MA_OUT_OF_RANGE; - #endif - #ifdef EUNATCH - case EUNATCH: return MA_ERROR; - #endif - #ifdef ENOCSI - case ENOCSI: return MA_ERROR; - #endif - #ifdef EL2HLT - case EL2HLT: return MA_ERROR; - #endif - #ifdef EBADE - case EBADE: return MA_ERROR; - #endif - #ifdef EBADR - case EBADR: return MA_ERROR; - #endif - #ifdef EXFULL - case EXFULL: return MA_ERROR; - #endif - #ifdef ENOANO - case ENOANO: return MA_ERROR; - #endif - #ifdef EBADRQC - case EBADRQC: return MA_ERROR; - #endif - #ifdef EBADSLT - case EBADSLT: return MA_ERROR; - #endif - #ifdef EBFONT - case EBFONT: return MA_INVALID_FILE; - #endif - #ifdef ENOSTR - case ENOSTR: return MA_ERROR; - #endif - #ifdef ENODATA - case ENODATA: return MA_NO_DATA_AVAILABLE; - #endif - #ifdef ETIME - case ETIME: return MA_TIMEOUT; - #endif - #ifdef ENOSR - case ENOSR: return MA_NO_DATA_AVAILABLE; - #endif - #ifdef ENONET - case ENONET: return MA_NO_NETWORK; - #endif - #ifdef ENOPKG - case ENOPKG: return MA_ERROR; - #endif - #ifdef EREMOTE - case EREMOTE: return MA_ERROR; - #endif - #ifdef ENOLINK - case ENOLINK: return MA_ERROR; - #endif - #ifdef EADV - case EADV: return MA_ERROR; - #endif - #ifdef ESRMNT - case ESRMNT: return MA_ERROR; - #endif - #ifdef ECOMM - case ECOMM: return MA_ERROR; - #endif - #ifdef EPROTO - case EPROTO: return MA_ERROR; - #endif - #ifdef EMULTIHOP - case EMULTIHOP: return MA_ERROR; - #endif - #ifdef EDOTDOT - case EDOTDOT: return MA_ERROR; - #endif - #ifdef EBADMSG - case EBADMSG: return MA_BAD_MESSAGE; - #endif - #ifdef EOVERFLOW - case EOVERFLOW: return MA_TOO_BIG; - #endif - #ifdef ENOTUNIQ - case ENOTUNIQ: return MA_NOT_UNIQUE; - #endif - #ifdef EBADFD - case EBADFD: return MA_ERROR; - #endif - #ifdef EREMCHG - case EREMCHG: return MA_ERROR; - #endif - #ifdef ELIBACC - case ELIBACC: return MA_ACCESS_DENIED; - #endif - #ifdef ELIBBAD - case ELIBBAD: return MA_INVALID_FILE; - #endif - #ifdef ELIBSCN - case ELIBSCN: return MA_INVALID_FILE; - #endif - #ifdef ELIBMAX - case ELIBMAX: return MA_ERROR; - #endif - #ifdef ELIBEXEC - case ELIBEXEC: return MA_ERROR; - #endif - #ifdef EILSEQ - case EILSEQ: return MA_INVALID_DATA; - #endif - #ifdef ERESTART - case ERESTART: return MA_ERROR; - #endif - #ifdef ESTRPIPE - case ESTRPIPE: return MA_ERROR; - #endif - #ifdef EUSERS - case EUSERS: return MA_ERROR; - #endif - #ifdef ENOTSOCK - case ENOTSOCK: return MA_NOT_SOCKET; - #endif - #ifdef EDESTADDRREQ - case EDESTADDRREQ: return MA_NO_ADDRESS; - #endif - #ifdef EMSGSIZE - case EMSGSIZE: return MA_TOO_BIG; - #endif - #ifdef EPROTOTYPE - case EPROTOTYPE: return MA_BAD_PROTOCOL; - #endif - #ifdef ENOPROTOOPT - case ENOPROTOOPT: return MA_PROTOCOL_UNAVAILABLE; - #endif - #ifdef EPROTONOSUPPORT - case EPROTONOSUPPORT: return MA_PROTOCOL_NOT_SUPPORTED; - #endif - #ifdef ESOCKTNOSUPPORT - case ESOCKTNOSUPPORT: return MA_SOCKET_NOT_SUPPORTED; - #endif - #ifdef EOPNOTSUPP - case EOPNOTSUPP: return MA_INVALID_OPERATION; - #endif - #ifdef EPFNOSUPPORT - case EPFNOSUPPORT: return MA_PROTOCOL_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EAFNOSUPPORT - case EAFNOSUPPORT: return MA_ADDRESS_FAMILY_NOT_SUPPORTED; - #endif - #ifdef EADDRINUSE - case EADDRINUSE: return MA_ALREADY_IN_USE; - #endif - #ifdef EADDRNOTAVAIL - case EADDRNOTAVAIL: return MA_ERROR; - #endif - #ifdef ENETDOWN - case ENETDOWN: return MA_NO_NETWORK; - #endif - #ifdef ENETUNREACH - case ENETUNREACH: return MA_NO_NETWORK; - #endif - #ifdef ENETRESET - case ENETRESET: return MA_NO_NETWORK; - #endif - #ifdef ECONNABORTED - case ECONNABORTED: return MA_NO_NETWORK; - #endif - #ifdef ECONNRESET - case ECONNRESET: return MA_CONNECTION_RESET; - #endif - #ifdef ENOBUFS - case ENOBUFS: return MA_NO_SPACE; - #endif - #ifdef EISCONN - case EISCONN: return MA_ALREADY_CONNECTED; - #endif - #ifdef ENOTCONN - case ENOTCONN: return MA_NOT_CONNECTED; - #endif - #ifdef ESHUTDOWN - case ESHUTDOWN: return MA_ERROR; - #endif - #ifdef ETOOMANYREFS - case ETOOMANYREFS: return MA_ERROR; - #endif - #ifdef ETIMEDOUT - case ETIMEDOUT: return MA_TIMEOUT; - #endif - #ifdef ECONNREFUSED - case ECONNREFUSED: return MA_CONNECTION_REFUSED; - #endif - #ifdef EHOSTDOWN - case EHOSTDOWN: return MA_NO_HOST; - #endif - #ifdef EHOSTUNREACH - case EHOSTUNREACH: return MA_NO_HOST; - #endif - #ifdef EALREADY - case EALREADY: return MA_IN_PROGRESS; - #endif - #ifdef EINPROGRESS - case EINPROGRESS: return MA_IN_PROGRESS; - #endif - #ifdef ESTALE - case ESTALE: return MA_INVALID_FILE; - #endif - #ifdef EUCLEAN - case EUCLEAN: return MA_ERROR; - #endif - #ifdef ENOTNAM - case ENOTNAM: return MA_ERROR; - #endif - #ifdef ENAVAIL - case ENAVAIL: return MA_ERROR; - #endif - #ifdef EISNAM - case EISNAM: return MA_ERROR; - #endif - #ifdef EREMOTEIO - case EREMOTEIO: return MA_IO_ERROR; - #endif - #ifdef EDQUOT - case EDQUOT: return MA_NO_SPACE; - #endif - #ifdef ENOMEDIUM - case ENOMEDIUM: return MA_DOES_NOT_EXIST; - #endif - #ifdef EMEDIUMTYPE - case EMEDIUMTYPE: return MA_ERROR; - #endif - #ifdef ECANCELED - case ECANCELED: return MA_CANCELLED; - #endif - #ifdef ENOKEY - case ENOKEY: return MA_ERROR; - #endif - #ifdef EKEYEXPIRED - case EKEYEXPIRED: return MA_ERROR; - #endif - #ifdef EKEYREVOKED - case EKEYREVOKED: return MA_ERROR; - #endif - #ifdef EKEYREJECTED - case EKEYREJECTED: return MA_ERROR; - #endif - #ifdef EOWNERDEAD - case EOWNERDEAD: return MA_ERROR; - #endif - #ifdef ENOTRECOVERABLE - case ENOTRECOVERABLE: return MA_ERROR; - #endif - #ifdef ERFKILL - case ERFKILL: return MA_ERROR; - #endif - #ifdef EHWPOISON - case EHWPOISON: return MA_ERROR; - #endif - default: return MA_ERROR; + if (e == 0) { + return MA_SUCCESS; + } +#ifdef EPERM + else if (e == EPERM) { return MA_INVALID_OPERATION; } +#endif +#ifdef ENOENT + else if (e == ENOENT) { return MA_DOES_NOT_EXIST; } +#endif +#ifdef ESRCH + else if (e == ESRCH) { return MA_DOES_NOT_EXIST; } +#endif +#ifdef EINTR + else if (e == EINTR) { return MA_INTERRUPT; } +#endif +#ifdef EIO + else if (e == EIO) { return MA_IO_ERROR; } +#endif +#ifdef ENXIO + else if (e == ENXIO) { return MA_DOES_NOT_EXIST; } +#endif +#ifdef E2BIG + else if (e == E2BIG) { return MA_INVALID_ARGS; } +#endif +#ifdef ENOEXEC + else if (e == ENOEXEC) { return MA_INVALID_FILE; } +#endif +#ifdef EBADF + else if (e == EBADF) { return MA_INVALID_FILE; } +#endif +#ifdef ECHILD + else if (e == ECHILD) { return MA_ERROR; } +#endif +#ifdef EAGAIN + else if (e == EAGAIN) { return MA_UNAVAILABLE; } +#endif +#ifdef ENOMEM + else if (e == ENOMEM) { return MA_OUT_OF_MEMORY; } +#endif +#ifdef EACCES + else if (e == EACCES) { return MA_ACCESS_DENIED; } +#endif +#ifdef EFAULT + else if (e == EFAULT) { return MA_BAD_ADDRESS; } +#endif +#ifdef ENOTBLK + else if (e == ENOTBLK) { return MA_ERROR; } +#endif +#ifdef EBUSY + else if (e == EBUSY) { return MA_BUSY; } +#endif +#ifdef EEXIST + else if (e == EEXIST) { return MA_ALREADY_EXISTS; } +#endif +#ifdef EXDEV + else if (e == EXDEV) { return MA_ERROR; } +#endif +#ifdef ENODEV + else if (e == ENODEV) { return MA_DOES_NOT_EXIST; } +#endif +#ifdef ENOTDIR + else if (e == ENOTDIR) { return MA_NOT_DIRECTORY; } +#endif +#ifdef EISDIR + else if (e == EISDIR) { return MA_IS_DIRECTORY; } +#endif +#ifdef EINVAL + else if (e == EINVAL) { return MA_INVALID_ARGS; } +#endif +#ifdef ENFILE + else if (e == ENFILE) { return MA_TOO_MANY_OPEN_FILES; } +#endif +#ifdef EMFILE + else if (e == EMFILE) { return MA_TOO_MANY_OPEN_FILES; } +#endif +#ifdef ENOTTY + else if (e == ENOTTY) { return MA_INVALID_OPERATION; } +#endif +#ifdef ETXTBSY + else if (e == ETXTBSY) { return MA_BUSY; } +#endif +#ifdef EFBIG + else if (e == EFBIG) { return MA_TOO_BIG; } +#endif +#ifdef ENOSPC + else if (e == ENOSPC) { return MA_NO_SPACE; } +#endif +#ifdef ESPIPE + else if (e == ESPIPE) { return MA_BAD_SEEK; } +#endif +#ifdef EROFS + else if (e == EROFS) { return MA_ACCESS_DENIED; } +#endif +#ifdef EMLINK + else if (e == EMLINK) { return MA_TOO_MANY_LINKS; } +#endif +#ifdef EPIPE + else if (e == EPIPE) { return MA_BAD_PIPE; } +#endif +#ifdef EDOM + else if (e == EDOM) { return MA_OUT_OF_RANGE; } +#endif +#ifdef ERANGE + else if (e == ERANGE) { return MA_OUT_OF_RANGE; } +#endif +#ifdef EDEADLK + else if (e == EDEADLK) { return MA_DEADLOCK; } +#endif +#ifdef ENAMETOOLONG + else if (e == ENAMETOOLONG) { return MA_PATH_TOO_LONG; } +#endif +#ifdef ENOLCK + else if (e == ENOLCK) { return MA_ERROR; } +#endif +#ifdef ENOSYS + else if (e == ENOSYS) { return MA_NOT_IMPLEMENTED; } +#endif +#ifdef ENOTEMPTY + else if (e == ENOTEMPTY) { return MA_DIRECTORY_NOT_EMPTY; } +#endif +#ifdef ELOOP + else if (e == ELOOP) { return MA_TOO_MANY_LINKS; } +#endif +#ifdef ENOMSG + else if (e == ENOMSG) { return MA_NO_MESSAGE; } +#endif +#ifdef EIDRM + else if (e == EIDRM) { return MA_ERROR; } +#endif +#ifdef ECHRNG + else if (e == ECHRNG) { return MA_ERROR; } +#endif +#ifdef EL2NSYNC + else if (e == EL2NSYNC) { return MA_ERROR; } +#endif +#ifdef EL3HLT + else if (e == EL3HLT) { return MA_ERROR; } +#endif +#ifdef EL3RST + else if (e == EL3RST) { return MA_ERROR; } +#endif +#ifdef ELNRNG + else if (e == ELNRNG) { return MA_OUT_OF_RANGE; } +#endif +#ifdef EUNATCH + else if (e == EUNATCH) { return MA_ERROR; } +#endif +#ifdef ENOCSI + else if (e == ENOCSI) { return MA_ERROR; } +#endif +#ifdef EL2HLT + else if (e == EL2HLT) { return MA_ERROR; } +#endif +#ifdef EBADE + else if (e == EBADE) { return MA_ERROR; } +#endif +#ifdef EBADR + else if (e == EBADR) { return MA_ERROR; } +#endif +#ifdef EXFULL + else if (e == EXFULL) { return MA_ERROR; } +#endif +#ifdef ENOANO + else if (e == ENOANO) { return MA_ERROR; } +#endif +#ifdef EBADRQC + else if (e == EBADRQC) { return MA_ERROR; } +#endif +#ifdef EBADSLT + else if (e == EBADSLT) { return MA_ERROR; } +#endif +#ifdef EBFONT + else if (e == EBFONT) { return MA_INVALID_FILE; } +#endif +#ifdef ENOSTR + else if (e == ENOSTR) { return MA_ERROR; } +#endif +#ifdef ENODATA + else if (e == ENODATA) { return MA_NO_DATA_AVAILABLE; } +#endif +#ifdef ETIME + else if (e == ETIME) { return MA_TIMEOUT; } +#endif +#ifdef ENOSR + else if (e == ENOSR) { return MA_NO_DATA_AVAILABLE; } +#endif +#ifdef ENONET + else if (e == ENONET) { return MA_NO_NETWORK; } +#endif +#ifdef ENOPKG + else if (e == ENOPKG) { return MA_ERROR; } +#endif +#ifdef EREMOTE + else if (e == EREMOTE) { return MA_ERROR; } +#endif +#ifdef ENOLINK + else if (e == ENOLINK) { return MA_ERROR; } +#endif +#ifdef EADV + else if (e == EADV) { return MA_ERROR; } +#endif +#ifdef ESRMNT + else if (e == ESRMNT) { return MA_ERROR; } +#endif +#ifdef ECOMM + else if (e == ECOMM) { return MA_ERROR; } +#endif +#ifdef EPROTO + else if (e == EPROTO) { return MA_ERROR; } +#endif +#ifdef EMULTIHOP + else if (e == EMULTIHOP) { return MA_ERROR; } +#endif +#ifdef EDOTDOT + else if (e == EDOTDOT) { return MA_ERROR; } +#endif +#ifdef EBADMSG + else if (e == EBADMSG) { return MA_BAD_MESSAGE; } +#endif +#ifdef EOVERFLOW + else if (e == EOVERFLOW) { return MA_TOO_BIG; } +#endif +#ifdef ENOTUNIQ + else if (e == ENOTUNIQ) { return MA_NOT_UNIQUE; } +#endif +#ifdef EBADFD + else if (e == EBADFD) { return MA_ERROR; } +#endif +#ifdef EREMCHG + else if (e == EREMCHG) { return MA_ERROR; } +#endif +#ifdef ELIBACC + else if (e == ELIBACC) { return MA_ACCESS_DENIED; } +#endif +#ifdef ELIBBAD + else if (e == ELIBBAD) { return MA_INVALID_FILE; } +#endif +#ifdef ELIBSCN + else if (e == ELIBSCN) { return MA_INVALID_FILE; } +#endif +#ifdef ELIBMAX + else if (e == ELIBMAX) { return MA_ERROR; } +#endif +#ifdef ELIBEXEC + else if (e == ELIBEXEC) { return MA_ERROR; } +#endif +#ifdef EILSEQ + else if (e == EILSEQ) { return MA_INVALID_DATA; } +#endif +#ifdef ERESTART + else if (e == ERESTART) { return MA_ERROR; } +#endif +#ifdef ESTRPIPE + else if (e == ESTRPIPE) { return MA_ERROR; } +#endif +#ifdef EUSERS + else if (e == EUSERS) { return MA_ERROR; } +#endif +#ifdef ENOTSOCK + else if (e == ENOTSOCK) { return MA_NOT_SOCKET; } +#endif +#ifdef EDESTADDRREQ + else if (e == EDESTADDRREQ) { return MA_NO_ADDRESS; } +#endif +#ifdef EMSGSIZE + else if (e == EMSGSIZE) { return MA_TOO_BIG; } +#endif +#ifdef EPROTOTYPE + else if (e == EPROTOTYPE) { return MA_BAD_PROTOCOL; } +#endif +#ifdef ENOPROTOOPT + else if (e == ENOPROTOOPT) { return MA_PROTOCOL_UNAVAILABLE; } +#endif +#ifdef EPROTONOSUPPORT + else if (e == EPROTONOSUPPORT) { return MA_PROTOCOL_NOT_SUPPORTED; } +#endif +#ifdef ESOCKTNOSUPPORT + else if (e == ESOCKTNOSUPPORT) { return MA_SOCKET_NOT_SUPPORTED; } +#endif +#ifdef EOPNOTSUPP + else if (e == EOPNOTSUPP) { return MA_INVALID_OPERATION; } +#endif +#ifdef EPFNOSUPPORT + else if (e == EPFNOSUPPORT) { return MA_PROTOCOL_FAMILY_NOT_SUPPORTED; } +#endif +#ifdef EAFNOSUPPORT + else if (e == EAFNOSUPPORT) { return MA_ADDRESS_FAMILY_NOT_SUPPORTED; } +#endif +#ifdef EADDRINUSE + else if (e == EADDRINUSE) { return MA_ALREADY_IN_USE; } +#endif +#ifdef EADDRNOTAVAIL + else if (e == EADDRNOTAVAIL) { return MA_ERROR; } +#endif +#ifdef ENETDOWN + else if (e == ENETDOWN) { return MA_NO_NETWORK; } +#endif +#ifdef ENETUNREACH + else if (e == ENETUNREACH) { return MA_NO_NETWORK; } +#endif +#ifdef ENETRESET + else if (e == ENETRESET) { return MA_NO_NETWORK; } +#endif +#ifdef ECONNABORTED + else if (e == ECONNABORTED) { return MA_NO_NETWORK; } +#endif +#ifdef ECONNRESET + else if (e == ECONNRESET) { return MA_CONNECTION_RESET; } +#endif +#ifdef ENOBUFS + else if (e == ENOBUFS) { return MA_NO_SPACE; } +#endif +#ifdef EISCONN + else if (e == EISCONN) { return MA_ALREADY_CONNECTED; } +#endif +#ifdef ENOTCONN + else if (e == ENOTCONN) { return MA_NOT_CONNECTED; } +#endif +#ifdef ESHUTDOWN + else if (e == ESHUTDOWN) { return MA_ERROR; } +#endif +#ifdef ETOOMANYREFS + else if (e == ETOOMANYREFS) { return MA_ERROR; } +#endif +#ifdef ETIMEDOUT + else if (e == ETIMEDOUT) { return MA_TIMEOUT; } +#endif +#ifdef ECONNREFUSED + else if (e == ECONNREFUSED) { return MA_CONNECTION_REFUSED; } +#endif +#ifdef EHOSTDOWN + else if (e == EHOSTDOWN) { return MA_NO_HOST; } +#endif +#ifdef EHOSTUNREACH + else if (e == EHOSTUNREACH) { return MA_NO_HOST; } +#endif +#ifdef EALREADY + else if (e == EALREADY) { return MA_IN_PROGRESS; } +#endif +#ifdef EINPROGRESS + else if (e == EINPROGRESS) { return MA_IN_PROGRESS; } +#endif +#ifdef ESTALE + else if (e == ESTALE) { return MA_INVALID_FILE; } +#endif +#ifdef EUCLEAN + else if (e == EUCLEAN) { return MA_ERROR; } +#endif +#ifdef ENOTNAM + else if (e == ENOTNAM) { return MA_ERROR; } +#endif +#ifdef ENAVAIL + else if (e == ENAVAIL) { return MA_ERROR; } +#endif +#ifdef EISNAM + else if (e == EISNAM) { return MA_ERROR; } +#endif +#ifdef EREMOTEIO + else if (e == EREMOTEIO) { return MA_IO_ERROR; } +#endif +#ifdef EDQUOT + else if (e == EDQUOT) { return MA_NO_SPACE; } +#endif +#ifdef ENOMEDIUM + else if (e == ENOMEDIUM) { return MA_DOES_NOT_EXIST; } +#endif +#ifdef EMEDIUMTYPE + else if (e == EMEDIUMTYPE) { return MA_ERROR; } +#endif +#ifdef ECANCELED + else if (e == ECANCELED) { return MA_CANCELLED; } +#endif +#ifdef ENOKEY + else if (e == ENOKEY) { return MA_ERROR; } +#endif +#ifdef EKEYEXPIRED + else if (e == EKEYEXPIRED) { return MA_ERROR; } +#endif +#ifdef EKEYREVOKED + else if (e == EKEYREVOKED) { return MA_ERROR; } +#endif +#ifdef EKEYREJECTED + else if (e == EKEYREJECTED) { return MA_ERROR; } +#endif +#ifdef EOWNERDEAD + else if (e == EOWNERDEAD) { return MA_ERROR; } +#endif +#ifdef ENOTRECOVERABLE + else if (e == ENOTRECOVERABLE) { return MA_ERROR; } +#endif +#ifdef ERFKILL + else if (e == ERFKILL) { return MA_ERROR; } +#endif +#ifdef EHWPOISON + else if (e == EHWPOISON) { return MA_ERROR; } +#endif + else { + return MA_ERROR; } } @@ -13864,6 +13950,13 @@ Atomics #if defined(__cplusplus) extern "C" { #endif +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wlong-long" + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc++11-long-long" + #endif +#endif typedef signed char c89atomic_int8; typedef unsigned char c89atomic_uint8; typedef signed short c89atomic_int16; @@ -13874,18 +13967,8 @@ typedef unsigned int c89atomic_uint32; typedef signed __int64 c89atomic_int64; typedef unsigned __int64 c89atomic_uint64; #else - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wlong-long" - #if defined(__clang__) - #pragma GCC diagnostic ignored "-Wc++11-long-long" - #endif - #endif typedef signed long long c89atomic_int64; typedef unsigned long long c89atomic_uint64; - #if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) - #pragma GCC diagnostic pop - #endif #endif typedef int c89atomic_memory_order; typedef unsigned char c89atomic_bool; @@ -14725,10 +14808,26 @@ typedef unsigned char c89atomic_bool; #define c89atomic_fetch_and_explicit_16(dst, src, order) __atomic_fetch_and(dst, src, order) #define c89atomic_fetch_and_explicit_32(dst, src, order) __atomic_fetch_and(dst, src, order) #define c89atomic_fetch_and_explicit_64(dst, src, order) __atomic_fetch_and(dst, src, order) - #define c89atomic_compare_and_swap_8 (dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_compare_and_swap_16(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_compare_and_swap_32(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) - #define c89atomic_compare_and_swap_64(dst, expected, desired) __sync_val_compare_and_swap(dst, expected, desired) + static C89ATOMIC_INLINE c89atomic_uint8 c89atomic_compare_and_swap_8(volatile c89atomic_uint8* dst, c89atomic_uint8 expected, c89atomic_uint8 desired) + { + __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return expected; + } + static C89ATOMIC_INLINE c89atomic_uint16 c89atomic_compare_and_swap_16(volatile c89atomic_uint16* dst, c89atomic_uint16 expected, c89atomic_uint16 desired) + { + __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return expected; + } + static C89ATOMIC_INLINE c89atomic_uint32 c89atomic_compare_and_swap_32(volatile c89atomic_uint32* dst, c89atomic_uint32 expected, c89atomic_uint32 desired) + { + __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return expected; + } + static C89ATOMIC_INLINE c89atomic_uint64 c89atomic_compare_and_swap_64(volatile c89atomic_uint64* dst, c89atomic_uint64 expected, c89atomic_uint64 desired) + { + __atomic_compare_exchange_n(dst, &expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + return expected; + } typedef c89atomic_uint8 c89atomic_flag; #define c89atomic_flag_test_and_set_explicit(dst, order) (c89atomic_bool)__atomic_test_and_set(dst, order) #define c89atomic_flag_clear_explicit(dst, order) __atomic_clear(dst, order) @@ -15250,7 +15349,7 @@ typedef unsigned char c89atomic_bool; #endif #if !defined(C89ATOMIC_HAS_NATIVE_COMPARE_EXCHANGE) #if defined(C89ATOMIC_HAS_8) - c89atomic_bool c89atomic_compare_exchange_strong_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8* expected, c89atomic_uint8 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_8(volatile c89atomic_uint8* dst, c89atomic_uint8* expected, c89atomic_uint8 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint8 expectedValue; c89atomic_uint8 result; @@ -15267,7 +15366,7 @@ typedef unsigned char c89atomic_bool; } #endif #if defined(C89ATOMIC_HAS_16) - c89atomic_bool c89atomic_compare_exchange_strong_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16* expected, c89atomic_uint16 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_16(volatile c89atomic_uint16* dst, c89atomic_uint16* expected, c89atomic_uint16 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint16 expectedValue; c89atomic_uint16 result; @@ -15284,7 +15383,7 @@ typedef unsigned char c89atomic_bool; } #endif #if defined(C89ATOMIC_HAS_32) - c89atomic_bool c89atomic_compare_exchange_strong_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32* expected, c89atomic_uint32 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_32(volatile c89atomic_uint32* dst, c89atomic_uint32* expected, c89atomic_uint32 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint32 expectedValue; c89atomic_uint32 result; @@ -15301,7 +15400,7 @@ typedef unsigned char c89atomic_bool; } #endif #if defined(C89ATOMIC_HAS_64) - c89atomic_bool c89atomic_compare_exchange_strong_explicit_64(volatile c89atomic_uint64* dst, volatile c89atomic_uint64* expected, c89atomic_uint64 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) + static C89ATOMIC_INLINE c89atomic_bool c89atomic_compare_exchange_strong_explicit_64(volatile c89atomic_uint64* dst, volatile c89atomic_uint64* expected, c89atomic_uint64 desired, c89atomic_memory_order successOrder, c89atomic_memory_order failureOrder) { c89atomic_uint64 expectedValue; c89atomic_uint64 result; @@ -15779,6 +15878,9 @@ static C89ATOMIC_INLINE void c89atomic_spinlock_unlock(volatile c89atomic_spinlo { c89atomic_flag_clear_explicit(pSpinlock, c89atomic_memory_order_release); } +#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))) + #pragma GCC diagnostic pop +#endif #if defined(__cplusplus) } #endif @@ -15854,7 +15956,10 @@ MA_ATOMIC_SAFE_TYPE_IMPL(i32, int32) MA_ATOMIC_SAFE_TYPE_IMPL(64, uint64) MA_ATOMIC_SAFE_TYPE_IMPL(f32, float) MA_ATOMIC_SAFE_TYPE_IMPL(32, bool32) + +#if !defined(MA_NO_DEVICE_IO) MA_ATOMIC_SAFE_TYPE_IMPL(i32, device_state) +#endif MA_API ma_uint64 ma_calculate_frame_count_after_resampling(ma_uint32 sampleRateOut, ma_uint32 sampleRateIn, ma_uint64 frameCountIn) @@ -15962,158 +16067,16 @@ MA_API ma_result ma_spinlock_unlock(volatile ma_spinlock* pSpinlock) #ifndef MA_NO_THREADING -#ifdef MA_WIN32 - #define MA_THREADCALL WINAPI - typedef unsigned long ma_thread_result; -#else +#if defined(MA_POSIX) #define MA_THREADCALL typedef void* ma_thread_result; +#elif defined(MA_WIN32) + #define MA_THREADCALL WINAPI + typedef unsigned long ma_thread_result; #endif + typedef ma_thread_result (MA_THREADCALL * ma_thread_entry_proc)(void* pData); -#ifdef MA_WIN32 -static int ma_thread_priority_to_win32(ma_thread_priority priority) -{ - switch (priority) { - case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE; - case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST; - case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL; - case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL; - case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL; - case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST; - case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL; - default: return THREAD_PRIORITY_NORMAL; - } -} - -static ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) -{ - DWORD threadID; /* Not used. Only used for passing into CreateThread() so it doesn't fail on Windows 98. */ - - *pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, &threadID); - if (*pThread == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - SetThreadPriority((HANDLE)*pThread, ma_thread_priority_to_win32(priority)); - - return MA_SUCCESS; -} - -static void ma_thread_wait__win32(ma_thread* pThread) -{ - WaitForSingleObject((HANDLE)*pThread, INFINITE); - CloseHandle((HANDLE)*pThread); -} - - -static ma_result ma_mutex_init__win32(ma_mutex* pMutex) -{ - *pMutex = CreateEventA(NULL, FALSE, TRUE, NULL); - if (*pMutex == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_mutex_uninit__win32(ma_mutex* pMutex) -{ - CloseHandle((HANDLE)*pMutex); -} - -static void ma_mutex_lock__win32(ma_mutex* pMutex) -{ - WaitForSingleObject((HANDLE)*pMutex, INFINITE); -} - -static void ma_mutex_unlock__win32(ma_mutex* pMutex) -{ - SetEvent((HANDLE)*pMutex); -} - - -static ma_result ma_event_init__win32(ma_event* pEvent) -{ - *pEvent = CreateEventA(NULL, FALSE, FALSE, NULL); - if (*pEvent == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_event_uninit__win32(ma_event* pEvent) -{ - CloseHandle((HANDLE)*pEvent); -} - -static ma_result ma_event_wait__win32(ma_event* pEvent) -{ - DWORD result = WaitForSingleObject((HANDLE)*pEvent, INFINITE); - if (result == WAIT_OBJECT_0) { - return MA_SUCCESS; - } - - if (result == WAIT_TIMEOUT) { - return MA_TIMEOUT; - } - - return ma_result_from_GetLastError(GetLastError()); -} - -static ma_result ma_event_signal__win32(ma_event* pEvent) -{ - BOOL result = SetEvent((HANDLE)*pEvent); - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - - -static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore) -{ - *pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL); - if (*pSemaphore == NULL) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} - -static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore) -{ - CloseHandle((HANDLE)*pSemaphore); -} - -static ma_result ma_semaphore_wait__win32(ma_semaphore* pSemaphore) -{ - DWORD result = WaitForSingleObject((HANDLE)*pSemaphore, INFINITE); - if (result == WAIT_OBJECT_0) { - return MA_SUCCESS; - } - - if (result == WAIT_TIMEOUT) { - return MA_TIMEOUT; - } - - return ma_result_from_GetLastError(GetLastError()); -} - -static ma_result ma_semaphore_release__win32(ma_semaphore* pSemaphore) -{ - BOOL result = ReleaseSemaphore((HANDLE)*pSemaphore, 1, NULL); - if (result == 0) { - return ma_result_from_GetLastError(GetLastError()); - } - - return MA_SUCCESS; -} -#endif - - #ifdef MA_POSIX static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) { @@ -16129,23 +16092,28 @@ static ma_result ma_thread_create__posix(ma_thread* pThread, ma_thread_priority /* We successfully initialized our attributes object so we can assign the pointer so it's passed into pthread_create(). */ pAttr = &attr; - if (priority == ma_thread_priority_idle) { -#ifdef SCHED_IDLE - if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) { - scheduler = SCHED_IDLE; + /* We need to set the scheduler policy. Only do this if the OS supports pthread_attr_setschedpolicy() */ + #if !defined(MA_BEOS) + { + if (priority == ma_thread_priority_idle) { + #ifdef SCHED_IDLE + if (pthread_attr_setschedpolicy(&attr, SCHED_IDLE) == 0) { + scheduler = SCHED_IDLE; + } + #endif + } else if (priority == ma_thread_priority_realtime) { + #ifdef SCHED_FIFO + if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) { + scheduler = SCHED_FIFO; + } + #endif + #ifdef MA_LINUX + } else { + scheduler = sched_getscheduler(0); + #endif } -#endif - } else if (priority == ma_thread_priority_realtime) { -#ifdef SCHED_FIFO - if (pthread_attr_setschedpolicy(&attr, SCHED_FIFO) == 0) { - scheduler = SCHED_FIFO; - } -#endif -#ifdef MA_LINUX - } else { - scheduler = sched_getscheduler(0); -#endif } + #endif if (stackSize > 0) { pthread_attr_setstacksize(&attr, stackSize); @@ -16350,6 +16318,146 @@ static ma_result ma_semaphore_release__posix(ma_semaphore* pSemaphore) return MA_SUCCESS; } +#elif defined(MA_WIN32) +static int ma_thread_priority_to_win32(ma_thread_priority priority) +{ + switch (priority) { + case ma_thread_priority_idle: return THREAD_PRIORITY_IDLE; + case ma_thread_priority_lowest: return THREAD_PRIORITY_LOWEST; + case ma_thread_priority_low: return THREAD_PRIORITY_BELOW_NORMAL; + case ma_thread_priority_normal: return THREAD_PRIORITY_NORMAL; + case ma_thread_priority_high: return THREAD_PRIORITY_ABOVE_NORMAL; + case ma_thread_priority_highest: return THREAD_PRIORITY_HIGHEST; + case ma_thread_priority_realtime: return THREAD_PRIORITY_TIME_CRITICAL; + default: return THREAD_PRIORITY_NORMAL; + } +} + +static ma_result ma_thread_create__win32(ma_thread* pThread, ma_thread_priority priority, size_t stackSize, ma_thread_entry_proc entryProc, void* pData) +{ + DWORD threadID; /* Not used. Only used for passing into CreateThread() so it doesn't fail on Windows 98. */ + + *pThread = CreateThread(NULL, stackSize, entryProc, pData, 0, &threadID); + if (*pThread == NULL) { + return ma_result_from_GetLastError(GetLastError()); + } + + SetThreadPriority((HANDLE)*pThread, ma_thread_priority_to_win32(priority)); + + return MA_SUCCESS; +} + +static void ma_thread_wait__win32(ma_thread* pThread) +{ + WaitForSingleObject((HANDLE)*pThread, INFINITE); + CloseHandle((HANDLE)*pThread); +} + + +static ma_result ma_mutex_init__win32(ma_mutex* pMutex) +{ + *pMutex = CreateEventA(NULL, FALSE, TRUE, NULL); + if (*pMutex == NULL) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + +static void ma_mutex_uninit__win32(ma_mutex* pMutex) +{ + CloseHandle((HANDLE)*pMutex); +} + +static void ma_mutex_lock__win32(ma_mutex* pMutex) +{ + WaitForSingleObject((HANDLE)*pMutex, INFINITE); +} + +static void ma_mutex_unlock__win32(ma_mutex* pMutex) +{ + SetEvent((HANDLE)*pMutex); +} + + +static ma_result ma_event_init__win32(ma_event* pEvent) +{ + *pEvent = CreateEventA(NULL, FALSE, FALSE, NULL); + if (*pEvent == NULL) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + +static void ma_event_uninit__win32(ma_event* pEvent) +{ + CloseHandle((HANDLE)*pEvent); +} + +static ma_result ma_event_wait__win32(ma_event* pEvent) +{ + DWORD result = WaitForSingleObject((HANDLE)*pEvent, INFINITE); + if (result == WAIT_OBJECT_0) { + return MA_SUCCESS; + } + + if (result == WAIT_TIMEOUT) { + return MA_TIMEOUT; + } + + return ma_result_from_GetLastError(GetLastError()); +} + +static ma_result ma_event_signal__win32(ma_event* pEvent) +{ + BOOL result = SetEvent((HANDLE)*pEvent); + if (result == 0) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + + +static ma_result ma_semaphore_init__win32(int initialValue, ma_semaphore* pSemaphore) +{ + *pSemaphore = CreateSemaphoreW(NULL, (LONG)initialValue, LONG_MAX, NULL); + if (*pSemaphore == NULL) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} + +static void ma_semaphore_uninit__win32(ma_semaphore* pSemaphore) +{ + CloseHandle((HANDLE)*pSemaphore); +} + +static ma_result ma_semaphore_wait__win32(ma_semaphore* pSemaphore) +{ + DWORD result = WaitForSingleObject((HANDLE)*pSemaphore, INFINITE); + if (result == WAIT_OBJECT_0) { + return MA_SUCCESS; + } + + if (result == WAIT_TIMEOUT) { + return MA_TIMEOUT; + } + + return ma_result_from_GetLastError(GetLastError()); +} + +static ma_result ma_semaphore_release__win32(ma_semaphore* pSemaphore) +{ + BOOL result = ReleaseSemaphore((HANDLE)*pSemaphore, 1, NULL); + if (result == 0) { + return ma_result_from_GetLastError(GetLastError()); + } + + return MA_SUCCESS; +} #endif typedef struct @@ -16399,15 +16507,20 @@ static ma_result ma_thread_create(ma_thread* pThread, ma_thread_priority priorit return MA_OUT_OF_MEMORY; } +#if defined(MA_THREAD_DEFAULT_STACK_SIZE) + if (stackSize == 0) { + stackSize = MA_THREAD_DEFAULT_STACK_SIZE; + } +#endif + pProxyData->entryProc = entryProc; pProxyData->pData = pData; ma_allocation_callbacks_init_copy(&pProxyData->allocationCallbacks, pAllocationCallbacks); -#ifdef MA_WIN32 - result = ma_thread_create__win32(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) result = ma_thread_create__posix(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); +#elif defined(MA_WIN32) + result = ma_thread_create__win32(pThread, priority, stackSize, ma_thread_entry_proxy, pProxyData); #endif if (result != MA_SUCCESS) { @@ -16424,11 +16537,10 @@ static void ma_thread_wait(ma_thread* pThread) return; } -#ifdef MA_WIN32 - ma_thread_wait__win32(pThread); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) ma_thread_wait__posix(pThread); +#elif defined(MA_WIN32) + ma_thread_wait__win32(pThread); #endif } @@ -16440,11 +16552,10 @@ MA_API ma_result ma_mutex_init(ma_mutex* pMutex) return MA_INVALID_ARGS; } -#ifdef MA_WIN32 - return ma_mutex_init__win32(pMutex); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) return ma_mutex_init__posix(pMutex); +#elif defined(MA_WIN32) + return ma_mutex_init__win32(pMutex); #endif } @@ -16454,11 +16565,10 @@ MA_API void ma_mutex_uninit(ma_mutex* pMutex) return; } -#ifdef MA_WIN32 - ma_mutex_uninit__win32(pMutex); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) ma_mutex_uninit__posix(pMutex); +#elif defined(MA_WIN32) + ma_mutex_uninit__win32(pMutex); #endif } @@ -16469,11 +16579,10 @@ MA_API void ma_mutex_lock(ma_mutex* pMutex) return; } -#ifdef MA_WIN32 - ma_mutex_lock__win32(pMutex); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) ma_mutex_lock__posix(pMutex); +#elif defined(MA_WIN32) + ma_mutex_lock__win32(pMutex); #endif } @@ -16482,13 +16591,12 @@ MA_API void ma_mutex_unlock(ma_mutex* pMutex) if (pMutex == NULL) { MA_ASSERT(MA_FALSE); /* Fire an assert so the caller is aware of this bug. */ return; -} + } -#ifdef MA_WIN32 - ma_mutex_unlock__win32(pMutex); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) ma_mutex_unlock__posix(pMutex); +#elif defined(MA_WIN32) + ma_mutex_unlock__win32(pMutex); #endif } @@ -16500,11 +16608,10 @@ MA_API ma_result ma_event_init(ma_event* pEvent) return MA_INVALID_ARGS; } -#ifdef MA_WIN32 - return ma_event_init__win32(pEvent); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) return ma_event_init__posix(pEvent); +#elif defined(MA_WIN32) + return ma_event_init__win32(pEvent); #endif } @@ -16542,11 +16649,10 @@ MA_API void ma_event_uninit(ma_event* pEvent) return; } -#ifdef MA_WIN32 - ma_event_uninit__win32(pEvent); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) ma_event_uninit__posix(pEvent); +#elif defined(MA_WIN32) + ma_event_uninit__win32(pEvent); #endif } @@ -16569,11 +16675,10 @@ MA_API ma_result ma_event_wait(ma_event* pEvent) return MA_INVALID_ARGS; } -#ifdef MA_WIN32 - return ma_event_wait__win32(pEvent); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) return ma_event_wait__posix(pEvent); +#elif defined(MA_WIN32) + return ma_event_wait__win32(pEvent); #endif } @@ -16584,11 +16689,10 @@ MA_API ma_result ma_event_signal(ma_event* pEvent) return MA_INVALID_ARGS; } -#ifdef MA_WIN32 - return ma_event_signal__win32(pEvent); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) return ma_event_signal__posix(pEvent); +#elif defined(MA_WIN32) + return ma_event_signal__win32(pEvent); #endif } @@ -16600,11 +16704,10 @@ MA_API ma_result ma_semaphore_init(int initialValue, ma_semaphore* pSemaphore) return MA_INVALID_ARGS; } -#ifdef MA_WIN32 - return ma_semaphore_init__win32(initialValue, pSemaphore); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) return ma_semaphore_init__posix(initialValue, pSemaphore); +#elif defined(MA_WIN32) + return ma_semaphore_init__win32(initialValue, pSemaphore); #endif } @@ -16615,11 +16718,10 @@ MA_API void ma_semaphore_uninit(ma_semaphore* pSemaphore) return; } -#ifdef MA_WIN32 - ma_semaphore_uninit__win32(pSemaphore); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) ma_semaphore_uninit__posix(pSemaphore); +#elif defined(MA_WIN32) + ma_semaphore_uninit__win32(pSemaphore); #endif } @@ -16630,11 +16732,10 @@ MA_API ma_result ma_semaphore_wait(ma_semaphore* pSemaphore) return MA_INVALID_ARGS; } -#ifdef MA_WIN32 - return ma_semaphore_wait__win32(pSemaphore); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) return ma_semaphore_wait__posix(pSemaphore); +#elif defined(MA_WIN32) + return ma_semaphore_wait__win32(pSemaphore); #endif } @@ -16645,11 +16746,10 @@ MA_API ma_result ma_semaphore_release(ma_semaphore* pSemaphore) return MA_INVALID_ARGS; } -#ifdef MA_WIN32 - return ma_semaphore_release__win32(pSemaphore); -#endif -#ifdef MA_POSIX +#if defined(MA_POSIX) return ma_semaphore_release__posix(pSemaphore); +#elif defined(MA_WIN32) + return ma_semaphore_release__win32(pSemaphore); #endif } #else @@ -17697,11 +17797,6 @@ DEVICE I/O #endif #ifndef MA_NO_DEVICE_IO -#ifdef MA_WIN32 - #include - #include - #include -#endif #if defined(MA_APPLE) && (__MAC_OS_X_VERSION_MIN_REQUIRED < 101200) #include /* For mach_absolute_time() */ @@ -17965,7 +18060,7 @@ MA_API ma_bool32 ma_is_loopback_supported(ma_backend backend) -#ifdef MA_WIN32 +#if defined(MA_WIN32) /* WASAPI error codes. */ #define MA_AUDCLNT_E_NOT_INITIALIZED ((HRESULT)0x88890001) #define MA_AUDCLNT_E_ALREADY_INITIALIZED ((HRESULT)0x88890002) @@ -18125,23 +18220,109 @@ static ma_result ma_result_from_HRESULT(HRESULT hr) } } -typedef HRESULT (WINAPI * MA_PFN_CoInitialize)(LPVOID pvReserved); -typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(LPVOID pvReserved, DWORD dwCoInit); -typedef void (WINAPI * MA_PFN_CoUninitialize)(void); -typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv); -typedef void (WINAPI * MA_PFN_CoTaskMemFree)(LPVOID pv); -typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(PROPVARIANT *pvar); -typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, LPOLESTR lpsz, int cchMax); +/* PROPVARIANT */ +#define MA_VT_LPWSTR 31 +#define MA_VT_BLOB 65 -typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void); -typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void); +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(push) + #pragma warning(disable:4201) /* nonstandard extension used: nameless struct/union */ +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wpedantic" /* For ISO C99 doesn't support unnamed structs/unions [-Wpedantic] */ + #if defined(__clang__) + #pragma GCC diagnostic ignored "-Wc11-extensions" /* anonymous unions are a C11 extension */ + #endif +#endif +typedef struct +{ + WORD vt; + WORD wReserved1; + WORD wReserved2; + WORD wReserved3; + union + { + struct + { + ULONG cbSize; + BYTE* pBlobData; + } blob; + WCHAR* pwszVal; + char pad[16]; /* Just to ensure the size of the struct matches the official version. */ + }; +} MA_PROPVARIANT; +#if defined(_MSC_VER) && !defined(__clang__) + #pragma warning(pop) +#elif defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))) + #pragma GCC diagnostic pop +#endif + +typedef HRESULT (WINAPI * MA_PFN_CoInitialize)(void* pvReserved); +typedef HRESULT (WINAPI * MA_PFN_CoInitializeEx)(void* pvReserved, DWORD dwCoInit); +typedef void (WINAPI * MA_PFN_CoUninitialize)(void); +typedef HRESULT (WINAPI * MA_PFN_CoCreateInstance)(const IID* rclsid, void* pUnkOuter, DWORD dwClsContext, const IID* riid, void* ppv); +typedef void (WINAPI * MA_PFN_CoTaskMemFree)(void* pv); +typedef HRESULT (WINAPI * MA_PFN_PropVariantClear)(MA_PROPVARIANT *pvar); +typedef int (WINAPI * MA_PFN_StringFromGUID2)(const GUID* const rguid, WCHAR* lpsz, int cchMax); + +typedef HWND (WINAPI * MA_PFN_GetForegroundWindow)(void); +typedef HWND (WINAPI * MA_PFN_GetDesktopWindow)(void); #if defined(MA_WIN32_DESKTOP) /* Microsoft documents these APIs as returning LSTATUS, but the Win32 API shipping with some compilers do not define it. It's just a LONG. */ -typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult); -typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey); -typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD lpType, LPBYTE lpData, LPDWORD lpcbData); +typedef LONG (WINAPI * MA_PFN_RegOpenKeyExA)(HKEY hKey, const char* lpSubKey, DWORD ulOptions, DWORD samDesired, HKEY* phkResult); +typedef LONG (WINAPI * MA_PFN_RegCloseKey)(HKEY hKey); +typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, const char* lpValueName, DWORD* lpReserved, DWORD* lpType, BYTE* lpData, DWORD* lpcbData); #endif /* MA_WIN32_DESKTOP */ + + +MA_API size_t ma_strlen_WCHAR(const WCHAR* str) +{ + size_t len = 0; + while (str[len] != '\0') { + len += 1; + } + + return len; +} + +MA_API int ma_strcmp_WCHAR(const WCHAR *s1, const WCHAR *s2) +{ + while (*s1 != '\0' && *s1 == *s2) { + s1 += 1; + s2 += 1; + } + + return *s1 - *s2; +} + +MA_API int ma_strcpy_s_WCHAR(WCHAR* dst, size_t dstCap, const WCHAR* src) +{ + size_t i; + + if (dst == 0) { + return 22; + } + if (dstCap == 0) { + return 34; + } + if (src == 0) { + dst[0] = '\0'; + return 22; + } + + for (i = 0; i < dstCap && src[i] != '\0'; ++i) { + dst[i] = src[i]; + } + + if (i < dstCap) { + dst[i] = '\0'; + return 0; + } + + dst[0] = '\0'; + return 34; +} #endif /* MA_WIN32 */ @@ -18156,7 +18337,7 @@ typedef LONG (WINAPI * MA_PFN_RegQueryValueExA)(HKEY hKey, LPCSTR lpValueName, L Timing *******************************************************************************/ -#ifdef MA_WIN32 +#if defined(MA_WIN32) && !defined(MA_POSIX) static LARGE_INTEGER g_ma_TimerFrequency; /* <-- Initialized to zero since it's static. */ void ma_timer_init(ma_timer* pTimer) { @@ -18274,22 +18455,22 @@ MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename) ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_DEBUG, "Loading library: %s\n", filename); -#ifdef _WIN32 - /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ - #if !defined(WINAPI_FAMILY) || (defined(WINAPI_FAMILY) && (defined(WINAPI_FAMILY_DESKTOP_APP) && WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) - handle = (ma_handle)LoadLibraryA(filename); + #ifdef MA_WIN32 + /* From MSDN: Desktop applications cannot use LoadPackagedLibrary; if a desktop application calls this function it fails with APPMODEL_ERROR_NO_PACKAGE.*/ + #if !defined(MA_WIN32_UWP) + handle = (ma_handle)LoadLibraryA(filename); + #else + /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ + WCHAR filenameW[4096]; + if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { + handle = NULL; + } else { + handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); + } + #endif #else - /* *sigh* It appears there is no ANSI version of LoadPackagedLibrary()... */ - WCHAR filenameW[4096]; - if (MultiByteToWideChar(CP_UTF8, 0, filename, -1, filenameW, sizeof(filenameW)) == 0) { - handle = NULL; - } else { - handle = (ma_handle)LoadPackagedLibrary(filenameW, 0); - } + handle = (ma_handle)dlopen(filename, RTLD_NOW); #endif -#else - handle = (ma_handle)dlopen(filename, RTLD_NOW); -#endif /* I'm not considering failure to load a library an error nor a warning because seamlessly falling through to a lower-priority @@ -18312,11 +18493,11 @@ MA_API ma_handle ma_dlopen(ma_context* pContext, const char* filename) MA_API void ma_dlclose(ma_context* pContext, ma_handle handle) { #ifndef MA_NO_RUNTIME_LINKING -#ifdef _WIN32 - FreeLibrary((HMODULE)handle); -#else - dlclose((void*)handle); -#endif + #ifdef MA_WIN32 + FreeLibrary((HMODULE)handle); + #else + dlclose((void*)handle); + #endif (void)pContext; #else @@ -18919,7 +19100,7 @@ static MA_INLINE void ma_device__set_state(ma_device* pDevice, ma_device_state n } -#ifdef MA_WIN32 +#if defined(MA_WIN32) GUID MA_GUID_KSDATAFORMAT_SUBTYPE_PCM = {0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; GUID MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT = {0x00000003, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; /*GUID MA_GUID_KSDATAFORMAT_SUBTYPE_ALAW = {0x00000006, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};*/ @@ -19639,7 +19820,7 @@ WIN32 COMMON *******************************************************************************/ #if defined(MA_WIN32) -#if defined(MA_WIN32_DESKTOP) +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) #define ma_CoInitializeEx(pContext, pvReserved, dwCoInit) ((pContext->win32.CoInitializeEx) ? ((MA_PFN_CoInitializeEx)pContext->win32.CoInitializeEx)(pvReserved, dwCoInit) : ((MA_PFN_CoInitialize)pContext->win32.CoInitialize)(pvReserved)) #define ma_CoUninitialize(pContext) ((MA_PFN_CoUninitialize)pContext->win32.CoUninitialize)() #define ma_CoCreateInstance(pContext, rclsid, pUnkOuter, dwClsContext, riid, ppv) ((MA_PFN_CoCreateInstance)pContext->win32.CoCreateInstance)(rclsid, pUnkOuter, dwClsContext, riid, ppv) @@ -19657,19 +19838,34 @@ WIN32 COMMON typedef size_t DWORD_PTR; #endif +#if !defined(WAVE_FORMAT_1M08) +#define WAVE_FORMAT_1M08 0x00000001 +#define WAVE_FORMAT_1S08 0x00000002 +#define WAVE_FORMAT_1M16 0x00000004 +#define WAVE_FORMAT_1S16 0x00000008 +#define WAVE_FORMAT_2M08 0x00000010 +#define WAVE_FORMAT_2S08 0x00000020 +#define WAVE_FORMAT_2M16 0x00000040 +#define WAVE_FORMAT_2S16 0x00000080 +#define WAVE_FORMAT_4M08 0x00000100 +#define WAVE_FORMAT_4S08 0x00000200 +#define WAVE_FORMAT_4M16 0x00000400 +#define WAVE_FORMAT_4S16 0x00000800 +#endif + #if !defined(WAVE_FORMAT_44M08) -#define WAVE_FORMAT_44M08 0x00000100 -#define WAVE_FORMAT_44S08 0x00000200 -#define WAVE_FORMAT_44M16 0x00000400 -#define WAVE_FORMAT_44S16 0x00000800 -#define WAVE_FORMAT_48M08 0x00001000 -#define WAVE_FORMAT_48S08 0x00002000 -#define WAVE_FORMAT_48M16 0x00004000 -#define WAVE_FORMAT_48S16 0x00008000 -#define WAVE_FORMAT_96M08 0x00010000 -#define WAVE_FORMAT_96S08 0x00020000 -#define WAVE_FORMAT_96M16 0x00040000 -#define WAVE_FORMAT_96S16 0x00080000 +#define WAVE_FORMAT_44M08 0x00000100 +#define WAVE_FORMAT_44S08 0x00000200 +#define WAVE_FORMAT_44M16 0x00000400 +#define WAVE_FORMAT_44S16 0x00000800 +#define WAVE_FORMAT_48M08 0x00001000 +#define WAVE_FORMAT_48S08 0x00002000 +#define WAVE_FORMAT_48M16 0x00004000 +#define WAVE_FORMAT_48S16 0x00008000 +#define WAVE_FORMAT_96M08 0x00010000 +#define WAVE_FORMAT_96S08 0x00020000 +#define WAVE_FORMAT_96M16 0x00040000 +#define WAVE_FORMAT_96S16 0x00080000 #endif #ifndef SPEAKER_FRONT_LEFT @@ -19694,13 +19890,30 @@ typedef size_t DWORD_PTR; #endif /* -The SDK that comes with old versions of MSVC (VC6, for example) does not appear to define WAVEFORMATEXTENSIBLE. We -define our own implementation in this case. +Implement our own version of MA_WAVEFORMATEXTENSIBLE so we can avoid a header. Be careful with this +because MA_WAVEFORMATEX has an extra two bytes over standard WAVEFORMATEX due to padding. The +standard version uses tight packing, but for compiler compatibility we're not doing that with ours. */ -#if (defined(_MSC_VER) && !defined(_WAVEFORMATEXTENSIBLE_)) || defined(__DMC__) typedef struct { - WAVEFORMATEX Format; + WORD wFormatTag; + WORD nChannels; + DWORD nSamplesPerSec; + DWORD nAvgBytesPerSec; + WORD nBlockAlign; + WORD wBitsPerSample; + WORD cbSize; +} MA_WAVEFORMATEX; + +typedef struct +{ + WORD wFormatTag; + WORD nChannels; + DWORD nSamplesPerSec; + DWORD nAvgBytesPerSec; + WORD nBlockAlign; + WORD wBitsPerSample; + WORD cbSize; union { WORD wValidBitsPerSample; @@ -19709,13 +19922,18 @@ typedef struct } Samples; DWORD dwChannelMask; GUID SubFormat; -} WAVEFORMATEXTENSIBLE; -#endif +} MA_WAVEFORMATEXTENSIBLE; + + #ifndef WAVE_FORMAT_EXTENSIBLE #define WAVE_FORMAT_EXTENSIBLE 0xFFFE #endif +#ifndef WAVE_FORMAT_PCM +#define WAVE_FORMAT_PCM 1 +#endif + #ifndef WAVE_FORMAT_IEEE_FLOAT #define WAVE_FORMAT_IEEE_FLOAT 0x0003 #endif @@ -19829,21 +20047,21 @@ static MA_INLINE ma_bool32 ma_is_guid_null(const void* guid) return ma_is_guid_equal(guid, &nullguid); } -static ma_format ma_format_from_WAVEFORMATEX(const WAVEFORMATEX* pWF) +static ma_format ma_format_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF) { MA_ASSERT(pWF != NULL); if (pWF->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { - const WAVEFORMATEXTENSIBLE* pWFEX = (const WAVEFORMATEXTENSIBLE*)pWF; + const MA_WAVEFORMATEXTENSIBLE* pWFEX = (const MA_WAVEFORMATEXTENSIBLE*)pWF; if (ma_is_guid_equal(&pWFEX->SubFormat, &MA_GUID_KSDATAFORMAT_SUBTYPE_PCM)) { if (pWFEX->Samples.wValidBitsPerSample == 32) { return ma_format_s32; } if (pWFEX->Samples.wValidBitsPerSample == 24) { - if (pWFEX->Format.wBitsPerSample == 32) { + if (pWFEX->wBitsPerSample == 32) { return ma_format_s32; } - if (pWFEX->Format.wBitsPerSample == 24) { + if (pWFEX->wBitsPerSample == 24) { return ma_format_s24; } } @@ -19951,7 +20169,7 @@ typedef struct #endif /* Some compilers don't define PropVariantInit(). We just do this ourselves since it's just a memset(). */ -static MA_INLINE void ma_PropVariantInit(PROPVARIANT* pProp) +static MA_INLINE void ma_PropVariantInit(MA_PROPVARIANT* pProp) { MA_ZERO_OBJECT(pProp); } @@ -19977,17 +20195,9 @@ static const IID MA_IID_DEVINTERFACE_AUDIO_CAPTURE = {0x2EEF81BE, static const IID MA_IID_IActivateAudioInterfaceCompletionHandler = {0x41D949AB, 0x9862, 0x444A, {0x80, 0xF6, 0xC2, 0x61, 0x33, 0x4D, 0xA5, 0xEB}}; /* 41D949AB-9862-444A-80F6-C261334DA5EB */ #endif -static const IID MA_CLSID_MMDeviceEnumerator_Instance = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */ -static const IID MA_IID_IMMDeviceEnumerator_Instance = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */ -#ifdef __cplusplus -#define MA_CLSID_MMDeviceEnumerator MA_CLSID_MMDeviceEnumerator_Instance -#define MA_IID_IMMDeviceEnumerator MA_IID_IMMDeviceEnumerator_Instance -#else -#define MA_CLSID_MMDeviceEnumerator &MA_CLSID_MMDeviceEnumerator_Instance -#define MA_IID_IMMDeviceEnumerator &MA_IID_IMMDeviceEnumerator_Instance -#endif +static const IID MA_CLSID_MMDeviceEnumerator = {0xBCDE0395, 0xE52F, 0x467C, {0x8E, 0x3D, 0xC4, 0x57, 0x92, 0x91, 0x69, 0x2E}}; /* BCDE0395-E52F-467C-8E3D-C4579291692E = __uuidof(MMDeviceEnumerator) */ +static const IID MA_IID_IMMDeviceEnumerator = {0xA95664D2, 0x9614, 0x4F35, {0xA7, 0x46, 0xDE, 0x8D, 0xB6, 0x36, 0x17, 0xE6}}; /* A95664D2-9614-4F35-A746-DE8DB63617E6 = __uuidof(IMMDeviceEnumerator) */ -typedef struct ma_IUnknown ma_IUnknown; #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) #define MA_MM_DEVICE_STATE_ACTIVE 1 #define MA_MM_DEVICE_STATE_DISABLED 2 @@ -20084,11 +20294,11 @@ static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) ULONG (STDMETHODCALLTYPE * Release) (ma_IMMNotificationClient* pThis); /* IMMNotificationClient */ - HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState); - HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID); - HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID); - HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID); - HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key); + HRESULT (STDMETHODCALLTYPE * OnDeviceStateChanged) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState); + HRESULT (STDMETHODCALLTYPE * OnDeviceAdded) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID); + HRESULT (STDMETHODCALLTYPE * OnDeviceRemoved) (ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID); + HRESULT (STDMETHODCALLTYPE * OnDefaultDeviceChanged)(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID); + HRESULT (STDMETHODCALLTYPE * OnPropertyValueChanged)(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key); } ma_IMMNotificationClientVtbl; /* IMMDeviceEnumerator */ @@ -20102,7 +20312,7 @@ static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) /* IMMDeviceEnumerator */ HRESULT (STDMETHODCALLTYPE * EnumAudioEndpoints) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices); HRESULT (STDMETHODCALLTYPE * GetDefaultAudioEndpoint) (ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint); - HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice); + HRESULT (STDMETHODCALLTYPE * GetDevice) (ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice); HRESULT (STDMETHODCALLTYPE * RegisterEndpointNotificationCallback) (ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); HRESULT (STDMETHODCALLTYPE * UnregisterEndpointNotificationCallback)(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient); } ma_IMMDeviceEnumeratorVtbl; @@ -20115,7 +20325,7 @@ static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) static MA_INLINE ULONG ma_IMMDeviceEnumerator_Release(ma_IMMDeviceEnumerator* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IMMDeviceEnumerator_EnumAudioEndpoints(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, DWORD dwStateMask, ma_IMMDeviceCollection** ppDevices) { return pThis->lpVtbl->EnumAudioEndpoints(pThis, dataFlow, dwStateMask, ppDevices); } static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDefaultAudioEndpoint(ma_IMMDeviceEnumerator* pThis, ma_EDataFlow dataFlow, ma_ERole role, ma_IMMDevice** ppEndpoint) { return pThis->lpVtbl->GetDefaultAudioEndpoint(pThis, dataFlow, role, ppEndpoint); } - static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, LPCWSTR pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); } + static MA_INLINE HRESULT ma_IMMDeviceEnumerator_GetDevice(ma_IMMDeviceEnumerator* pThis, const WCHAR* pID, ma_IMMDevice** ppDevice) { return pThis->lpVtbl->GetDevice(pThis, pID, ppDevice); } static MA_INLINE HRESULT ma_IMMDeviceEnumerator_RegisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->RegisterEndpointNotificationCallback(pThis, pClient); } static MA_INLINE HRESULT ma_IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(ma_IMMDeviceEnumerator* pThis, ma_IMMNotificationClient* pClient) { return pThis->lpVtbl->UnregisterEndpointNotificationCallback(pThis, pClient); } @@ -20152,9 +20362,9 @@ static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) ULONG (STDMETHODCALLTYPE * Release) (ma_IMMDevice* pThis); /* IMMDevice */ - HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, PROPVARIANT* pActivationParams, void** ppInterface); + HRESULT (STDMETHODCALLTYPE * Activate) (ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface); HRESULT (STDMETHODCALLTYPE * OpenPropertyStore)(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties); - HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, LPWSTR *pID); + HRESULT (STDMETHODCALLTYPE * GetId) (ma_IMMDevice* pThis, WCHAR** pID); HRESULT (STDMETHODCALLTYPE * GetState) (ma_IMMDevice* pThis, DWORD *pState); } ma_IMMDeviceVtbl; struct ma_IMMDevice @@ -20164,9 +20374,9 @@ static MA_INLINE ULONG ma_IUnknown_Release(ma_IUnknown* pThis) static MA_INLINE HRESULT ma_IMMDevice_QueryInterface(ma_IMMDevice* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IMMDevice_AddRef(ma_IMMDevice* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IMMDevice_Release(ma_IMMDevice* pThis) { return pThis->lpVtbl->Release(pThis); } - static MA_INLINE HRESULT ma_IMMDevice_Activate(ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, PROPVARIANT* pActivationParams, void** ppInterface) { return pThis->lpVtbl->Activate(pThis, iid, dwClsCtx, pActivationParams, ppInterface); } + static MA_INLINE HRESULT ma_IMMDevice_Activate(ma_IMMDevice* pThis, const IID* const iid, DWORD dwClsCtx, MA_PROPVARIANT* pActivationParams, void** ppInterface) { return pThis->lpVtbl->Activate(pThis, iid, dwClsCtx, pActivationParams, ppInterface); } static MA_INLINE HRESULT ma_IMMDevice_OpenPropertyStore(ma_IMMDevice* pThis, DWORD stgmAccess, ma_IPropertyStore** ppProperties) { return pThis->lpVtbl->OpenPropertyStore(pThis, stgmAccess, ppProperties); } - static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, LPWSTR *pID) { return pThis->lpVtbl->GetId(pThis, pID); } + static MA_INLINE HRESULT ma_IMMDevice_GetId(ma_IMMDevice* pThis, WCHAR** pID) { return pThis->lpVtbl->GetId(pThis, pID); } static MA_INLINE HRESULT ma_IMMDevice_GetState(ma_IMMDevice* pThis, DWORD *pState) { return pThis->lpVtbl->GetState(pThis, pState); } #else /* IActivateAudioInterfaceAsyncOperation */ @@ -20201,8 +20411,8 @@ typedef struct /* IPropertyStore */ HRESULT (STDMETHODCALLTYPE * GetCount)(ma_IPropertyStore* pThis, DWORD* pPropCount); HRESULT (STDMETHODCALLTYPE * GetAt) (ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey); - HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar); - HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar); + HRESULT (STDMETHODCALLTYPE * GetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar); + HRESULT (STDMETHODCALLTYPE * SetValue)(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar); HRESULT (STDMETHODCALLTYPE * Commit) (ma_IPropertyStore* pThis); } ma_IPropertyStoreVtbl; struct ma_IPropertyStore @@ -20214,8 +20424,8 @@ static MA_INLINE ULONG ma_IPropertyStore_AddRef(ma_IPropertyStore* pThis) static MA_INLINE ULONG ma_IPropertyStore_Release(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IPropertyStore_GetCount(ma_IPropertyStore* pThis, DWORD* pPropCount) { return pThis->lpVtbl->GetCount(pThis, pPropCount); } static MA_INLINE HRESULT ma_IPropertyStore_GetAt(ma_IPropertyStore* pThis, DWORD propIndex, PROPERTYKEY* pPropKey) { return pThis->lpVtbl->GetAt(pThis, propIndex, pPropKey); } -static MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); } -static MA_INLINE HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); } +static MA_INLINE HRESULT ma_IPropertyStore_GetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, MA_PROPVARIANT* pPropVar) { return pThis->lpVtbl->GetValue(pThis, pKey, pPropVar); } +static MA_INLINE HRESULT ma_IPropertyStore_SetValue(ma_IPropertyStore* pThis, const PROPERTYKEY* const pKey, const MA_PROPVARIANT* const pPropVar) { return pThis->lpVtbl->SetValue(pThis, pKey, pPropVar); } static MA_INLINE HRESULT ma_IPropertyStore_Commit(ma_IPropertyStore* pThis) { return pThis->lpVtbl->Commit(pThis); } @@ -20228,12 +20438,12 @@ typedef struct ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient* pThis); /* IAudioClient */ - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); + HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames); HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency); HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames); - HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch); - HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat); + HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); + HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat); HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient* pThis); HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient* pThis); @@ -20248,12 +20458,12 @@ struct ma_IAudioClient static MA_INLINE HRESULT ma_IAudioClient_QueryInterface(ma_IAudioClient* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IAudioClient_AddRef(ma_IAudioClient* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IAudioClient_Release(ma_IAudioClient* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioClient_Initialize(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } +static MA_INLINE HRESULT ma_IAudioClient_Initialize(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } static MA_INLINE HRESULT ma_IAudioClient_GetBufferSize(ma_IAudioClient* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } static MA_INLINE HRESULT ma_IAudioClient_GetStreamLatency(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } static MA_INLINE HRESULT ma_IAudioClient_GetCurrentPadding(ma_IAudioClient* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } -static MA_INLINE HRESULT ma_IAudioClient_IsFormatSupported(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } -static MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } +static MA_INLINE HRESULT ma_IAudioClient_IsFormatSupported(ma_IAudioClient* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } +static MA_INLINE HRESULT ma_IAudioClient_GetMixFormat(ma_IAudioClient* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } static MA_INLINE HRESULT ma_IAudioClient_GetDevicePeriod(ma_IAudioClient* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } static MA_INLINE HRESULT ma_IAudioClient_Start(ma_IAudioClient* pThis) { return pThis->lpVtbl->Start(pThis); } static MA_INLINE HRESULT ma_IAudioClient_Stop(ma_IAudioClient* pThis) { return pThis->lpVtbl->Stop(pThis); } @@ -20270,12 +20480,12 @@ typedef struct ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient2* pThis); /* IAudioClient */ - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); + HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames); HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency); HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames); - HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch); - HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat); + HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); + HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat); HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient2* pThis); HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient2* pThis); @@ -20286,7 +20496,7 @@ typedef struct /* IAudioClient2 */ HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties); - HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); + HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); } ma_IAudioClient2Vtbl; struct ma_IAudioClient2 { @@ -20295,12 +20505,12 @@ struct ma_IAudioClient2 static MA_INLINE HRESULT ma_IAudioClient2_QueryInterface(ma_IAudioClient2* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IAudioClient2_AddRef(ma_IAudioClient2* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IAudioClient2_Release(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioClient2_Initialize(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } +static MA_INLINE HRESULT ma_IAudioClient2_Initialize(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSize(ma_IAudioClient2* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } static MA_INLINE HRESULT ma_IAudioClient2_GetStreamLatency(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } static MA_INLINE HRESULT ma_IAudioClient2_GetCurrentPadding(ma_IAudioClient2* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } -static MA_INLINE HRESULT ma_IAudioClient2_IsFormatSupported(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } -static MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } +static MA_INLINE HRESULT ma_IAudioClient2_IsFormatSupported(ma_IAudioClient2* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } +static MA_INLINE HRESULT ma_IAudioClient2_GetMixFormat(ma_IAudioClient2* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } static MA_INLINE HRESULT ma_IAudioClient2_GetDevicePeriod(ma_IAudioClient2* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } static MA_INLINE HRESULT ma_IAudioClient2_Start(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Start(pThis); } static MA_INLINE HRESULT ma_IAudioClient2_Stop(ma_IAudioClient2* pThis) { return pThis->lpVtbl->Stop(pThis); } @@ -20309,7 +20519,7 @@ static MA_INLINE HRESULT ma_IAudioClient2_SetEventHandle(ma_IAudioClient2* pThis static MA_INLINE HRESULT ma_IAudioClient2_GetService(ma_IAudioClient2* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } static MA_INLINE HRESULT ma_IAudioClient2_IsOffloadCapable(ma_IAudioClient2* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } static MA_INLINE HRESULT ma_IAudioClient2_SetClientProperties(ma_IAudioClient2* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } -static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSizeLimits(ma_IAudioClient2* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } +static MA_INLINE HRESULT ma_IAudioClient2_GetBufferSizeLimits(ma_IAudioClient2* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } /* IAudioClient3 */ @@ -20321,12 +20531,12 @@ typedef struct ULONG (STDMETHODCALLTYPE * Release) (ma_IAudioClient3* pThis); /* IAudioClient */ - HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); + HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); HRESULT (STDMETHODCALLTYPE * GetBufferSize) (ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames); HRESULT (STDMETHODCALLTYPE * GetStreamLatency) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency); HRESULT (STDMETHODCALLTYPE * GetCurrentPadding)(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames); - HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch); - HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat); + HRESULT (STDMETHODCALLTYPE * IsFormatSupported)(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch); + HRESULT (STDMETHODCALLTYPE * GetMixFormat) (ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat); HRESULT (STDMETHODCALLTYPE * GetDevicePeriod) (ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod); HRESULT (STDMETHODCALLTYPE * Start) (ma_IAudioClient3* pThis); HRESULT (STDMETHODCALLTYPE * Stop) (ma_IAudioClient3* pThis); @@ -20337,12 +20547,12 @@ typedef struct /* IAudioClient2 */ HRESULT (STDMETHODCALLTYPE * IsOffloadCapable) (ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable); HRESULT (STDMETHODCALLTYPE * SetClientProperties)(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties); - HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); + HRESULT (STDMETHODCALLTYPE * GetBufferSizeLimits)(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration); /* IAudioClient3 */ - HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames); - HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames); - HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); + HRESULT (STDMETHODCALLTYPE * GetSharedModeEnginePeriod) (ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames); + HRESULT (STDMETHODCALLTYPE * GetCurrentSharedModeEnginePeriod)(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames); + HRESULT (STDMETHODCALLTYPE * InitializeSharedAudioStream) (ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid); } ma_IAudioClient3Vtbl; struct ma_IAudioClient3 { @@ -20351,12 +20561,12 @@ struct ma_IAudioClient3 static MA_INLINE HRESULT ma_IAudioClient3_QueryInterface(ma_IAudioClient3* pThis, const IID* const riid, void** ppObject) { return pThis->lpVtbl->QueryInterface(pThis, riid, ppObject); } static MA_INLINE ULONG ma_IAudioClient3_AddRef(ma_IAudioClient3* pThis) { return pThis->lpVtbl->AddRef(pThis); } static MA_INLINE ULONG ma_IAudioClient3_Release(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Release(pThis); } -static MA_INLINE HRESULT ma_IAudioClient3_Initialize(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } +static MA_INLINE HRESULT ma_IAudioClient3_Initialize(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, DWORD streamFlags, MA_REFERENCE_TIME bufferDuration, MA_REFERENCE_TIME periodicity, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGuid) { return pThis->lpVtbl->Initialize(pThis, shareMode, streamFlags, bufferDuration, periodicity, pFormat, pAudioSessionGuid); } static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSize(ma_IAudioClient3* pThis, ma_uint32* pNumBufferFrames) { return pThis->lpVtbl->GetBufferSize(pThis, pNumBufferFrames); } static MA_INLINE HRESULT ma_IAudioClient3_GetStreamLatency(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pLatency) { return pThis->lpVtbl->GetStreamLatency(pThis, pLatency); } static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentPadding(ma_IAudioClient3* pThis, ma_uint32* pNumPaddingFrames) { return pThis->lpVtbl->GetCurrentPadding(pThis, pNumPaddingFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_IsFormatSupported(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const WAVEFORMATEX* pFormat, WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } -static MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } +static MA_INLINE HRESULT ma_IAudioClient3_IsFormatSupported(ma_IAudioClient3* pThis, MA_AUDCLNT_SHAREMODE shareMode, const MA_WAVEFORMATEX* pFormat, MA_WAVEFORMATEX** ppClosestMatch) { return pThis->lpVtbl->IsFormatSupported(pThis, shareMode, pFormat, ppClosestMatch); } +static MA_INLINE HRESULT ma_IAudioClient3_GetMixFormat(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppDeviceFormat) { return pThis->lpVtbl->GetMixFormat(pThis, ppDeviceFormat); } static MA_INLINE HRESULT ma_IAudioClient3_GetDevicePeriod(ma_IAudioClient3* pThis, MA_REFERENCE_TIME* pDefaultDevicePeriod, MA_REFERENCE_TIME* pMinimumDevicePeriod) { return pThis->lpVtbl->GetDevicePeriod(pThis, pDefaultDevicePeriod, pMinimumDevicePeriod); } static MA_INLINE HRESULT ma_IAudioClient3_Start(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Start(pThis); } static MA_INLINE HRESULT ma_IAudioClient3_Stop(ma_IAudioClient3* pThis) { return pThis->lpVtbl->Stop(pThis); } @@ -20365,10 +20575,10 @@ static MA_INLINE HRESULT ma_IAudioClient3_SetEventHandle(ma_IAudioClient3* pThis static MA_INLINE HRESULT ma_IAudioClient3_GetService(ma_IAudioClient3* pThis, const IID* const riid, void** pp) { return pThis->lpVtbl->GetService(pThis, riid, pp); } static MA_INLINE HRESULT ma_IAudioClient3_IsOffloadCapable(ma_IAudioClient3* pThis, MA_AUDIO_STREAM_CATEGORY category, BOOL* pOffloadCapable) { return pThis->lpVtbl->IsOffloadCapable(pThis, category, pOffloadCapable); } static MA_INLINE HRESULT ma_IAudioClient3_SetClientProperties(ma_IAudioClient3* pThis, const ma_AudioClientProperties* pProperties) { return pThis->lpVtbl->SetClientProperties(pThis, pProperties); } -static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSizeLimits(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } -static MA_INLINE HRESULT ma_IAudioClient3_GetSharedModeEnginePeriod(ma_IAudioClient3* pThis, const WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); } -static MA_INLINE HRESULT ma_IAudioClient3_InitializeSharedAudioStream(ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); } +static MA_INLINE HRESULT ma_IAudioClient3_GetBufferSizeLimits(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, BOOL eventDriven, MA_REFERENCE_TIME* pMinBufferDuration, MA_REFERENCE_TIME* pMaxBufferDuration) { return pThis->lpVtbl->GetBufferSizeLimits(pThis, pFormat, eventDriven, pMinBufferDuration, pMaxBufferDuration); } +static MA_INLINE HRESULT ma_IAudioClient3_GetSharedModeEnginePeriod(ma_IAudioClient3* pThis, const MA_WAVEFORMATEX* pFormat, ma_uint32* pDefaultPeriodInFrames, ma_uint32* pFundamentalPeriodInFrames, ma_uint32* pMinPeriodInFrames, ma_uint32* pMaxPeriodInFrames) { return pThis->lpVtbl->GetSharedModeEnginePeriod(pThis, pFormat, pDefaultPeriodInFrames, pFundamentalPeriodInFrames, pMinPeriodInFrames, pMaxPeriodInFrames); } +static MA_INLINE HRESULT ma_IAudioClient3_GetCurrentSharedModeEnginePeriod(ma_IAudioClient3* pThis, MA_WAVEFORMATEX** ppFormat, ma_uint32* pCurrentPeriodInFrames) { return pThis->lpVtbl->GetCurrentSharedModeEnginePeriod(pThis, ppFormat, pCurrentPeriodInFrames); } +static MA_INLINE HRESULT ma_IAudioClient3_InitializeSharedAudioStream(ma_IAudioClient3* pThis, DWORD streamFlags, ma_uint32 periodInFrames, const MA_WAVEFORMATEX* pFormat, const GUID* pAudioSessionGUID) { return pThis->lpVtbl->InitializeSharedAudioStream(pThis, streamFlags, periodInFrames, pFormat, pAudioSessionGUID); } /* IAudioRenderClient */ @@ -20420,11 +20630,11 @@ static MA_INLINE HRESULT ma_IAudioCaptureClient_GetNextPacketSize(ma_IAudioCaptu #if defined(MA_WIN32_UWP) /* mmdevapi Functions */ -typedef HRESULT (WINAPI * MA_PFN_ActivateAudioInterfaceAsync)(LPCWSTR deviceInterfacePath, const IID* riid, PROPVARIANT *activationParams, ma_IActivateAudioInterfaceCompletionHandler *completionHandler, ma_IActivateAudioInterfaceAsyncOperation **activationOperation); +typedef HRESULT (WINAPI * MA_PFN_ActivateAudioInterfaceAsync)(const wchar_t* deviceInterfacePath, const IID* riid, MA_PROPVARIANT* activationParams, ma_IActivateAudioInterfaceCompletionHandler* completionHandler, ma_IActivateAudioInterfaceAsyncOperation** activationOperation); #endif /* Avrt Functions */ -typedef HANDLE (WINAPI * MA_PFN_AvSetMmThreadCharacteristicsW)(LPCWSTR TaskName, LPDWORD TaskIndex); +typedef HANDLE (WINAPI * MA_PFN_AvSetMmThreadCharacteristicsA)(const char* TaskName, DWORD* TaskIndex); typedef BOOL (WINAPI * MA_PFN_AvRevertMmThreadCharacteristics)(HANDLE AvrtHandle); #if !defined(MA_WIN32_DESKTOP) && !defined(MA_WIN32_GDK) @@ -20518,7 +20728,7 @@ static void ma_completion_handler_uwp_uninit(ma_completion_handler_uwp* pHandler static void ma_completion_handler_uwp_wait(ma_completion_handler_uwp* pHandler) { - WaitForSingleObject(pHandler->hEvent, INFINITE); + WaitForSingleObject((HANDLE)pHandler->hEvent, INFINITE); } #endif /* !MA_WIN32_DESKTOP */ @@ -20556,7 +20766,7 @@ static ULONG STDMETHODCALLTYPE ma_IMMNotificationClient_Release(ma_IMMNotificati return (ULONG)newRefCount; } -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, DWORD dwNewState) +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, DWORD dwNewState) { ma_bool32 isThisDevice = MA_FALSE; ma_bool32 isCapture = MA_FALSE; @@ -20572,14 +20782,14 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(m */ if (pThis->pDevice->wasapi.allowCaptureAutoStreamRouting && (pThis->pDevice->type == ma_device_type_capture || pThis->pDevice->type == ma_device_type_duplex || pThis->pDevice->type == ma_device_type_loopback)) { isCapture = MA_TRUE; - if (wcscmp(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) { + if (ma_strcmp_WCHAR(pThis->pDevice->capture.id.wasapi, pDeviceID) == 0) { isThisDevice = MA_TRUE; } } if (pThis->pDevice->wasapi.allowPlaybackAutoStreamRouting && (pThis->pDevice->type == ma_device_type_playback || pThis->pDevice->type == ma_device_type_duplex)) { isPlayback = MA_TRUE; - if (wcscmp(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) { + if (ma_strcmp_WCHAR(pThis->pDevice->playback.id.wasapi, pDeviceID) == 0) { isThisDevice = MA_TRUE; } } @@ -20640,7 +20850,7 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceStateChanged(m return S_OK; } -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID) +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID) { #ifdef MA_DEBUG_OUTPUT /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceAdded(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ @@ -20652,7 +20862,7 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceAdded(ma_IMMNo return S_OK; } -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID) +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID) { #ifdef MA_DEBUG_OUTPUT /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDeviceRemoved(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ @@ -20664,19 +20874,15 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDeviceRemoved(ma_IMM return S_OK; } -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, LPCWSTR pDefaultDeviceID) +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged(ma_IMMNotificationClient* pThis, ma_EDataFlow dataFlow, ma_ERole role, const WCHAR* pDefaultDeviceID) { #ifdef MA_DEBUG_OUTPUT /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnDefaultDeviceChanged(dataFlow=%d, role=%d, pDefaultDeviceID=%S)\n", dataFlow, role, (pDefaultDeviceID != NULL) ? pDefaultDeviceID : L"(NULL)");*/ #endif - /* We only ever use the eConsole role in miniaudio. */ - if (role != ma_eConsole) { - ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "[WASAPI] Stream rerouting: role != eConsole\n"); - return S_OK; - } + (void)role; - /* We only care about devices with the same data flow and role as the current device. */ + /* We only care about devices with the same data flow as the current device. */ if ((pThis->pDevice->type == ma_device_type_playback && dataFlow != ma_eRender) || (pThis->pDevice->type == ma_device_type_capture && dataFlow != ma_eCapture) || (pThis->pDevice->type == ma_device_type_loopback && dataFlow != ma_eRender)) { @@ -20771,7 +20977,7 @@ static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnDefaultDeviceChanged return S_OK; } -static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, LPCWSTR pDeviceID, const PROPERTYKEY key) +static HRESULT STDMETHODCALLTYPE ma_IMMNotificationClient_OnPropertyValueChanged(ma_IMMNotificationClient* pThis, const WCHAR* pDeviceID, const PROPERTYKEY key) { #ifdef MA_DEBUG_OUTPUT /*ma_log_postf(ma_device_get_log(pThis->pDevice), MA_LOG_LEVEL_DEBUG, "IMMNotificationClient_OnPropertyValueChanged(pDeviceID=%S)\n", (pDeviceID != NULL) ? pDeviceID : L"(NULL)");*/ @@ -20795,12 +21001,13 @@ static ma_IMMNotificationClientVtbl g_maNotificationCientVtbl = { }; #endif /* MA_WIN32_DESKTOP */ -static LPCWSTR ma_to_usage_string__wasapi(ma_wasapi_usage usage) +static const char* ma_to_usage_string__wasapi(ma_wasapi_usage usage) { - switch (usage) { + switch (usage) + { case ma_wasapi_usage_default: return NULL; - case ma_wasapi_usage_games: return L"Games"; - case ma_wasapi_usage_pro_audio: return L"Pro Audio"; + case ma_wasapi_usage_games: return "Games"; + case ma_wasapi_usage_pro_audio: return "Pro Audio"; default: break; } @@ -20998,7 +21205,7 @@ static ma_result ma_device_release_IAudioClient_service__wasapi(ma_device* pDevi #endif -static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const WAVEFORMATEX* pWF, ma_share_mode shareMode, ma_device_info* pInfo) +static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const MA_WAVEFORMATEX* pWF, ma_share_mode shareMode, ma_device_info* pInfo) { MA_ASSERT(pWF != NULL); MA_ASSERT(pInfo != NULL); @@ -21017,13 +21224,13 @@ static void ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(const WAV static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context* pContext, /*ma_IMMDevice**/void* pMMDevice, ma_IAudioClient* pAudioClient, ma_device_info* pInfo) { HRESULT hr; - WAVEFORMATEX* pWF = NULL; + MA_WAVEFORMATEX* pWF = NULL; MA_ASSERT(pAudioClient != NULL); MA_ASSERT(pInfo != NULL); /* Shared Mode. We use GetMixFormat() here. */ - hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (WAVEFORMATEX**)&pWF); + hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pAudioClient, (MA_WAVEFORMATEX**)&pWF); if (SUCCEEDED(hr)) { ma_add_native_data_format_to_device_info_from_WAVEFORMATEX(pWF, ma_share_mode_shared, pInfo); } else { @@ -21046,12 +21253,12 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context */ hr = ma_IMMDevice_OpenPropertyStore((ma_IMMDevice*)pMMDevice, STGM_READ, &pProperties); if (SUCCEEDED(hr)) { - PROPVARIANT var; + MA_PROPVARIANT var; ma_PropVariantInit(&var); hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_AudioEngine_DeviceFormat, &var); if (SUCCEEDED(hr)) { - pWF = (WAVEFORMATEX*)var.blob.pBlobData; + pWF = (MA_WAVEFORMATEX*)var.blob.pBlobData; /* In my testing, the format returned by PKEY_AudioEngine_DeviceFormat is suitable for exclusive mode so we check this format @@ -21068,7 +21275,7 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context */ ma_uint32 channels = pWF->nChannels; ma_channel defaultChannelMap[MA_MAX_CHANNELS]; - WAVEFORMATEXTENSIBLE wf; + MA_WAVEFORMATEXTENSIBLE wf; ma_bool32 found; ma_uint32 iFormat; @@ -21080,9 +21287,9 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context ma_channel_map_init_standard(ma_standard_channel_map_microsoft, defaultChannelMap, ma_countof(defaultChannelMap), channels); MA_ZERO_OBJECT(&wf); - wf.Format.cbSize = sizeof(wf); - wf.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - wf.Format.nChannels = (WORD)channels; + wf.cbSize = sizeof(wf); + wf.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + wf.nChannels = (WORD)channels; wf.dwChannelMask = ma_channel_map_to_channel_mask__win32(defaultChannelMap, channels); found = MA_FALSE; @@ -21090,10 +21297,10 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context ma_format format = g_maFormatPriorities[iFormat]; ma_uint32 iSampleRate; - wf.Format.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); - wf.Format.nBlockAlign = (WORD)(wf.Format.nChannels * wf.Format.wBitsPerSample / 8); - wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec; - wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.Format.wBitsPerSample; + wf.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); + wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); + wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; + wf.Samples.wValidBitsPerSample = /*(format == ma_format_s24_32) ? 24 :*/ wf.wBitsPerSample; if (format == ma_format_f32) { wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; } else { @@ -21101,11 +21308,11 @@ static ma_result ma_context_get_device_info_from_IAudioClient__wasapi(ma_context } for (iSampleRate = 0; iSampleRate < ma_countof(g_maStandardSampleRatePriorities); ++iSampleRate) { - wf.Format.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate]; + wf.nSamplesPerSec = g_maStandardSampleRatePriorities[iSampleRate]; - hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (WAVEFORMATEX*)&wf, NULL); + hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, (MA_WAVEFORMATEX*)&wf, NULL); if (SUCCEEDED(hr)) { - ma_add_native_data_format_to_device_info_from_WAVEFORMATEX((WAVEFORMATEX*)&wf, ma_share_mode_exclusive, pInfo); + ma_add_native_data_format_to_device_info_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf, ma_share_mode_exclusive, pInfo); found = MA_TRUE; break; } @@ -21163,7 +21370,7 @@ static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pCont *ppDeviceEnumerator = NULL; /* Safety. */ - hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if (FAILED(hr)) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); return ma_result_from_HRESULT(hr); @@ -21174,11 +21381,11 @@ static ma_result ma_context_create_IMMDeviceEnumerator__wasapi(ma_context* pCont return MA_SUCCESS; } -static LPWSTR ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType) +static WCHAR* ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi(ma_context* pContext, ma_IMMDeviceEnumerator* pDeviceEnumerator, ma_device_type deviceType) { HRESULT hr; ma_IMMDevice* pMMDefaultDevice = NULL; - LPWSTR pDefaultDeviceID = NULL; + WCHAR* pDefaultDeviceID = NULL; ma_EDataFlow dataFlow; ma_ERole role; @@ -21210,11 +21417,11 @@ static LPWSTR ma_context_get_default_device_id_from_IMMDeviceEnumerator__wasapi( return pDefaultDeviceID; } -static LPWSTR ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType) /* Free the returned pointer with ma_CoTaskMemFree() */ +static WCHAR* ma_context_get_default_device_id__wasapi(ma_context* pContext, ma_device_type deviceType) /* Free the returned pointer with ma_CoTaskMemFree() */ { ma_result result; ma_IMMDeviceEnumerator* pDeviceEnumerator; - LPWSTR pDefaultDeviceID = NULL; + WCHAR* pDefaultDeviceID = NULL; MA_ASSERT(pContext != NULL); @@ -21237,7 +21444,7 @@ static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device MA_ASSERT(pContext != NULL); MA_ASSERT(ppMMDevice != NULL); - hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if (FAILED(hr)) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create IMMDeviceEnumerator.\n"); return ma_result_from_HRESULT(hr); @@ -21260,14 +21467,14 @@ static ma_result ma_context_get_MMDevice__wasapi(ma_context* pContext, ma_device static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, ma_device_id* pDeviceID) { - LPWSTR pDeviceIDString; + WCHAR* pDeviceIDString; HRESULT hr; MA_ASSERT(pDeviceID != NULL); hr = ma_IMMDevice_GetId(pMMDevice, &pDeviceIDString); if (SUCCEEDED(hr)) { - size_t idlen = wcslen(pDeviceIDString); + size_t idlen = ma_strlen_WCHAR(pDeviceIDString); if (idlen+1 > ma_countof(pDeviceID->wasapi)) { ma_CoTaskMemFree(pContext, pDeviceIDString); MA_ASSERT(MA_FALSE); /* NOTE: If this is triggered, please report it. It means the format of the ID must haved change and is too long to fit in our fixed sized buffer. */ @@ -21285,7 +21492,7 @@ static ma_result ma_context_get_device_id_from_MMDevice__wasapi(ma_context* pCon return MA_ERROR; } -static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, LPWSTR pDefaultDeviceID, ma_bool32 onlySimpleInfo, ma_device_info* pInfo) +static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pContext, ma_IMMDevice* pMMDevice, WCHAR* pDefaultDeviceID, ma_bool32 onlySimpleInfo, ma_device_info* pInfo) { ma_result result; HRESULT hr; @@ -21298,7 +21505,7 @@ static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pC result = ma_context_get_device_id_from_MMDevice__wasapi(pContext, pMMDevice, &pInfo->id); if (result == MA_SUCCESS) { if (pDefaultDeviceID != NULL) { - if (wcscmp(pInfo->id.wasapi, pDefaultDeviceID) == 0) { + if (ma_strcmp_WCHAR(pInfo->id.wasapi, pDefaultDeviceID) == 0) { pInfo->isDefault = MA_TRUE; } } @@ -21309,7 +21516,7 @@ static ma_result ma_context_get_device_info_from_MMDevice__wasapi(ma_context* pC ma_IPropertyStore *pProperties; hr = ma_IMMDevice_OpenPropertyStore(pMMDevice, STGM_READ, &pProperties); if (SUCCEEDED(hr)) { - PROPVARIANT var; + MA_PROPVARIANT var; ma_PropVariantInit(&var); hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &var); @@ -21346,7 +21553,7 @@ static ma_result ma_context_enumerate_devices_by_type__wasapi(ma_context* pConte UINT deviceCount; HRESULT hr; ma_uint32 iDevice; - LPWSTR pDefaultDeviceID = NULL; + WCHAR* pDefaultDeviceID = NULL; ma_IMMDeviceCollection* pDeviceCollection = NULL; MA_ASSERT(pContext != NULL); @@ -21400,7 +21607,7 @@ done: return result; } -static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice) +static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IMMDevice** ppMMDevice) { ma_result result; HRESULT hr; @@ -21422,12 +21629,12 @@ static ma_result ma_context_get_IAudioClient_Desktop__wasapi(ma_context* pContex return MA_SUCCESS; } #else -static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface) +static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, ma_device_type deviceType, const ma_device_id* pDeviceID, MA_PROPVARIANT* pActivationParams, ma_IAudioClient** ppAudioClient, ma_IUnknown** ppActivatedInterface) { ma_IActivateAudioInterfaceAsyncOperation *pAsyncOp = NULL; ma_completion_handler_uwp completionHandler; IID iid; - LPOLESTR iidStr; + WCHAR* iidStr; HRESULT hr; ma_result result; HRESULT activateResult; @@ -21437,7 +21644,7 @@ static ma_result ma_context_get_IAudioClient_UWP__wasapi(ma_context* pContext, m MA_ASSERT(ppAudioClient != NULL); if (pDeviceID != NULL) { - iidStr = (LPOLESTR)pDeviceID->wasapi; + iidStr = (WCHAR*)pDeviceID->wasapi; } else { if (deviceType == ma_device_type_capture) { iid = MA_IID_DEVINTERFACE_AUDIO_CAPTURE; @@ -21558,8 +21765,8 @@ static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_de ma_result result; ma_bool32 usingProcessLoopback = MA_FALSE; MA_AUDIOCLIENT_ACTIVATION_PARAMS audioclientActivationParams; - PROPVARIANT activationParams; - PROPVARIANT* pActivationParams = NULL; + MA_PROPVARIANT activationParams; + MA_PROPVARIANT* pActivationParams = NULL; ma_device_id virtualDeviceID; /* Activation parameters specific to loopback mode. Note that process-specific loopback will only work when a default device ID is specified. */ @@ -21574,7 +21781,7 @@ static ma_result ma_context_get_IAudioClient__wasapi(ma_context* pContext, ma_de audioclientActivationParams.ProcessLoopbackParams.TargetProcessId = (DWORD)loopbackProcessID; ma_PropVariantInit(&activationParams); - activationParams.vt = VT_BLOB; + activationParams.vt = MA_VT_BLOB; activationParams.blob.cbSize = sizeof(audioclientActivationParams); activationParams.blob.pBlobData = (BYTE*)&audioclientActivationParams; pActivationParams = &activationParams; @@ -21615,7 +21822,7 @@ static ma_result ma_context_enumerate_devices__wasapi(ma_context* pContext, ma_e HRESULT hr; ma_IMMDeviceEnumerator* pDeviceEnumerator; - hr = ma_CoCreateInstance(pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + hr = ma_CoCreateInstance(pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if (FAILED(hr)) { ma_log_postf(ma_context_get_log(pContext), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); return ma_result_from_HRESULT(hr); @@ -21665,7 +21872,7 @@ static ma_result ma_context_get_device_info__wasapi(ma_context* pContext, ma_dev #if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) ma_result result; ma_IMMDevice* pMMDevice = NULL; - LPWSTR pDefaultDeviceID = NULL; + WCHAR* pDefaultDeviceID = NULL; result = ma_context_get_MMDevice__wasapi(pContext, deviceType, pDeviceID, &pMMDevice); if (result != MA_SUCCESS) { @@ -21750,10 +21957,10 @@ static ma_result ma_device_uninit__wasapi(ma_device* pDevice) } if (pDevice->wasapi.hEventPlayback) { - CloseHandle(pDevice->wasapi.hEventPlayback); + CloseHandle((HANDLE)pDevice->wasapi.hEventPlayback); } if (pDevice->wasapi.hEventCapture) { - CloseHandle(pDevice->wasapi.hEventCapture); + CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); } return MA_SUCCESS; @@ -21802,10 +22009,11 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device DWORD streamFlags = 0; MA_REFERENCE_TIME periodDurationInMicroseconds; ma_bool32 wasInitializedUsingIAudioClient3 = MA_FALSE; - WAVEFORMATEXTENSIBLE wf; + MA_WAVEFORMATEXTENSIBLE wf; ma_WASAPIDeviceInterface* pDeviceInterface = NULL; ma_IAudioClient2* pAudioClient2; ma_uint32 nativeSampleRate; + ma_bool32 usingProcessLoopback = MA_FALSE; MA_ASSERT(pContext != NULL); MA_ASSERT(pData != NULL); @@ -21815,6 +22023,8 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device return MA_INVALID_ARGS; } + usingProcessLoopback = deviceType == ma_device_type_loopback && pData->loopbackProcessID != 0 && pDeviceID == NULL; + pData->pAudioClient = NULL; pData->pRenderClient = NULL; pData->pCaptureClient = NULL; @@ -21864,14 +22074,14 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device ma_IPropertyStore* pStore = NULL; hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pStore); if (SUCCEEDED(hr)) { - PROPVARIANT prop; + MA_PROPVARIANT prop; ma_PropVariantInit(&prop); hr = ma_IPropertyStore_GetValue(pStore, &MA_PKEY_AudioEngine_DeviceFormat, &prop); if (SUCCEEDED(hr)) { - WAVEFORMATEX* pActualFormat = (WAVEFORMATEX*)prop.blob.pBlobData; + MA_WAVEFORMATEX* pActualFormat = (MA_WAVEFORMATEX*)prop.blob.pBlobData; hr = ma_IAudioClient_IsFormatSupported((ma_IAudioClient*)pData->pAudioClient, MA_AUDCLNT_SHAREMODE_EXCLUSIVE, pActualFormat, NULL); if (SUCCEEDED(hr)) { - MA_COPY_MEMORY(&wf, pActualFormat, sizeof(WAVEFORMATEXTENSIBLE)); + MA_COPY_MEMORY(&wf, pActualFormat, sizeof(MA_WAVEFORMATEXTENSIBLE)); } ma_PropVariantClear(pContext, &prop); @@ -21898,12 +22108,47 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device } } else { /* In shared mode we are always using the format reported by the operating system. */ - WAVEFORMATEXTENSIBLE* pNativeFormat = NULL; - hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (WAVEFORMATEX**)&pNativeFormat); + MA_WAVEFORMATEXTENSIBLE* pNativeFormat = NULL; + hr = ma_IAudioClient_GetMixFormat((ma_IAudioClient*)pData->pAudioClient, (MA_WAVEFORMATEX**)&pNativeFormat); if (hr != S_OK) { - result = MA_FORMAT_NOT_SUPPORTED; + /* When using process-specific loopback, GetMixFormat() seems to always fail. */ + if (usingProcessLoopback) { + wf.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; + wf.nChannels = 2; + wf.nSamplesPerSec = 44100; + wf.wBitsPerSample = 32; + wf.nBlockAlign = wf.nChannels * wf.wBitsPerSample / 8; + wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; + wf.cbSize = sizeof(MA_WAVEFORMATEX); + + result = MA_SUCCESS; + } else { + result = MA_FORMAT_NOT_SUPPORTED; + } } else { - MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(wf)); + /* + I've seen cases where cbSize will be set to sizeof(WAVEFORMATEX) even though the structure itself + is given the format tag of WAVE_FORMAT_EXTENSIBLE. If the format tag is WAVE_FORMAT_EXTENSIBLE + want to make sure we copy the whole WAVEFORMATEXTENSIBLE structure. Otherwise we'll have to be + safe and only copy the WAVEFORMATEX part. + */ + if (pNativeFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + MA_COPY_MEMORY(&wf, pNativeFormat, sizeof(MA_WAVEFORMATEXTENSIBLE)); + } else { + /* I've seen a case where cbSize was set to 0. Assume sizeof(WAVEFORMATEX) in this case. */ + size_t cbSize = pNativeFormat->cbSize; + if (cbSize == 0) { + cbSize = sizeof(MA_WAVEFORMATEX); + } + + /* Make sure we don't copy more than the capacity of `wf`. */ + if (cbSize > sizeof(wf)) { + cbSize = sizeof(wf); + } + + MA_COPY_MEMORY(&wf, pNativeFormat, cbSize); + } + result = MA_SUCCESS; } @@ -21922,13 +22167,13 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device Override the native sample rate with the one requested by the caller, but only if we're not using the default sample rate. We'll use WASAPI to perform the sample rate conversion. */ - nativeSampleRate = wf.Format.nSamplesPerSec; + nativeSampleRate = wf.nSamplesPerSec; if (streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) { - wf.Format.nSamplesPerSec = (pData->sampleRateIn != 0) ? pData->sampleRateIn : MA_DEFAULT_SAMPLE_RATE; - wf.Format.nAvgBytesPerSec = wf.Format.nSamplesPerSec * wf.Format.nBlockAlign; + wf.nSamplesPerSec = (pData->sampleRateIn != 0) ? pData->sampleRateIn : MA_DEFAULT_SAMPLE_RATE; + wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; } - pData->formatOut = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)&wf); + pData->formatOut = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)&wf); if (pData->formatOut == ma_format_unknown) { /* The format isn't supported. This is almost certainly because the exclusive mode format isn't supported by miniaudio. We need to return MA_SHARE_MODE_NOT_SUPPORTED @@ -21945,11 +22190,19 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device goto done; } - pData->channelsOut = wf.Format.nChannels; - pData->sampleRateOut = wf.Format.nSamplesPerSec; + pData->channelsOut = wf.nChannels; + pData->sampleRateOut = wf.nSamplesPerSec; - /* Get the internal channel map based on the channel mask. */ - ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut); + /* + Get the internal channel map based on the channel mask. There is a possibility that GetMixFormat() returns + a WAVEFORMATEX instead of a WAVEFORMATEXTENSIBLE, in which case the channel mask will be undefined. In this + case we'll just use the default channel map. + */ + if (wf.wFormatTag == WAVE_FORMAT_EXTENSIBLE || wf.cbSize >= sizeof(MA_WAVEFORMATEXTENSIBLE)) { + ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pData->channelsOut, pData->channelMapOut); + } else { + ma_channel_map_init_standard(ma_standard_channel_map_microsoft, pData->channelMapOut, ma_countof(pData->channelMapOut), pData->channelsOut); + } /* Period size. */ pData->periodsOut = (pData->periodsIn != 0) ? pData->periodsIn : MA_DEFAULT_PERIODS; @@ -21957,16 +22210,16 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device if (pData->periodSizeInFramesOut == 0) { if (pData->periodSizeInMillisecondsIn == 0) { if (pData->performanceProfile == ma_performance_profile_low_latency) { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.Format.nSamplesPerSec); + pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_LOW_LATENCY, wf.nSamplesPerSec); } else { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.Format.nSamplesPerSec); + pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(MA_DEFAULT_PERIOD_SIZE_IN_MILLISECONDS_CONSERVATIVE, wf.nSamplesPerSec); } } else { - pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.Format.nSamplesPerSec); + pData->periodSizeInFramesOut = ma_calculate_buffer_size_in_frames_from_milliseconds(pData->periodSizeInMillisecondsIn, wf.nSamplesPerSec); } } - periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.Format.nSamplesPerSec; + periodDurationInMicroseconds = ((ma_uint64)pData->periodSizeInFramesOut * 1000 * 1000) / wf.nSamplesPerSec; /* Slightly different initialization for shared and exclusive modes. We try exclusive mode first, and if it fails, fall back to shared mode. */ @@ -21979,7 +22232,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device */ hr = E_FAIL; for (;;) { - hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL); + hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL); if (hr == MA_AUDCLNT_E_INVALID_DEVICE_PERIOD) { if (bufferDuration > 500*10000) { break; @@ -22000,7 +22253,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device ma_uint32 bufferSizeInFrames; hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); if (SUCCEEDED(hr)) { - bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.Format.nSamplesPerSec * bufferSizeInFrames) + 0.5); + bufferDuration = (MA_REFERENCE_TIME)((10000.0 * 1000 / wf.nSamplesPerSec * bufferSizeInFrames) + 0.5); /* Unfortunately we need to release and re-acquire the audio client according to MSDN. Seems silly - why not just call IAudioClient_Initialize() again?! */ ma_IAudioClient_Release((ma_IAudioClient*)pData->pAudioClient); @@ -22012,7 +22265,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device #endif if (SUCCEEDED(hr)) { - hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (WAVEFORMATEX*)&wf, NULL); + hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, bufferDuration, (MA_WAVEFORMATEX*)&wf, NULL); } } } @@ -22043,7 +22296,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device */ #ifndef MA_WASAPI_NO_LOW_LATENCY_SHARED_MODE { - if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.Format.nSamplesPerSec) { + if ((streamFlags & MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM) == 0 || nativeSampleRate == wf.nSamplesPerSec) { ma_IAudioClient3* pAudioClient3 = NULL; hr = ma_IAudioClient_QueryInterface(pData->pAudioClient, &MA_IID_IAudioClient3, (void**)&pAudioClient3); if (SUCCEEDED(hr)) { @@ -22051,7 +22304,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device ma_uint32 fundamentalPeriodInFrames; ma_uint32 minPeriodInFrames; ma_uint32 maxPeriodInFrames; - hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames); + hr = ma_IAudioClient3_GetSharedModeEnginePeriod(pAudioClient3, (MA_WAVEFORMATEX*)&wf, &defaultPeriodInFrames, &fundamentalPeriodInFrames, &minPeriodInFrames, &maxPeriodInFrames); if (SUCCEEDED(hr)) { ma_uint32 desiredPeriodInFrames = pData->periodSizeInFramesOut; ma_uint32 actualPeriodInFrames = desiredPeriodInFrames; @@ -22075,7 +22328,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY must not be in the stream flags. If either of these are specified, IAudioClient3_InitializeSharedAudioStream() will fail. */ - hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (WAVEFORMATEX*)&wf, NULL); + hr = ma_IAudioClient3_InitializeSharedAudioStream(pAudioClient3, streamFlags & ~(MA_AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM | MA_AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY), actualPeriodInFrames, (MA_WAVEFORMATEX*)&wf, NULL); if (SUCCEEDED(hr)) { wasInitializedUsingIAudioClient3 = MA_TRUE; pData->periodSizeInFramesOut = actualPeriodInFrames; @@ -22106,7 +22359,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device /* If we don't have an IAudioClient3 then we need to use the normal initialization routine. */ if (!wasInitializedUsingIAudioClient3) { MA_REFERENCE_TIME bufferDuration = periodDurationInMicroseconds * pData->periodsOut * 10; /* <-- Multiply by 10 for microseconds to 100-nanoseconds. */ - hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (WAVEFORMATEX*)&wf, NULL); + hr = ma_IAudioClient_Initialize((ma_IAudioClient*)pData->pAudioClient, shareMode, streamFlags, bufferDuration, 0, (const MA_WAVEFORMATEX*)&wf, NULL); if (FAILED(hr)) { if (hr == E_ACCESSDENIED) { errorMsg = "[WASAPI] Failed to initialize device. Access denied.", result = MA_ACCESS_DENIED; @@ -22122,13 +22375,22 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device } if (!wasInitializedUsingIAudioClient3) { - ma_uint32 bufferSizeInFrames; + ma_uint32 bufferSizeInFrames = 0; hr = ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pData->pAudioClient, &bufferSizeInFrames); if (FAILED(hr)) { errorMsg = "[WASAPI] Failed to get audio client's actual buffer size.", result = ma_result_from_HRESULT(hr); goto done; } + /* + When using process loopback mode, retrieval of the buffer size seems to result in totally + incorrect values. In this case we'll just assume it's the same size as what we requested + when we initialized the client. + */ + if (usingProcessLoopback) { + bufferSizeInFrames = (ma_uint32)((periodDurationInMicroseconds * pData->periodsOut) * pData->sampleRateOut / 1000000); + } + pData->periodSizeInFramesOut = bufferSizeInFrames / pData->periodsOut; } @@ -22154,7 +22416,7 @@ static ma_result ma_device_init_internal__wasapi(ma_context* pContext, ma_device ma_IPropertyStore *pProperties; hr = ma_IMMDevice_OpenPropertyStore(pDeviceInterface, STGM_READ, &pProperties); if (SUCCEEDED(hr)) { - PROPVARIANT varName; + MA_PROPVARIANT varName; ma_PropVariantInit(&varName); hr = ma_IPropertyStore_GetValue(pProperties, &MA_PKEY_Device_FriendlyName, &varName); if (SUCCEEDED(hr)) { @@ -22308,13 +22570,13 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev pDevice->capture.internalPeriods = data.periodsOut; ma_strcpy_s(pDevice->capture.name, sizeof(pDevice->capture.name), data.deviceName); - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture); + ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture); pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); /* We must always have a valid ID. */ - ma_wcscpy_s(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); + ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); } if (deviceType == ma_device_type_playback) { @@ -22329,13 +22591,13 @@ static ma_result ma_device_reinit__wasapi(ma_device* pDevice, ma_device_type dev pDevice->playback.internalPeriods = data.periodsOut; ma_strcpy_s(pDevice->playback.name, sizeof(pDevice->playback.name), data.deviceName); - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback); + ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback); pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); /* We must always have a valid ID because rerouting will look at it. */ - ma_wcscpy_s(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); + ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); } return MA_SUCCESS; @@ -22398,7 +22660,7 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf The event for capture needs to be manual reset for the same reason as playback. We keep the initial state set to unsignaled, however, because we want to block until we actually have something for the first call to ma_device_read(). */ - pDevice->wasapi.hEventCapture = CreateEventA(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */ + pDevice->wasapi.hEventCapture = (ma_handle)CreateEventA(NULL, FALSE, FALSE, NULL); /* Auto reset, unsignaled by default. */ if (pDevice->wasapi.hEventCapture == NULL) { result = ma_result_from_GetLastError(GetLastError()); @@ -22414,13 +22676,13 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for capture."); return result; } - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, pDevice->wasapi.hEventCapture); + ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, (HANDLE)pDevice->wasapi.hEventCapture); pDevice->wasapi.periodSizeInFramesCapture = data.periodSizeInFramesOut; ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture, &pDevice->wasapi.actualBufferSizeInFramesCapture); /* We must always have a valid ID. */ - ma_wcscpy_s(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); + ma_strcpy_s_WCHAR(pDevice->capture.id.wasapi, sizeof(pDevice->capture.id.wasapi), data.id.wasapi); /* The descriptor needs to be updated with actual values. */ pDescriptorCapture->format = data.formatOut; @@ -22460,7 +22722,7 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf pDevice->wasapi.pAudioClientCapture = NULL; } - CloseHandle(pDevice->wasapi.hEventCapture); + CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); pDevice->wasapi.hEventCapture = NULL; } return result; @@ -22480,7 +22742,7 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf The playback event also needs to be initially set to a signaled state so that the first call to ma_device_write() is able to get passed WaitForMultipleObjects(). */ - pDevice->wasapi.hEventPlayback = CreateEventA(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */ + pDevice->wasapi.hEventPlayback = (ma_handle)CreateEventA(NULL, FALSE, TRUE, NULL); /* Auto reset, signaled by default. */ if (pDevice->wasapi.hEventPlayback == NULL) { result = ma_result_from_GetLastError(GetLastError()); @@ -22494,7 +22756,7 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf pDevice->wasapi.pAudioClientCapture = NULL; } - CloseHandle(pDevice->wasapi.hEventCapture); + CloseHandle((HANDLE)pDevice->wasapi.hEventCapture); pDevice->wasapi.hEventCapture = NULL; } @@ -22510,13 +22772,13 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create event for playback."); return result; } - ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, pDevice->wasapi.hEventPlayback); + ma_IAudioClient_SetEventHandle((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, (HANDLE)pDevice->wasapi.hEventPlayback); pDevice->wasapi.periodSizeInFramesPlayback = data.periodSizeInFramesOut; ma_IAudioClient_GetBufferSize((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback, &pDevice->wasapi.actualBufferSizeInFramesPlayback); /* We must always have a valid ID because rerouting will look at it. */ - ma_wcscpy_s(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); + ma_strcpy_s_WCHAR(pDevice->playback.id.wasapi, sizeof(pDevice->playback.id.wasapi), data.id.wasapi); /* The descriptor needs to be updated with actual values. */ pDescriptorPlayback->format = data.formatOut; @@ -22544,7 +22806,7 @@ static ma_result ma_device_init__wasapi(ma_device* pDevice, const ma_device_conf ma_mutex_init(&pDevice->wasapi.rerouteLock); - hr = ma_CoCreateInstance(pDevice->pContext, MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); + hr = ma_CoCreateInstance(pDevice->pContext, &MA_CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, &MA_IID_IMMDeviceEnumerator, (void**)&pDeviceEnumerator); if (FAILED(hr)) { ma_device_uninit__wasapi(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to create device enumerator."); @@ -22655,17 +22917,17 @@ static ma_result ma_device_start__wasapi_nolock(ma_device* pDevice) HRESULT hr; if (pDevice->pContext->wasapi.hAvrt) { - LPCWSTR pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage); + const char* pTaskName = ma_to_usage_string__wasapi(pDevice->wasapi.usage); if (pTaskName) { DWORD idx = 0; - pDevice->wasapi.hAvrtHandle = ((MA_PFN_AvSetMmThreadCharacteristicsW)pDevice->pContext->wasapi.AvSetMmThreadCharacteristicsW)(pTaskName, &idx); + pDevice->wasapi.hAvrtHandle = (ma_handle)((MA_PFN_AvSetMmThreadCharacteristicsA)pDevice->pContext->wasapi.AvSetMmThreadCharacteristicsA)(pTaskName, &idx); } } if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex || pDevice->type == ma_device_type_loopback) { hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientCapture); if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device."); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal capture device. HRESULT = %d.", (int)hr); return ma_result_from_HRESULT(hr); } @@ -22675,7 +22937,7 @@ static ma_result ma_device_start__wasapi_nolock(ma_device* pDevice) if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { hr = ma_IAudioClient_Start((ma_IAudioClient*)pDevice->wasapi.pAudioClientPlayback); if (FAILED(hr)) { - ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device."); + ma_log_postf(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WASAPI] Failed to start internal playback device. HRESULT = %d.", (int)hr); return ma_result_from_HRESULT(hr); } @@ -22748,7 +23010,7 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) DWORD waitTime = pDevice->wasapi.actualBufferSizeInFramesPlayback / pDevice->playback.internalSampleRate; if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime); + WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime); } else { ma_uint32 prevFramesAvaialablePlayback = (ma_uint32)-1; @@ -22772,8 +23034,8 @@ static ma_result ma_device_stop__wasapi_nolock(ma_device* pDevice) } prevFramesAvaialablePlayback = framesAvailablePlayback; - WaitForSingleObject(pDevice->wasapi.hEventPlayback, waitTime * 1000); - ResetEvent(pDevice->wasapi.hEventPlayback); /* Manual reset. */ + WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, waitTime * 1000); + ResetEvent((HANDLE)pDevice->wasapi.hEventPlayback); /* Manual reset. */ } } } @@ -22983,7 +23245,7 @@ static ma_result ma_device_read__wasapi(ma_device* pDevice, void* pFrames, ma_ui timeoutInMilliseconds = 10; } - if (WaitForSingleObject(pDevice->wasapi.hEventCapture, timeoutInMilliseconds) != WAIT_OBJECT_0) { + if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventCapture, timeoutInMilliseconds) != WAIT_OBJECT_0) { if (pDevice->type == ma_device_type_loopback) { continue; /* Keep waiting in loopback mode. */ } else { @@ -23068,7 +23330,7 @@ static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames whether or not we need to wait for more data. */ if (pDevice->playback.shareMode == ma_share_mode_exclusive) { - if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { + if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { result = MA_ERROR; break; /* Wait failed. Probably timed out. */ } @@ -23094,7 +23356,7 @@ static ma_result ma_device_write__wasapi(ma_device* pDevice, const void* pFrames } else { if (hr == MA_AUDCLNT_E_BUFFER_TOO_LARGE || hr == MA_AUDCLNT_E_BUFFER_ERROR) { /* Not enough data available. We need to wait for more. */ - if (WaitForSingleObject(pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { + if (WaitForSingleObject((HANDLE)pDevice->wasapi.hEventPlayback, MA_WASAPI_WAIT_TIMEOUT_MILLISECONDS) != WAIT_OBJECT_0) { result = MA_ERROR; break; /* Wait failed. Probably timed out. */ } @@ -23133,33 +23395,32 @@ static ma_result ma_device_data_loop_wakeup__wasapi(ma_device* pDevice) static ma_result ma_context_uninit__wasapi(ma_context* pContext) { + ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI); + MA_ASSERT(pContext != NULL); MA_ASSERT(pContext->backend == ma_backend_wasapi); - if (pContext->wasapi.commandThread != NULL) { - ma_context_command__wasapi cmd = ma_context_init_command__wasapi(MA_CONTEXT_COMMAND_QUIT__WASAPI); - ma_context_post_command__wasapi(pContext, &cmd); - ma_thread_wait(&pContext->wasapi.commandThread); + ma_context_post_command__wasapi(pContext, &cmd); + ma_thread_wait(&pContext->wasapi.commandThread); - if (pContext->wasapi.hAvrt) { - ma_dlclose(pContext, pContext->wasapi.hAvrt); - pContext->wasapi.hAvrt = NULL; - } - - #if defined(MA_WIN32_UWP) - { - if (pContext->wasapi.hMMDevapi) { - ma_dlclose(pContext, pContext->wasapi.hMMDevapi); - pContext->wasapi.hMMDevapi = NULL; - } - } - #endif - - /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */ - ma_semaphore_uninit(&pContext->wasapi.commandSem); - ma_mutex_uninit(&pContext->wasapi.commandLock); + if (pContext->wasapi.hAvrt) { + ma_dlclose(pContext, pContext->wasapi.hAvrt); + pContext->wasapi.hAvrt = NULL; } + #if defined(MA_WIN32_UWP) + { + if (pContext->wasapi.hMMDevapi) { + ma_dlclose(pContext, pContext->wasapi.hMMDevapi); + pContext->wasapi.hMMDevapi = NULL; + } + } + #endif + + /* Only after the thread has been terminated can we uninitialize the sync objects for the command thread. */ + ma_semaphore_uninit(&pContext->wasapi.commandSem); + ma_mutex_uninit(&pContext->wasapi.commandLock); + return MA_SUCCESS; } @@ -23284,12 +23545,12 @@ static ma_result ma_context_init__wasapi(ma_context* pContext, const ma_context_ /* Optionally use the Avrt API to specify the audio thread's latency sensitivity requirements */ pContext->wasapi.hAvrt = ma_dlopen(pContext, "avrt.dll"); if (pContext->wasapi.hAvrt) { - pContext->wasapi.AvSetMmThreadCharacteristicsW = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsW"); + pContext->wasapi.AvSetMmThreadCharacteristicsA = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvSetMmThreadCharacteristicsA"); pContext->wasapi.AvRevertMmThreadcharacteristics = ma_dlsym(pContext, pContext->wasapi.hAvrt, "AvRevertMmThreadCharacteristics"); /* If either function could not be found, disable use of avrt entirely. */ - if (!pContext->wasapi.AvSetMmThreadCharacteristicsW || !pContext->wasapi.AvRevertMmThreadcharacteristics) { - pContext->wasapi.AvSetMmThreadCharacteristicsW = NULL; + if (!pContext->wasapi.AvSetMmThreadCharacteristicsA || !pContext->wasapi.AvRevertMmThreadcharacteristics) { + pContext->wasapi.AvSetMmThreadCharacteristicsA = NULL; pContext->wasapi.AvRevertMmThreadcharacteristics = NULL; ma_dlclose(pContext, pContext->wasapi.hAvrt); pContext->wasapi.hAvrt = NULL; @@ -23375,7 +23636,7 @@ typedef struct DWORD dwFlags; DWORD dwBufferBytes; DWORD dwReserved; - WAVEFORMATEX* lpwfxFormat; + MA_WAVEFORMATEX* lpwfxFormat; GUID guid3DAlgorithm; } MA_DSBUFFERDESC; @@ -23385,7 +23646,7 @@ typedef struct DWORD dwFlags; DWORD dwBufferBytes; DWORD dwReserved; - WAVEFORMATEX* lpwfxFormat; + MA_WAVEFORMATEX* lpwfxFormat; DWORD dwFXCount; void* lpDSCFXDesc; /* <-- miniaudio doesn't use this, so set to void*. */ } MA_DSCBUFFERDESC; @@ -23509,7 +23770,7 @@ typedef struct /* IDirectSoundBuffer */ HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps); HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor); - HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); + HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); HRESULT (STDMETHODCALLTYPE * GetVolume) (ma_IDirectSoundBuffer* pThis, LONG* pVolume); HRESULT (STDMETHODCALLTYPE * GetPan) (ma_IDirectSoundBuffer* pThis, LONG* pPan); HRESULT (STDMETHODCALLTYPE * GetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD* pFrequency); @@ -23518,7 +23779,7 @@ typedef struct HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); HRESULT (STDMETHODCALLTYPE * Play) (ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags); HRESULT (STDMETHODCALLTYPE * SetCurrentPosition)(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition); - HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat); + HRESULT (STDMETHODCALLTYPE * SetFormat) (ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat); HRESULT (STDMETHODCALLTYPE * SetVolume) (ma_IDirectSoundBuffer* pThis, LONG volume); HRESULT (STDMETHODCALLTYPE * SetPan) (ma_IDirectSoundBuffer* pThis, LONG pan); HRESULT (STDMETHODCALLTYPE * SetFrequency) (ma_IDirectSoundBuffer* pThis, DWORD dwFrequency); @@ -23535,7 +23796,7 @@ static MA_INLINE ULONG ma_IDirectSoundBuffer_AddRef(ma_IDirectSoundBuffer* pTh static MA_INLINE ULONG ma_IDirectSoundBuffer_Release(ma_IDirectSoundBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCaps(ma_IDirectSoundBuffer* pThis, MA_DSBCAPS* pDSBufferCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSBufferCaps); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD* pCurrentPlayCursor, DWORD* pCurrentWriteCursor) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCurrentPlayCursor, pCurrentWriteCursor); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFormat(ma_IDirectSoundBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetVolume(ma_IDirectSoundBuffer* pThis, LONG* pVolume) { return pThis->lpVtbl->GetVolume(pThis, pVolume); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetPan(ma_IDirectSoundBuffer* pThis, LONG* pPan) { return pThis->lpVtbl->GetPan(pThis, pPan); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_GetFrequency(ma_IDirectSoundBuffer* pThis, DWORD* pFrequency) { return pThis->lpVtbl->GetFrequency(pThis, pFrequency); } @@ -23544,7 +23805,7 @@ static MA_INLINE HRESULT ma_IDirectSoundBuffer_Initialize(ma_IDirectSoundBuffer* static MA_INLINE HRESULT ma_IDirectSoundBuffer_Lock(ma_IDirectSoundBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_Play(ma_IDirectSoundBuffer* pThis, DWORD dwReserved1, DWORD dwPriority, DWORD dwFlags) { return pThis->lpVtbl->Play(pThis, dwReserved1, dwPriority, dwFlags); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetCurrentPosition(ma_IDirectSoundBuffer* pThis, DWORD dwNewPosition) { return pThis->lpVtbl->SetCurrentPosition(pThis, dwNewPosition); } -static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); } +static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFormat(ma_IDirectSoundBuffer* pThis, const MA_WAVEFORMATEX* pFormat) { return pThis->lpVtbl->SetFormat(pThis, pFormat); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetVolume(ma_IDirectSoundBuffer* pThis, LONG volume) { return pThis->lpVtbl->SetVolume(pThis, volume); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetPan(ma_IDirectSoundBuffer* pThis, LONG pan) { return pThis->lpVtbl->SetPan(pThis, pan); } static MA_INLINE HRESULT ma_IDirectSoundBuffer_SetFrequency(ma_IDirectSoundBuffer* pThis, DWORD dwFrequency) { return pThis->lpVtbl->SetFrequency(pThis, dwFrequency); } @@ -23589,7 +23850,7 @@ typedef struct /* IDirectSoundCaptureBuffer */ HRESULT (STDMETHODCALLTYPE * GetCaps) (ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps); HRESULT (STDMETHODCALLTYPE * GetCurrentPosition)(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition); - HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); + HRESULT (STDMETHODCALLTYPE * GetFormat) (ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten); HRESULT (STDMETHODCALLTYPE * GetStatus) (ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus); HRESULT (STDMETHODCALLTYPE * Initialize) (ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc); HRESULT (STDMETHODCALLTYPE * Lock) (ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags); @@ -23606,7 +23867,7 @@ static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_AddRef(ma_IDirectSoundCapt static MA_INLINE ULONG ma_IDirectSoundCaptureBuffer_Release(ma_IDirectSoundCaptureBuffer* pThis) { return pThis->lpVtbl->Release(pThis); } static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCaps(ma_IDirectSoundCaptureBuffer* pThis, MA_DSCBCAPS* pDSCBCaps) { return pThis->lpVtbl->GetCaps(pThis, pDSCBCaps); } static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetCurrentPosition(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pCapturePosition, DWORD* pReadPosition) { return pThis->lpVtbl->GetCurrentPosition(pThis, pCapturePosition, pReadPosition); } -static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } +static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetFormat(ma_IDirectSoundCaptureBuffer* pThis, MA_WAVEFORMATEX* pFormat, DWORD dwSizeAllocated, DWORD* pSizeWritten) { return pThis->lpVtbl->GetFormat(pThis, pFormat, dwSizeAllocated, pSizeWritten); } static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_GetStatus(ma_IDirectSoundCaptureBuffer* pThis, DWORD* pStatus) { return pThis->lpVtbl->GetStatus(pThis, pStatus); } static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Initialize(ma_IDirectSoundCaptureBuffer* pThis, ma_IDirectSoundCapture* pDirectSoundCapture, const MA_DSCBUFFERDESC* pDSCBufferDesc) { return pThis->lpVtbl->Initialize(pThis, pDirectSoundCapture, pDSCBufferDesc); } static MA_INLINE HRESULT ma_IDirectSoundCaptureBuffer_Lock(ma_IDirectSoundCaptureBuffer* pThis, DWORD dwOffset, DWORD dwBytes, void** ppAudioPtr1, DWORD* pAudioBytes1, void** ppAudioPtr2, DWORD* pAudioBytes2, DWORD dwFlags) { return pThis->lpVtbl->Lock(pThis, dwOffset, dwBytes, ppAudioPtr1, pAudioBytes1, ppAudioPtr2, pAudioBytes2, dwFlags); } @@ -23636,11 +23897,11 @@ static MA_INLINE ULONG ma_IDirectSoundNotify_Release(ma_IDirectSoundNotify* pT static MA_INLINE HRESULT ma_IDirectSoundNotify_SetNotificationPositions(ma_IDirectSoundNotify* pThis, DWORD dwPositionNotifies, const MA_DSBPOSITIONNOTIFY* pPositionNotifies) { return pThis->lpVtbl->SetNotificationPositions(pThis, dwPositionNotifies, pPositionNotifies); } -typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (LPGUID pDeviceGUID, LPCSTR pDeviceDescription, LPCSTR pModule, LPVOID pContext); -typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, LPUNKNOWN pUnkOuter); -typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext); -typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, LPUNKNOWN pUnkOuter); -typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, LPVOID pContext); +typedef BOOL (CALLBACK * ma_DSEnumCallbackAProc) (GUID* pDeviceGUID, const char* pDeviceDescription, const char* pModule, void* pContext); +typedef HRESULT (WINAPI * ma_DirectSoundCreateProc) (const GUID* pcGuidDevice, ma_IDirectSound** ppDS8, ma_IUnknown* pUnkOuter); +typedef HRESULT (WINAPI * ma_DirectSoundEnumerateAProc) (ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext); +typedef HRESULT (WINAPI * ma_DirectSoundCaptureCreateProc) (const GUID* pcGuidDevice, ma_IDirectSoundCapture** ppDSC8, ma_IUnknown* pUnkOuter); +typedef HRESULT (WINAPI * ma_DirectSoundCaptureEnumerateAProc)(ma_DSEnumCallbackAProc pDSEnumCallback, void* pContext); static ma_uint32 ma_get_best_sample_rate_within_range(ma_uint32 sampleRateMin, ma_uint32 sampleRateMax) { @@ -23737,7 +23998,7 @@ static ma_result ma_context_create_IDirectSound__dsound(ma_context* pContext, ma /* The cooperative level must be set before doing anything else. */ hWnd = ((MA_PFN_GetForegroundWindow)pContext->win32.GetForegroundWindow)(); - if (hWnd == NULL) { + if (hWnd == 0) { hWnd = ((MA_PFN_GetDesktopWindow)pContext->win32.GetDesktopWindow)(); } @@ -23889,7 +24150,7 @@ typedef struct ma_bool32 terminated; } ma_context_enumerate_devices_callback_data__dsound; -static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext) +static BOOL CALLBACK ma_context_enumerate_devices_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext) { ma_context_enumerate_devices_callback_data__dsound* pData = (ma_context_enumerate_devices_callback_data__dsound*)lpContext; ma_device_info deviceInfo; @@ -23955,7 +24216,7 @@ typedef struct ma_bool32 found; } ma_context_get_device_info_callback_data__dsound; -static BOOL CALLBACK ma_context_get_device_info_callback__dsound(LPGUID lpGuid, LPCSTR lpcstrDescription, LPCSTR lpcstrModule, LPVOID lpContext) +static BOOL CALLBACK ma_context_get_device_info_callback__dsound(GUID* lpGuid, const char* lpcstrDescription, const char* lpcstrModule, void* lpContext) { ma_context_get_device_info_callback_data__dsound* pData = (ma_context_get_device_info_callback_data__dsound*)lpContext; MA_ASSERT(pData != NULL); @@ -24160,7 +24421,7 @@ static ma_result ma_device_uninit__dsound(ma_device* pDevice) return MA_SUCCESS; } -static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, WAVEFORMATEXTENSIBLE* pWF) +static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 channels, ma_uint32 sampleRate, const ma_channel* pChannelMap, MA_WAVEFORMATEXTENSIBLE* pWF) { GUID subformat; @@ -24197,14 +24458,14 @@ static ma_result ma_config_to_WAVEFORMATEXTENSIBLE(ma_format format, ma_uint32 c } MA_ZERO_OBJECT(pWF); - pWF->Format.cbSize = sizeof(*pWF); - pWF->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; - pWF->Format.nChannels = (WORD)channels; - pWF->Format.nSamplesPerSec = (DWORD)sampleRate; - pWF->Format.wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); - pWF->Format.nBlockAlign = (WORD)(pWF->Format.nChannels * pWF->Format.wBitsPerSample / 8); - pWF->Format.nAvgBytesPerSec = pWF->Format.nBlockAlign * pWF->Format.nSamplesPerSec; - pWF->Samples.wValidBitsPerSample = pWF->Format.wBitsPerSample; + pWF->cbSize = sizeof(*pWF); + pWF->wFormatTag = WAVE_FORMAT_EXTENSIBLE; + pWF->nChannels = (WORD)channels; + pWF->nSamplesPerSec = (DWORD)sampleRate; + pWF->wBitsPerSample = (WORD)(ma_get_bytes_per_sample(format)*8); + pWF->nBlockAlign = (WORD)(pWF->nChannels * pWF->wBitsPerSample / 8); + pWF->nAvgBytesPerSec = pWF->nBlockAlign * pWF->nSamplesPerSec; + pWF->Samples.wValidBitsPerSample = pWF->wBitsPerSample; pWF->dwChannelMask = ma_channel_map_to_channel_mask__win32(pChannelMap, channels); pWF->SubFormat = subformat; @@ -24247,12 +24508,12 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf full-duplex mode. */ if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - WAVEFORMATEXTENSIBLE wf; + MA_WAVEFORMATEXTENSIBLE wf; MA_DSCBUFFERDESC descDS; ma_uint32 periodSizeInFrames; ma_uint32 periodCount; char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ - WAVEFORMATEXTENSIBLE* pActualFormat; + MA_WAVEFORMATEXTENSIBLE* pActualFormat; result = ma_config_to_WAVEFORMATEXTENSIBLE(pDescriptorCapture->format, pDescriptorCapture->channels, pDescriptorCapture->sampleRate, pDescriptorCapture->channelMap, &wf); if (result != MA_SUCCESS) { @@ -24265,26 +24526,26 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf return result; } - result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pDevice->pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.Format.nChannels, &wf.Format.wBitsPerSample, &wf.Format.nSamplesPerSec); + result = ma_context_get_format_info_for_IDirectSoundCapture__dsound(pDevice->pContext, (ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &wf.nChannels, &wf.wBitsPerSample, &wf.nSamplesPerSec); if (result != MA_SUCCESS) { ma_device_uninit__dsound(pDevice); return result; } - wf.Format.nBlockAlign = (WORD)(wf.Format.nChannels * wf.Format.wBitsPerSample / 8); - wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec; - wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample; + wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); + wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; + wf.Samples.wValidBitsPerSample = wf.wBitsPerSample; wf.SubFormat = MA_GUID_KSDATAFORMAT_SUBTYPE_PCM; /* The size of the buffer must be a clean multiple of the period count. */ - periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.Format.nSamplesPerSec, pConfig->performanceProfile); + periodSizeInFrames = ma_calculate_period_size_in_frames_from_descriptor__dsound(pDescriptorCapture, wf.nSamplesPerSec, pConfig->performanceProfile); periodCount = (pDescriptorCapture->periodCount > 0) ? pDescriptorCapture->periodCount : MA_DEFAULT_PERIODS; MA_ZERO_OBJECT(&descDS); descDS.dwSize = sizeof(descDS); descDS.dwFlags = 0; - descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.Format.nBlockAlign; - descDS.lpwfxFormat = (WAVEFORMATEX*)&wf; + descDS.dwBufferBytes = periodSizeInFrames * periodCount * wf.nBlockAlign; + descDS.lpwfxFormat = (MA_WAVEFORMATEX*)&wf; hr = ma_IDirectSoundCapture_CreateCaptureBuffer((ma_IDirectSoundCapture*)pDevice->dsound.pCapture, &descDS, (ma_IDirectSoundCaptureBuffer**)&pDevice->dsound.pCaptureBuffer, NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); @@ -24293,8 +24554,8 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf } /* Get the _actual_ properties of the buffer. */ - pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata; - hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); + pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata; + hr = ma_IDirectSoundCaptureBuffer_GetFormat((ma_IDirectSoundCaptureBuffer*)pDevice->dsound.pCaptureBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the capture device's buffer."); @@ -24302,12 +24563,12 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf } /* We can now start setting the output data formats. */ - pDescriptorCapture->format = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat); - pDescriptorCapture->channels = pActualFormat->Format.nChannels; - pDescriptorCapture->sampleRate = pActualFormat->Format.nSamplesPerSec; + pDescriptorCapture->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat); + pDescriptorCapture->channels = pActualFormat->nChannels; + pDescriptorCapture->sampleRate = pActualFormat->nSamplesPerSec; /* Get the native channel map based on the channel mask. */ - if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); } else { ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorCapture->channels, pDescriptorCapture->channelMap); @@ -24335,11 +24596,11 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - WAVEFORMATEXTENSIBLE wf; + MA_WAVEFORMATEXTENSIBLE wf; MA_DSBUFFERDESC descDSPrimary; MA_DSCAPS caps; char rawdata[1024]; /* <-- Ugly hack to avoid a malloc() due to a crappy DirectSound API. */ - WAVEFORMATEXTENSIBLE* pActualFormat; + MA_WAVEFORMATEXTENSIBLE* pActualFormat; ma_uint32 periodSizeInFrames; ma_uint32 periodCount; MA_DSBUFFERDESC descDS; @@ -24395,21 +24656,21 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf } if (pDescriptorPlayback->channels == 0) { - wf.Format.nChannels = nativeChannelCount; + wf.nChannels = nativeChannelCount; wf.dwChannelMask = nativeChannelMask; } if (pDescriptorPlayback->sampleRate == 0) { /* We base the sample rate on the values returned by GetCaps(). */ if ((caps.dwFlags & MA_DSCAPS_CONTINUOUSRATE) != 0) { - wf.Format.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate); + wf.nSamplesPerSec = ma_get_best_sample_rate_within_range(caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate); } else { - wf.Format.nSamplesPerSec = caps.dwMaxSecondarySampleRate; + wf.nSamplesPerSec = caps.dwMaxSecondarySampleRate; } } - wf.Format.nBlockAlign = (WORD)(wf.Format.nChannels * wf.Format.wBitsPerSample / 8); - wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec; + wf.nBlockAlign = (WORD)(wf.nChannels * wf.wBitsPerSample / 8); + wf.nAvgBytesPerSec = wf.nBlockAlign * wf.nSamplesPerSec; /* From MSDN: @@ -24418,7 +24679,7 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf supported format. To determine whether this has happened, an application can call the GetFormat method for the primary buffer and compare the result with the format that was requested with the SetFormat method. */ - hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, &wf.Format); + hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); if (FAILED(hr)) { /* If setting of the format failed we'll try again with some fallback settings. On Windows 98 I have @@ -24426,15 +24687,15 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf sample rate of 48000 did not work correctly. Not sure if it was a driver issue or not, but will use 44100 for the sample rate. */ - wf.Format.cbSize = sizeof(wf.Format); - wf.Format.wFormatTag = WAVE_FORMAT_PCM; - wf.Format.wBitsPerSample = 16; - wf.Format.nChannels = nativeChannelCount; - wf.Format.nSamplesPerSec = 44100; - wf.Format.nBlockAlign = wf.Format.nChannels * (wf.Format.wBitsPerSample / 8); - wf.Format.nAvgBytesPerSec = wf.Format.nSamplesPerSec * wf.Format.nBlockAlign; + wf.cbSize = 18; /* NOTE: Don't use sizeof(MA_WAVEFORMATEX) here because it's got an extra 2 bytes due to padding. */ + wf.wFormatTag = WAVE_FORMAT_PCM; + wf.wBitsPerSample = 16; + wf.nChannels = nativeChannelCount; + wf.nSamplesPerSec = 44100; + wf.nBlockAlign = wf.nChannels * (wf.wBitsPerSample / 8); + wf.nAvgBytesPerSec = wf.nSamplesPerSec * wf.nBlockAlign; - hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, &wf.Format); + hr = ma_IDirectSoundBuffer_SetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)&wf); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to set format of playback device's primary buffer."); @@ -24443,8 +24704,8 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf } /* Get the _actual_ properties of the buffer. */ - pActualFormat = (WAVEFORMATEXTENSIBLE*)rawdata; - hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); + pActualFormat = (MA_WAVEFORMATEXTENSIBLE*)rawdata; + hr = ma_IDirectSoundBuffer_GetFormat((ma_IDirectSoundBuffer*)pDevice->dsound.pPlaybackPrimaryBuffer, (MA_WAVEFORMATEX*)pActualFormat, sizeof(rawdata), NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[DirectSound] Failed to retrieve the actual format of the playback device's primary buffer."); @@ -24452,12 +24713,12 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf } /* We now have enough information to start setting some output properties. */ - pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX((WAVEFORMATEX*)pActualFormat); - pDescriptorPlayback->channels = pActualFormat->Format.nChannels; - pDescriptorPlayback->sampleRate = pActualFormat->Format.nSamplesPerSec; + pDescriptorPlayback->format = ma_format_from_WAVEFORMATEX((MA_WAVEFORMATEX*)pActualFormat); + pDescriptorPlayback->channels = pActualFormat->nChannels; + pDescriptorPlayback->sampleRate = pActualFormat->nSamplesPerSec; /* Get the internal channel map based on the channel mask. */ - if (pActualFormat->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) { + if (pActualFormat->wFormatTag == WAVE_FORMAT_EXTENSIBLE) { ma_channel_mask_to_channel_map__win32(pActualFormat->dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); } else { ma_channel_mask_to_channel_map__win32(wf.dwChannelMask, pDescriptorPlayback->channels, pDescriptorPlayback->channelMap); @@ -24486,7 +24747,7 @@ static ma_result ma_device_init__dsound(ma_device* pDevice, const ma_device_conf descDS.dwSize = sizeof(descDS); descDS.dwFlags = MA_DSBCAPS_CTRLPOSITIONNOTIFY | MA_DSBCAPS_GLOBALFOCUS | MA_DSBCAPS_GETCURRENTPOSITION2; descDS.dwBufferBytes = periodSizeInFrames * periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels); - descDS.lpwfxFormat = &pActualFormat->Format; + descDS.lpwfxFormat = (MA_WAVEFORMATEX*)pActualFormat; hr = ma_IDirectSound_CreateSoundBuffer((ma_IDirectSound*)pDevice->dsound.pPlayback, &descDS, (ma_IDirectSoundBuffer**)&pDevice->dsound.pPlaybackBuffer, NULL); if (FAILED(hr)) { ma_device_uninit__dsound(pDevice); @@ -25081,16 +25342,75 @@ WinMM Backend #ifdef MA_HAS_WINMM /* -Some older compilers don't have WAVEOUTCAPS2A and WAVEINCAPS2A, so we'll need to write this ourselves. These structures -are exactly the same as the older ones but they have a few GUIDs for manufacturer/product/name identification. I'm keeping -the names the same as the Win32 library for consistency, but namespaced to avoid naming conflicts with the Win32 version. +Some build configurations will exclude the WinMM API. An example is when WIN32_LEAN_AND_MEAN +is defined. We need to define the types and functions we need manually. */ +#define MA_MMSYSERR_NOERROR 0 +#define MA_MMSYSERR_ERROR 1 +#define MA_MMSYSERR_BADDEVICEID 2 +#define MA_MMSYSERR_INVALHANDLE 5 +#define MA_MMSYSERR_NOMEM 7 +#define MA_MMSYSERR_INVALFLAG 10 +#define MA_MMSYSERR_INVALPARAM 11 +#define MA_MMSYSERR_HANDLEBUSY 12 + +#define MA_CALLBACK_EVENT 0x00050000 +#define MA_WAVE_ALLOWSYNC 0x0002 + +#define MA_WHDR_DONE 0x00000001 +#define MA_WHDR_PREPARED 0x00000002 +#define MA_WHDR_BEGINLOOP 0x00000004 +#define MA_WHDR_ENDLOOP 0x00000008 +#define MA_WHDR_INQUEUE 0x00000010 + +#define MA_MAXPNAMELEN 32 + +typedef void* MA_HWAVEIN; +typedef void* MA_HWAVEOUT; +typedef UINT MA_MMRESULT; +typedef UINT MA_MMVERSION; + typedef struct { WORD wMid; WORD wPid; - MMVERSION vDriverVersion; - CHAR szPname[MAXPNAMELEN]; + MA_MMVERSION vDriverVersion; + CHAR szPname[MA_MAXPNAMELEN]; + DWORD dwFormats; + WORD wChannels; + WORD wReserved1; +} MA_WAVEINCAPSA; + +typedef struct +{ + WORD wMid; + WORD wPid; + MA_MMVERSION vDriverVersion; + CHAR szPname[MA_MAXPNAMELEN]; + DWORD dwFormats; + WORD wChannels; + WORD wReserved1; + DWORD dwSupport; +} MA_WAVEOUTCAPSA; + +typedef struct tagWAVEHDR +{ + char* lpData; + DWORD dwBufferLength; + DWORD dwBytesRecorded; + DWORD_PTR dwUser; + DWORD dwFlags; + DWORD dwLoops; + struct tagWAVEHDR* lpNext; + DWORD_PTR reserved; +} MA_WAVEHDR; + +typedef struct +{ + WORD wMid; + WORD wPid; + MA_MMVERSION vDriverVersion; + CHAR szPname[MA_MAXPNAMELEN]; DWORD dwFormats; WORD wChannels; WORD wReserved1; @@ -25099,12 +25419,13 @@ typedef struct GUID ProductGuid; GUID NameGuid; } MA_WAVEOUTCAPS2A; + typedef struct { WORD wMid; WORD wPid; - MMVERSION vDriverVersion; - CHAR szPname[MAXPNAMELEN]; + MA_MMVERSION vDriverVersion; + CHAR szPname[MA_MAXPNAMELEN]; DWORD dwFormats; WORD wChannels; WORD wReserved1; @@ -25113,36 +25434,37 @@ typedef struct GUID NameGuid; } MA_WAVEINCAPS2A; -typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void); -typedef MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEOUTCAPSA pwoc, UINT cbwoc); -typedef MMRESULT (WINAPI * MA_PFN_waveOutOpen)(LPHWAVEOUT phwo, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); -typedef MMRESULT (WINAPI * MA_PFN_waveOutClose)(HWAVEOUT hwo); -typedef MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh); -typedef MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh); -typedef MMRESULT (WINAPI * MA_PFN_waveOutWrite)(HWAVEOUT hwo, LPWAVEHDR pwh, UINT cbwh); -typedef MMRESULT (WINAPI * MA_PFN_waveOutReset)(HWAVEOUT hwo); -typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void); -typedef MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, LPWAVEINCAPSA pwic, UINT cbwic); -typedef MMRESULT (WINAPI * MA_PFN_waveInOpen)(LPHWAVEIN phwi, UINT uDeviceID, LPCWAVEFORMATEX pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); -typedef MMRESULT (WINAPI * MA_PFN_waveInClose)(HWAVEIN hwi); -typedef MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh); -typedef MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh); -typedef MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(HWAVEIN hwi, LPWAVEHDR pwh, UINT cbwh); -typedef MMRESULT (WINAPI * MA_PFN_waveInStart)(HWAVEIN hwi); -typedef MMRESULT (WINAPI * MA_PFN_waveInReset)(HWAVEIN hwi); +typedef UINT (WINAPI * MA_PFN_waveOutGetNumDevs)(void); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEOUTCAPSA* pwoc, UINT cbwoc); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutOpen)(MA_HWAVEOUT* phwo, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutClose)(MA_HWAVEOUT hwo); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutPrepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutUnprepareHeader)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutWrite)(MA_HWAVEOUT hwo, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveOutReset)(MA_HWAVEOUT hwo); +typedef UINT (WINAPI * MA_PFN_waveInGetNumDevs)(void); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInGetDevCapsA)(ma_uintptr uDeviceID, MA_WAVEINCAPSA* pwic, UINT cbwic); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInOpen)(MA_HWAVEIN* phwi, UINT uDeviceID, const MA_WAVEFORMATEX* pwfx, DWORD_PTR dwCallback, DWORD_PTR dwInstance, DWORD fdwOpen); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInClose)(MA_HWAVEIN hwi); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInPrepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInUnprepareHeader)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInAddBuffer)(MA_HWAVEIN hwi, MA_WAVEHDR* pwh, UINT cbwh); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInStart)(MA_HWAVEIN hwi); +typedef MA_MMRESULT (WINAPI * MA_PFN_waveInReset)(MA_HWAVEIN hwi); -static ma_result ma_result_from_MMRESULT(MMRESULT resultMM) +static ma_result ma_result_from_MMRESULT(MA_MMRESULT resultMM) { - switch (resultMM) { - case MMSYSERR_NOERROR: return MA_SUCCESS; - case MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS; - case MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS; - case MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY; - case MMSYSERR_INVALFLAG: return MA_INVALID_ARGS; - case MMSYSERR_INVALPARAM: return MA_INVALID_ARGS; - case MMSYSERR_HANDLEBUSY: return MA_BUSY; - case MMSYSERR_ERROR: return MA_ERROR; - default: return MA_ERROR; + switch (resultMM) + { + case MA_MMSYSERR_NOERROR: return MA_SUCCESS; + case MA_MMSYSERR_BADDEVICEID: return MA_INVALID_ARGS; + case MA_MMSYSERR_INVALHANDLE: return MA_INVALID_ARGS; + case MA_MMSYSERR_NOMEM: return MA_OUT_OF_MEMORY; + case MA_MMSYSERR_INVALFLAG: return MA_INVALID_ARGS; + case MA_MMSYSERR_INVALPARAM: return MA_INVALID_ARGS; + case MA_MMSYSERR_HANDLEBUSY: return MA_BUSY; + case MA_MMSYSERR_ERROR: return MA_ERROR; + default: return MA_ERROR; } } @@ -25178,7 +25500,7 @@ we can do things generically and typesafely. Names are being kept the same for c */ typedef struct { - CHAR szPname[MAXPNAMELEN]; + CHAR szPname[MA_MAXPNAMELEN]; DWORD dwFormats; WORD wChannels; GUID NameGuid; @@ -25264,7 +25586,7 @@ static ma_result ma_get_best_info_from_formats_flags__winmm(DWORD dwFormats, WOR return MA_SUCCESS; } -static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, WAVEFORMATEX* pWF) +static ma_result ma_formats_flags_to_WAVEFORMATEX__winmm(DWORD dwFormats, WORD channels, MA_WAVEFORMATEX* pWF) { ma_result result; @@ -25321,7 +25643,7 @@ static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, name, and then concatenate the name from the registry. */ if (!ma_is_guid_null(&pCaps->NameGuid)) { - wchar_t guidStrW[256]; + WCHAR guidStrW[256]; if (((MA_PFN_StringFromGUID2)pContext->win32.StringFromGUID2)(&pCaps->NameGuid, guidStrW, ma_countof(guidStrW)) > 0) { char guidStr[256]; char keyStr[1024]; @@ -25335,7 +25657,7 @@ static ma_result ma_context_get_device_info_from_WAVECAPS(ma_context* pContext, if (((MA_PFN_RegOpenKeyExA)pContext->win32.RegOpenKeyExA)(HKEY_LOCAL_MACHINE, keyStr, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { BYTE nameFromReg[512]; DWORD nameFromRegSize = sizeof(nameFromReg); - LONG resultWin32 = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (LPBYTE)nameFromReg, (LPDWORD)&nameFromRegSize); + LONG resultWin32 = ((MA_PFN_RegQueryValueExA)pContext->win32.RegQueryValueExA)(hKey, "Name", 0, NULL, (BYTE*)nameFromReg, (DWORD*)&nameFromRegSize); ((MA_PFN_RegCloseKey)pContext->win32.RegCloseKey)(hKey); if (resultWin32 == ERROR_SUCCESS) { @@ -25429,13 +25751,13 @@ static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_en /* Playback. */ playbackDeviceCount = ((MA_PFN_waveOutGetNumDevs)pContext->winmm.waveOutGetNumDevs)(); for (iPlaybackDevice = 0; iPlaybackDevice < playbackDeviceCount; ++iPlaybackDevice) { - MMRESULT result; + MA_MMRESULT result; MA_WAVEOUTCAPS2A caps; MA_ZERO_OBJECT(&caps); - result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (WAVEOUTCAPSA*)&caps, sizeof(caps)); - if (result == MMSYSERR_NOERROR) { + result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(iPlaybackDevice, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); + if (result == MA_MMSYSERR_NOERROR) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); @@ -25458,13 +25780,13 @@ static ma_result ma_context_enumerate_devices__winmm(ma_context* pContext, ma_en /* Capture. */ captureDeviceCount = ((MA_PFN_waveInGetNumDevs)pContext->winmm.waveInGetNumDevs)(); for (iCaptureDevice = 0; iCaptureDevice < captureDeviceCount; ++iCaptureDevice) { - MMRESULT result; + MA_MMRESULT result; MA_WAVEINCAPS2A caps; MA_ZERO_OBJECT(&caps); - result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (WAVEINCAPSA*)&caps, sizeof(caps)); - if (result == MMSYSERR_NOERROR) { + result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(iCaptureDevice, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); + if (result == MA_MMSYSERR_NOERROR) { ma_device_info deviceInfo; MA_ZERO_OBJECT(&deviceInfo); @@ -25506,23 +25828,23 @@ static ma_result ma_context_get_device_info__winmm(ma_context* pContext, ma_devi } if (deviceType == ma_device_type_playback) { - MMRESULT result; + MA_MMRESULT result; MA_WAVEOUTCAPS2A caps; MA_ZERO_OBJECT(&caps); - result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (WAVEOUTCAPSA*)&caps, sizeof(caps)); - if (result == MMSYSERR_NOERROR) { + result = ((MA_PFN_waveOutGetDevCapsA)pContext->winmm.waveOutGetDevCapsA)(winMMDeviceID, (MA_WAVEOUTCAPSA*)&caps, sizeof(caps)); + if (result == MA_MMSYSERR_NOERROR) { return ma_context_get_device_info_from_WAVEOUTCAPS2(pContext, &caps, pDeviceInfo); } } else { - MMRESULT result; + MA_MMRESULT result; MA_WAVEINCAPS2A caps; MA_ZERO_OBJECT(&caps); - result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (WAVEINCAPSA*)&caps, sizeof(caps)); - if (result == MMSYSERR_NOERROR) { + result = ((MA_PFN_waveInGetDevCapsA)pContext->winmm.waveInGetDevCapsA)(winMMDeviceID, (MA_WAVEINCAPSA*)&caps, sizeof(caps)); + if (result == MA_MMSYSERR_NOERROR) { return ma_context_get_device_info_from_WAVEINCAPS2(pContext, &caps, pDeviceInfo); } } @@ -25536,13 +25858,13 @@ static ma_result ma_device_uninit__winmm(ma_device* pDevice) MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture); + ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); CloseHandle((HANDLE)pDevice->winmm.hEventCapture); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { - ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback); - ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback); + ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); + ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); CloseHandle((HANDLE)pDevice->winmm.hEventPlayback); } @@ -25599,9 +25921,9 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi /* The capture device needs to be initialized first. */ if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - WAVEINCAPSA caps; - WAVEFORMATEX wf; - MMRESULT resultMM; + MA_WAVEINCAPSA caps; + MA_WAVEFORMATEX wf; + MA_MMRESULT resultMM; /* We use an event to know when a new fragment needs to be enqueued. */ pDevice->winmm.hEventCapture = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); @@ -25611,7 +25933,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi } /* The format should be based on the device's actual format. */ - if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MMSYSERR_NOERROR) { + if (((MA_PFN_waveInGetDevCapsA)pDevice->pContext->winmm.waveInGetDevCapsA)(winMMDeviceIDCapture, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; goto on_error; } @@ -25622,8 +25944,8 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi goto on_error; } - resultMM = ((MA_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((LPHWAVEIN)&pDevice->winmm.hDeviceCapture, winMMDeviceIDCapture, &wf, (DWORD_PTR)pDevice->winmm.hEventCapture, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC); - if (resultMM != MMSYSERR_NOERROR) { + resultMM = ((MA_PFN_waveInOpen)pDevice->pContext->winmm.waveInOpen)((MA_HWAVEIN*)&pDevice->winmm.hDeviceCapture, winMMDeviceIDCapture, &wf, (DWORD_PTR)pDevice->winmm.hEventCapture, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); + if (resultMM != MA_MMSYSERR_NOERROR) { errorMsg = "[WinMM] Failed to open capture device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; goto on_error; } @@ -25637,9 +25959,9 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - WAVEOUTCAPSA caps; - WAVEFORMATEX wf; - MMRESULT resultMM; + MA_WAVEOUTCAPSA caps; + MA_WAVEFORMATEX wf; + MA_MMRESULT resultMM; /* We use an event to know when a new fragment needs to be enqueued. */ pDevice->winmm.hEventPlayback = (ma_handle)CreateEventA(NULL, TRUE, TRUE, NULL); @@ -25649,7 +25971,7 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi } /* The format should be based on the device's actual format. */ - if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MMSYSERR_NOERROR) { + if (((MA_PFN_waveOutGetDevCapsA)pDevice->pContext->winmm.waveOutGetDevCapsA)(winMMDeviceIDPlayback, &caps, sizeof(caps)) != MA_MMSYSERR_NOERROR) { errorMsg = "[WinMM] Failed to retrieve internal device caps.", errorCode = MA_FORMAT_NOT_SUPPORTED; goto on_error; } @@ -25660,8 +25982,8 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi goto on_error; } - resultMM = ((MA_PFN_waveOutOpen)pDevice->pContext->winmm.waveOutOpen)((LPHWAVEOUT)&pDevice->winmm.hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEventPlayback, (DWORD_PTR)pDevice, CALLBACK_EVENT | WAVE_ALLOWSYNC); - if (resultMM != MMSYSERR_NOERROR) { + resultMM = ((MA_PFN_waveOutOpen)pDevice->pContext->winmm.waveOutOpen)((MA_HWAVEOUT*)&pDevice->winmm.hDevicePlayback, winMMDeviceIDPlayback, &wf, (DWORD_PTR)pDevice->winmm.hEventPlayback, (DWORD_PTR)pDevice, MA_CALLBACK_EVENT | MA_WAVE_ALLOWSYNC); + if (resultMM != MA_MMSYSERR_NOERROR) { errorMsg = "[WinMM] Failed to open playback device.", errorCode = MA_FAILED_TO_OPEN_BACKEND_DEVICE; goto on_error; } @@ -25681,10 +26003,10 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi */ heapSize = 0; if (pConfig->deviceType == ma_device_type_capture || pConfig->deviceType == ma_device_type_duplex) { - heapSize += sizeof(WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); + heapSize += sizeof(MA_WAVEHDR)*pDescriptorCapture->periodCount + (pDescriptorCapture->periodSizeInFrames * pDescriptorCapture->periodCount * ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); } if (pConfig->deviceType == ma_device_type_playback || pConfig->deviceType == ma_device_type_duplex) { - heapSize += sizeof(WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels)); + heapSize += sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount + (pDescriptorPlayback->periodSizeInFrames * pDescriptorPlayback->periodCount * ma_get_bytes_per_frame(pDescriptorPlayback->format, pDescriptorPlayback->channels)); } pDevice->winmm._pHeapData = (ma_uint8*)ma_calloc(heapSize, &pDevice->pContext->allocationCallbacks); @@ -25700,27 +26022,27 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi if (pConfig->deviceType == ma_device_type_capture) { pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount)); + pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); } else { pDevice->winmm.pWAVEHDRCapture = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)); + pDevice->winmm.pIntermediaryBufferCapture = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)); } /* Prepare headers. */ for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorCapture->periodSizeInFrames, pDescriptorCapture->format, pDescriptorCapture->channels); - ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod)); - ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes; - ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L; - ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L; - ((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR)); + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferCapture + (periodSizeInBytes*iPeriod)); + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwBufferLength = periodSizeInBytes; + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwFlags = 0L; + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwLoops = 0L; + ((MA_PFN_waveInPrepareHeader)pDevice->pContext->winmm.waveInPrepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); /* - The user data of the WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means + The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means it's unlocked and available for writing. A value of 1 means it's locked. */ - ((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0; + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod].dwUser = 0; } } @@ -25729,27 +26051,27 @@ static ma_result ma_device_init__winmm(ma_device* pDevice, const ma_device_confi if (pConfig->deviceType == ma_device_type_playback) { pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData; - pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*pDescriptorPlayback->periodCount); + pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*pDescriptorPlayback->periodCount); } else { - pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount)); - pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)) + (pDescriptorCapture->periodSizeInFrames*pDescriptorCapture->periodCount*ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); + pDevice->winmm.pWAVEHDRPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount)); + pDevice->winmm.pIntermediaryBufferPlayback = pDevice->winmm._pHeapData + (sizeof(MA_WAVEHDR)*(pDescriptorCapture->periodCount + pDescriptorPlayback->periodCount)) + (pDescriptorCapture->periodSizeInFrames*pDescriptorCapture->periodCount*ma_get_bytes_per_frame(pDescriptorCapture->format, pDescriptorCapture->channels)); } /* Prepare headers. */ for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { ma_uint32 periodSizeInBytes = ma_get_period_size_in_bytes(pDescriptorPlayback->periodSizeInFrames, pDescriptorPlayback->format, pDescriptorPlayback->channels); - ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (LPSTR)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod)); - ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes; - ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L; - ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L; - ((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR)); + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].lpData = (char*)(pDevice->winmm.pIntermediaryBufferPlayback + (periodSizeInBytes*iPeriod)); + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwBufferLength = periodSizeInBytes; + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwFlags = 0L; + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwLoops = 0L; + ((MA_PFN_waveOutPrepareHeader)pDevice->pContext->winmm.waveOutPrepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR)); /* - The user data of the WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means + The user data of the MA_WAVEHDR structure is a single flag the controls whether or not it is ready for writing. Consider it to be named "isLocked". A value of 0 means it's unlocked and available for writing. A value of 1 means it's locked. */ - ((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0; + ((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod].dwUser = 0; } } @@ -25760,22 +26082,22 @@ on_error: if (pDevice->winmm.pWAVEHDRCapture != NULL) { ma_uint32 iPeriod; for (iPeriod = 0; iPeriod < pDescriptorCapture->periodCount; ++iPeriod) { - ((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR)); + ((MA_PFN_waveInUnprepareHeader)pDevice->pContext->winmm.waveInUnprepareHeader)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); } } - ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((HWAVEIN)pDevice->winmm.hDeviceCapture); + ((MA_PFN_waveInClose)pDevice->pContext->winmm.waveInClose)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { if (pDevice->winmm.pWAVEHDRCapture != NULL) { ma_uint32 iPeriod; for (iPeriod = 0; iPeriod < pDescriptorPlayback->periodCount; ++iPeriod) { - ((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &((WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(WAVEHDR)); + ((MA_PFN_waveOutUnprepareHeader)pDevice->pContext->winmm.waveOutUnprepareHeader)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback)[iPeriod], sizeof(MA_WAVEHDR)); } } - ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((HWAVEOUT)pDevice->winmm.hDevicePlayback); + ((MA_PFN_waveOutClose)pDevice->pContext->winmm.waveOutClose)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); } ma_free(pDevice->winmm._pHeapData, &pDevice->pContext->allocationCallbacks); @@ -25792,19 +26114,19 @@ static ma_result ma_device_start__winmm(ma_device* pDevice) MA_ASSERT(pDevice != NULL); if (pDevice->type == ma_device_type_capture || pDevice->type == ma_device_type_duplex) { - MMRESULT resultMM; - WAVEHDR* pWAVEHDR; + MA_MMRESULT resultMM; + MA_WAVEHDR* pWAVEHDR; ma_uint32 iPeriod; - pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; + pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ ResetEvent((HANDLE)pDevice->winmm.hEventCapture); /* To start the device we attach all of the buffers and then start it. As the buffers are filled with data we will get notifications. */ for (iPeriod = 0; iPeriod < pDevice->capture.internalPeriods; ++iPeriod) { - resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(WAVEHDR)); - if (resultMM != MMSYSERR_NOERROR) { + resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[iPeriod], sizeof(MA_WAVEHDR)); + if (resultMM != MA_MMSYSERR_NOERROR) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to attach input buffers to capture device in preparation for capture."); return ma_result_from_MMRESULT(resultMM); } @@ -25814,8 +26136,8 @@ static ma_result ma_device_start__winmm(ma_device* pDevice) } /* Capture devices need to be explicitly started, unlike playback devices. */ - resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((HWAVEIN)pDevice->winmm.hDeviceCapture); - if (resultMM != MMSYSERR_NOERROR) { + resultMM = ((MA_PFN_waveInStart)pDevice->pContext->winmm.waveInStart)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); + if (resultMM != MA_MMSYSERR_NOERROR) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] Failed to start backend device."); return ma_result_from_MMRESULT(resultMM); } @@ -25830,7 +26152,7 @@ static ma_result ma_device_start__winmm(ma_device* pDevice) static ma_result ma_device_stop__winmm(ma_device* pDevice) { - MMRESULT resultMM; + MA_MMRESULT resultMM; MA_ASSERT(pDevice != NULL); @@ -25839,22 +26161,22 @@ static ma_result ma_device_stop__winmm(ma_device* pDevice) return MA_INVALID_ARGS; } - resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((HWAVEIN)pDevice->winmm.hDeviceCapture); - if (resultMM != MMSYSERR_NOERROR) { + resultMM = ((MA_PFN_waveInReset)pDevice->pContext->winmm.waveInReset)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture); + if (resultMM != MA_MMSYSERR_NOERROR) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset capture device."); } } if (pDevice->type == ma_device_type_playback || pDevice->type == ma_device_type_duplex) { ma_uint32 iPeriod; - WAVEHDR* pWAVEHDR; + MA_WAVEHDR* pWAVEHDR; if (pDevice->winmm.hDevicePlayback == NULL) { return MA_INVALID_ARGS; } /* We need to drain the device. To do this we just loop over each header and if it's locked just wait for the event. */ - pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; + pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; for (iPeriod = 0; iPeriod < pDevice->playback.internalPeriods; iPeriod += 1) { if (pWAVEHDR[iPeriod].dwUser == 1) { /* 1 = locked. */ if (WaitForSingleObject((HANDLE)pDevice->winmm.hEventPlayback, INFINITE) != WAIT_OBJECT_0) { @@ -25865,8 +26187,8 @@ static ma_result ma_device_stop__winmm(ma_device* pDevice) } } - resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((HWAVEOUT)pDevice->winmm.hDevicePlayback); - if (resultMM != MMSYSERR_NOERROR) { + resultMM = ((MA_PFN_waveOutReset)pDevice->pContext->winmm.waveOutReset)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback); + if (resultMM != MA_MMSYSERR_NOERROR) { ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_WARNING, "[WinMM] WARNING: Failed to reset playback device."); } } @@ -25877,9 +26199,9 @@ static ma_result ma_device_stop__winmm(ma_device* pDevice) static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesWritten) { ma_result result = MA_SUCCESS; - MMRESULT resultMM; + MA_MMRESULT resultMM; ma_uint32 totalFramesWritten; - WAVEHDR* pWAVEHDR; + MA_WAVEHDR* pWAVEHDR; MA_ASSERT(pDevice != NULL); MA_ASSERT(pPCMFrames != NULL); @@ -25888,7 +26210,7 @@ static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFram *pFramesWritten = 0; } - pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; + pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRPlayback; /* Keep processing as much data as possible. */ totalFramesWritten = 0; @@ -25913,14 +26235,14 @@ static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFram /* If we've consumed the buffer entirely we need to write it out to the device. */ if (pDevice->winmm.headerFramesConsumedPlayback == (pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwBufferLength/bpf)) { pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 1; /* 1 = locked. */ - pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ + pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ ResetEvent((HANDLE)pDevice->winmm.hEventPlayback); /* The device will be started here. */ - resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(WAVEHDR)); - if (resultMM != MMSYSERR_NOERROR) { + resultMM = ((MA_PFN_waveOutWrite)pDevice->pContext->winmm.waveOutWrite)((MA_HWAVEOUT)pDevice->winmm.hDevicePlayback, &pWAVEHDR[pDevice->winmm.iNextHeaderPlayback], sizeof(MA_WAVEHDR)); + if (resultMM != MA_MMSYSERR_NOERROR) { result = ma_result_from_MMRESULT(resultMM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveOutWrite() failed."); break; @@ -25948,7 +26270,7 @@ static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFram } /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ - if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & WHDR_DONE) != 0) { + if ((pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwFlags & MA_WHDR_DONE) != 0) { pWAVEHDR[pDevice->winmm.iNextHeaderPlayback].dwUser = 0; /* 0 = unlocked (make it available for writing). */ pDevice->winmm.headerFramesConsumedPlayback = 0; } @@ -25969,9 +26291,9 @@ static ma_result ma_device_write__winmm(ma_device* pDevice, const void* pPCMFram static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_uint32 frameCount, ma_uint32* pFramesRead) { ma_result result = MA_SUCCESS; - MMRESULT resultMM; + MA_MMRESULT resultMM; ma_uint32 totalFramesRead; - WAVEHDR* pWAVEHDR; + MA_WAVEHDR* pWAVEHDR; MA_ASSERT(pDevice != NULL); MA_ASSERT(pPCMFrames != NULL); @@ -25980,7 +26302,7 @@ static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_ *pFramesRead = 0; } - pWAVEHDR = (WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; + pWAVEHDR = (MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture; /* Keep processing as much data as possible. */ totalFramesRead = 0; @@ -26002,14 +26324,14 @@ static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_ /* If we've consumed the buffer entirely we need to add it back to the device. */ if (pDevice->winmm.headerFramesConsumedCapture == (pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwBufferLength/bpf)) { pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 1; /* 1 = locked. */ - pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ + pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags &= ~MA_WHDR_DONE; /* <-- Need to make sure the WHDR_DONE flag is unset. */ /* Make sure the event is reset to a non-signaled state to ensure we don't prematurely return from WaitForSingleObject(). */ ResetEvent((HANDLE)pDevice->winmm.hEventCapture); /* The device will be started here. */ - resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((HWAVEIN)pDevice->winmm.hDeviceCapture, &((LPWAVEHDR)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(WAVEHDR)); - if (resultMM != MMSYSERR_NOERROR) { + resultMM = ((MA_PFN_waveInAddBuffer)pDevice->pContext->winmm.waveInAddBuffer)((MA_HWAVEIN)pDevice->winmm.hDeviceCapture, &((MA_WAVEHDR*)pDevice->winmm.pWAVEHDRCapture)[pDevice->winmm.iNextHeaderCapture], sizeof(MA_WAVEHDR)); + if (resultMM != MA_MMSYSERR_NOERROR) { result = ma_result_from_MMRESULT(resultMM); ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[WinMM] waveInAddBuffer() failed."); break; @@ -26037,7 +26359,7 @@ static ma_result ma_device_read__winmm(ma_device* pDevice, void* pPCMFrames, ma_ } /* Something happened. If the next buffer has been marked as done we need to reset a bit of state. */ - if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & WHDR_DONE) != 0) { + if ((pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwFlags & MA_WHDR_DONE) != 0) { pWAVEHDR[pDevice->winmm.iNextHeaderCapture].dwUser = 0; /* 0 = unlocked (make it available for reading). */ pDevice->winmm.headerFramesConsumedCapture = 0; } @@ -29932,7 +30254,6 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ma_pa_channel_map cmap; ma_pa_buffer_attr attr; const ma_pa_sample_spec* pActualSS = NULL; - const ma_pa_channel_map* pActualCMap = NULL; const ma_pa_buffer_attr* pActualAttr = NULL; ma_uint32 iChannel; ma_pa_stream_flags_t streamFlags; @@ -29988,6 +30309,14 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ss = sourceInfo.sample_spec; cmap = sourceInfo.channel_map; + /* Use the requested channel count if we have one. */ + if (pDescriptorCapture->channels != 0) { + ss.channels = pDescriptorCapture->channels; + } + + /* Use a default channel map. */ + ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT); + /* Use the requested sample rate if one was specified. */ if (pDescriptorCapture->sampleRate != 0) { ss.rate = pDescriptorCapture->sampleRate; @@ -30082,11 +30411,6 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi fixed sooner than later. I might remove this hack later. */ if (pDescriptorCapture->channels > 2) { - pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamCapture); - if (pActualCMap != NULL) { - cmap = *pActualCMap; - } - for (iChannel = 0; iChannel < pDescriptorCapture->channels; ++iChannel) { pDescriptorCapture->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); } @@ -30129,6 +30453,15 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi ss = sinkInfo.sample_spec; cmap = sinkInfo.channel_map; + /* Use the requested channel count if we have one. */ + if (pDescriptorPlayback->channels != 0) { + ss.channels = pDescriptorPlayback->channels; + } + + /* Use a default channel map. */ + ((ma_pa_channel_map_init_extend_proc)pDevice->pContext->pulse.pa_channel_map_init_extend)(&cmap, ss.channels, MA_PA_CHANNEL_MAP_DEFAULT); + + /* Use the requested sample rate if one was specified. */ if (pDescriptorPlayback->sampleRate != 0) { ss.rate = pDescriptorPlayback->sampleRate; @@ -30227,11 +30560,6 @@ static ma_result ma_device_init__pulse(ma_device* pDevice, const ma_device_confi fixed sooner than later. I might remove this hack later. */ if (pDescriptorPlayback->channels > 2) { - pActualCMap = ((ma_pa_stream_get_channel_map_proc)pDevice->pContext->pulse.pa_stream_get_channel_map)((ma_pa_stream*)pDevice->pulse.pStreamPlayback); - if (pActualCMap != NULL) { - cmap = *pActualCMap; - } - for (iChannel = 0; iChannel < pDescriptorPlayback->channels; ++iChannel) { pDescriptorPlayback->channelMap[iChannel] = ma_channel_position_from_pulse(cmap.map[iChannel]); } @@ -31270,10 +31598,11 @@ static ma_result ma_context_init__jack(ma_context* pContext, const ma_context_co { #ifndef MA_NO_RUNTIME_LINKING const char* libjackNames[] = { -#ifdef MA_WIN32 +#if defined(MA_WIN32) "libjack.dll", "libjack64.dll" -#else +#endif +#if defined(MA_UNIX) "libjack.so", "libjack.so.0" #endif @@ -35877,8 +36206,13 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c "/dev/audio", "/dev/audio0" }; + const char* pDefaultDeviceCtlNames[] = { + "/dev/audioctl", + "/dev/audioctl0" + }; int fd; int fdFlags = 0; + size_t iDefaultDevice = (size_t)-1; ma_format internalFormat; ma_uint32 internalChannels; ma_uint32 internalSampleRate; @@ -35897,11 +36231,11 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c } /*fdFlags |= O_NONBLOCK;*/ + /* Find the index of the default device as a start. We'll use this index later. Set it to (size_t)-1 otherwise. */ if (pDescriptor->pDeviceID == NULL) { /* Default device. */ - size_t iDevice; - for (iDevice = 0; iDevice < ma_countof(pDefaultDeviceNames); ++iDevice) { - fd = open(pDefaultDeviceNames[iDevice], fdFlags, 0); + for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); ++iDefaultDevice) { + fd = open(pDefaultDeviceNames[iDefaultDevice], fdFlags, 0); if (fd != -1) { break; } @@ -35909,6 +36243,16 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c } else { /* Specific device. */ fd = open(pDescriptor->pDeviceID->audio4, fdFlags, 0); + + for (iDefaultDevice = 0; iDefaultDevice < ma_countof(pDefaultDeviceNames); iDefaultDevice += 1) { + if (ma_strcmp(pDefaultDeviceNames[iDefaultDevice], pDescriptor->pDeviceID->audio4) == 0) { + break; + } + } + + if (iDefaultDevice == ma_countof(pDefaultDeviceNames)) { + iDefaultDevice = (size_t)-1; + } } if (fd == -1) { @@ -35919,6 +36263,7 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c #if !defined(MA_AUDIO4_USE_NEW_API) /* Old API */ { audio_info_t fdInfo; + int fdInfoResult = -1; /* The documentation is a little bit unclear to me as to how it handles formats. It says the @@ -35938,6 +36283,28 @@ static ma_result ma_device_init_fd__audio4(ma_device* pDevice, const ma_device_c */ AUDIO_INITINFO(&fdInfo); + /* + Get the default format from the audioctl file if we're asking for a default device. If we + retrieve it from /dev/audio it'll default to mono 8000Hz. + */ + if (iDefaultDevice != (size_t)-1) { + /* We're using a default device. Get the info from the /dev/audioctl file instead of /dev/audio. */ + int fdctl = open(pDefaultDeviceCtlNames[iDefaultDevice], fdFlags, 0); + if (fdctl != -1) { + fdInfoResult = ioctl(fdctl, AUDIO_GETINFO, &fdInfo); + close(fdctl); + } + } + + if (fdInfoResult == -1) { + /* We still don't have the default device info so just retrieve it from the main audio device. */ + if (ioctl(fd, AUDIO_GETINFO, &fdInfo) < 0) { + close(fd); + ma_log_post(ma_device_get_log(pDevice), MA_LOG_LEVEL_ERROR, "[audio4] AUDIO_GETINFO failed."); + return ma_result_from_errno(errno); + } + } + /* We get the driver to do as much of the data conversion as possible. */ if (deviceType == ma_device_type_capture) { fdInfo.mode = AUMODE_RECORD; @@ -40363,7 +40730,7 @@ static ma_result ma_device__post_init_setup(ma_device* pDevice, ma_device_type d pDevice->playback.inputCacheConsumed = 0; pDevice->playback.inputCacheRemaining = 0; - if ((pDevice->type == ma_device_type_duplex && ma_context_is_backend_asynchronous(pDevice->pContext)) || /* Duplex with asynchronous backend. */ + if (pDevice->type == ma_device_type_duplex || /* Duplex. backend may decide to use ma_device_handle_backend_data_callback() which will require this cache. */ ma_data_converter_get_required_input_frame_count(&pDevice->playback.converter, 1, &unused) != MA_SUCCESS) /* Data conversion required input frame calculation not supported. */ { /* We need a heap allocated cache. We want to size this based on the period size. */ @@ -40601,11 +40968,15 @@ static ma_bool32 ma_device__is_initialized(ma_device* pDevice) static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) { /* For some reason UWP complains when CoUninitialize() is called. I'm just not going to call it on UWP. */ -#ifdef MA_WIN32_DESKTOP +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) ma_CoUninitialize(pContext); - ma_dlclose(pContext, pContext->win32.hUser32DLL); + + #if defined(MA_WIN32_DESKTOP) + ma_dlclose(pContext, pContext->win32.hUser32DLL); + ma_dlclose(pContext, pContext->win32.hAdvapi32DLL); + #endif + ma_dlclose(pContext, pContext->win32.hOle32DLL); - ma_dlclose(pContext, pContext->win32.hAdvapi32DLL); #else (void)pContext; #endif @@ -40615,7 +40986,29 @@ static ma_result ma_context_uninit_backend_apis__win32(ma_context* pContext) static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) { -#ifdef MA_WIN32_DESKTOP +#if defined(MA_WIN32_DESKTOP) || defined(MA_WIN32_GDK) + #if defined(MA_WIN32_DESKTOP) + /* User32.dll */ + pContext->win32.hUser32DLL = ma_dlopen(pContext, "user32.dll"); + if (pContext->win32.hUser32DLL == NULL) { + return MA_FAILED_TO_INIT_BACKEND; + } + + pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetForegroundWindow"); + pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetDesktopWindow"); + + + /* Advapi32.dll */ + pContext->win32.hAdvapi32DLL = ma_dlopen(pContext, "advapi32.dll"); + if (pContext->win32.hAdvapi32DLL == NULL) { + return MA_FAILED_TO_INIT_BACKEND; + } + + pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); + pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegCloseKey"); + pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); + #endif + /* Ole32.dll */ pContext->win32.hOle32DLL = ma_dlopen(pContext, "ole32.dll"); if (pContext->win32.hOle32DLL == NULL) { @@ -40629,27 +41022,6 @@ static ma_result ma_context_init_backend_apis__win32(ma_context* pContext) pContext->win32.CoTaskMemFree = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "CoTaskMemFree"); pContext->win32.PropVariantClear = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "PropVariantClear"); pContext->win32.StringFromGUID2 = (ma_proc)ma_dlsym(pContext, pContext->win32.hOle32DLL, "StringFromGUID2"); - - - /* User32.dll */ - pContext->win32.hUser32DLL = ma_dlopen(pContext, "user32.dll"); - if (pContext->win32.hUser32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.GetForegroundWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetForegroundWindow"); - pContext->win32.GetDesktopWindow = (ma_proc)ma_dlsym(pContext, pContext->win32.hUser32DLL, "GetDesktopWindow"); - - - /* Advapi32.dll */ - pContext->win32.hAdvapi32DLL = ma_dlopen(pContext, "advapi32.dll"); - if (pContext->win32.hAdvapi32DLL == NULL) { - return MA_FAILED_TO_INIT_BACKEND; - } - - pContext->win32.RegOpenKeyExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegOpenKeyExA"); - pContext->win32.RegCloseKey = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegCloseKey"); - pContext->win32.RegQueryValueExA = (ma_proc)ma_dlsym(pContext, pContext->win32.hAdvapi32DLL, "RegQueryValueExA"); #else (void)pContext; /* Unused. */ #endif @@ -41078,7 +41450,7 @@ MA_API ma_result ma_context_uninit(ma_context* pContext) return MA_SUCCESS; } -MA_API size_t ma_context_sizeof() +MA_API size_t ma_context_sizeof(void) { return sizeof(ma_context); } @@ -48353,7 +48725,7 @@ static /*__attribute__((noinline))*/ ma_result ma_gainer_process_pcm_frames_inte /* Initialize the running gain. */ for (iChannel = 0; iChannel < pGainer->config.channels; iChannel += 1) { - float t = (pGainer->pOldGains[iChannel] - pGainer->pNewGains[iChannel]) * pGainer->masterVolume; + float t = (pGainer->pNewGains[iChannel] - pGainer->pOldGains[iChannel]) * pGainer->masterVolume; pRunningGainDelta[iChannel] = t * d; pRunningGain[iChannel] = (pGainer->pOldGains[iChannel] * pGainer->masterVolume) + (t * a); } @@ -49652,6 +50024,7 @@ MA_API ma_spatializer_config ma_spatializer_config_init(ma_uint32 channelsIn, ma config.coneOuterGain = 0.0f; config.dopplerFactor = 1; config.directionalAttenuationFactor = 1; + config.minSpatializationChannelGain = 0.2f; config.gainSmoothTimeInFrames = 360; /* 7.5ms @ 48K. */ return config; @@ -49792,6 +50165,7 @@ MA_API ma_result ma_spatializer_init_preallocated(const ma_spatializer_config* p pSpatializer->coneOuterAngleInRadians = pConfig->coneOuterAngleInRadians; pSpatializer->coneOuterGain = pConfig->coneOuterGain; pSpatializer->dopplerFactor = pConfig->dopplerFactor; + pSpatializer->minSpatializationChannelGain = pConfig->minSpatializationChannelGain; pSpatializer->directionalAttenuationFactor = pConfig->directionalAttenuationFactor; pSpatializer->gainSmoothTimeInFrames = pConfig->gainSmoothTimeInFrames; ma_atomic_vec3f_init(&pSpatializer->position, ma_vec3f_init_3f(0, 0, 0)); @@ -50073,6 +50447,26 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, /* Clamp the gain. */ gain = ma_clamp(gain, ma_spatializer_get_min_gain(pSpatializer), ma_spatializer_get_max_gain(pSpatializer)); + /* + The gain needs to be applied per-channel here. The spatialization code below will be changing the per-channel + gains which will then eventually be passed into the gainer which will deal with smoothing the gain transitions + to avoid harsh changes in gain. + */ + for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { + pSpatializer->pNewChannelGainsOut[iChannel] = gain; + } + + /* + Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore + the whole section of code here because we need to update some internal spatialization state. + */ + if (ma_spatializer_listener_is_enabled(pListener)) { + ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, channelsOut, (const float*)pFramesIn, pChannelMapIn, channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); + } else { + ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); + } + + /* Panning. This is where we'll apply the gain and convert to the output channel count. We have an optimized path for when we're converting to a mono stream. In that case we don't really need to do any panning - we just apply the @@ -50094,19 +50488,6 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, be +1 on the X axis. A dot product is performed against the direction vector of the channel and the normalized position of the sound. */ - for (iChannel = 0; iChannel < channelsOut; iChannel += 1) { - pSpatializer->pNewChannelGainsOut[iChannel] = gain; - } - - /* - Convert to our output channel count. If the listener is disabled we just output silence here. We cannot ignore - the whole section of code here because we need to update some internal spatialization state. - */ - if (ma_spatializer_listener_is_enabled(pListener)) { - ma_channel_map_apply_f32((float*)pFramesOut, pChannelMapOut, channelsOut, (const float*)pFramesIn, pChannelMapIn, channelsIn, frameCount, ma_channel_mix_mode_rectangular, ma_mono_expansion_mode_default); - } else { - ma_silence_pcm_frames(pFramesOut, frameCount, ma_format_f32, pSpatializer->channelsOut); - } /* Calculate our per-channel gains. We do this based on the normalized relative position of the sound and it's @@ -50143,7 +50524,7 @@ MA_API ma_result ma_spatializer_process_pcm_frames(ma_spatializer* pSpatializer, Summary: 0 = more extreme panning; 1 = no panning. */ - dMin = 0.2f; /* TODO: Consider making this configurable. */ + dMin = pSpatializer->minSpatializationChannelGain; /* At this point, "d" will be positive if the sound is on the same side as the channel and negative if @@ -53476,7 +53857,7 @@ MA_API ma_result ma_channel_converter_get_output_channel_map(const ma_channel_co Data Conversion **************************************************************************************************************************************************************/ -MA_API ma_data_converter_config ma_data_converter_config_init_default() +MA_API ma_data_converter_config ma_data_converter_config_init_default(void) { ma_data_converter_config config; MA_ZERO_OBJECT(&config); @@ -56130,6 +56511,85 @@ MA_API void* ma_rb_get_subbuffer_ptr(ma_rb* pRB, size_t subbufferIndex, void* pB +static ma_result ma_pcm_rb_data_source__on_read(ma_data_source* pDataSource, void* pFramesOut, ma_uint64 frameCount, ma_uint64* pFramesRead) +{ + /* Since there's no notion of an end, we don't ever want to return MA_AT_END here. But it is possible to return 0. */ + ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; + ma_result result; + ma_uint64 totalFramesRead; + + MA_ASSERT(pRB != NULL); + + /* We need to run this in a loop since the ring buffer itself may loop. */ + totalFramesRead = 0; + while (totalFramesRead < frameCount) { + void* pMappedBuffer; + ma_uint32 mappedFrameCount; + ma_uint64 framesToRead = frameCount - totalFramesRead; + if (framesToRead > 0xFFFFFFFF) { + framesToRead = 0xFFFFFFFF; + } + + mappedFrameCount = (ma_uint32)framesToRead; + result = ma_pcm_rb_acquire_read(pRB, &mappedFrameCount, &pMappedBuffer); + if (result != MA_SUCCESS) { + break; + } + + if (mappedFrameCount == 0) { + break; /* <-- End of ring buffer. */ + } + + ma_copy_pcm_frames(ma_offset_pcm_frames_ptr(pFramesOut, totalFramesRead, pRB->format, pRB->channels), pMappedBuffer, mappedFrameCount, pRB->format, pRB->channels); + + result = ma_pcm_rb_commit_read(pRB, mappedFrameCount); + if (result != MA_SUCCESS) { + break; + } + + totalFramesRead += mappedFrameCount; + } + + *pFramesRead = totalFramesRead; + return MA_SUCCESS; +} + +static ma_result ma_pcm_rb_data_source__on_get_data_format(ma_data_source* pDataSource, ma_format* pFormat, ma_uint32* pChannels, ma_uint32* pSampleRate, ma_channel* pChannelMap, size_t channelMapCap) +{ + ma_pcm_rb* pRB = (ma_pcm_rb*)pDataSource; + MA_ASSERT(pRB != NULL); + + if (pFormat != NULL) { + *pFormat = pRB->format; + } + + if (pChannels != NULL) { + *pChannels = pRB->channels; + } + + if (pSampleRate != NULL) { + *pSampleRate = pRB->sampleRate; + } + + /* Just assume the default channel map. */ + if (pChannelMap != NULL) { + ma_channel_map_init_standard(ma_standard_channel_map_default, pChannelMap, channelMapCap, pRB->channels); + } + + return MA_SUCCESS; +} + +static ma_data_source_vtable ma_gRBDataSourceVTable = +{ + ma_pcm_rb_data_source__on_read, + NULL, /* onSeek */ + ma_pcm_rb_data_source__on_get_data_format, + NULL, /* onGetCursor */ + NULL, /* onGetLength */ + NULL, /* onSetLooping */ + 0 +}; + static MA_INLINE ma_uint32 ma_pcm_rb_get_bpf(ma_pcm_rb* pRB) { MA_ASSERT(pRB != NULL); @@ -56158,8 +56618,21 @@ MA_API ma_result ma_pcm_rb_init_ex(ma_format format, ma_uint32 channels, ma_uint return result; } - pRB->format = format; - pRB->channels = channels; + pRB->format = format; + pRB->channels = channels; + pRB->sampleRate = 0; /* The sample rate is not passed in as a parameter. */ + + /* The PCM ring buffer is a data source. We need to get that set up as well. */ + { + ma_data_source_config dataSourceConfig = ma_data_source_config_init(); + dataSourceConfig.vtable = &ma_gRBDataSourceVTable; + + result = ma_data_source_init(&dataSourceConfig, &pRB->ds); + if (result != MA_SUCCESS) { + ma_rb_uninit(&pRB->rb); + return result; + } + } return MA_SUCCESS; } @@ -56175,6 +56648,7 @@ MA_API void ma_pcm_rb_uninit(ma_pcm_rb* pRB) return; } + ma_data_source_uninit(&pRB->ds); ma_rb_uninit(&pRB->rb); } @@ -56326,6 +56800,42 @@ MA_API void* ma_pcm_rb_get_subbuffer_ptr(ma_pcm_rb* pRB, ma_uint32 subbufferInde return ma_rb_get_subbuffer_ptr(&pRB->rb, subbufferIndex, pBuffer); } +MA_API ma_format ma_pcm_rb_get_format(const ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return ma_format_unknown; + } + + return pRB->format; +} + +MA_API ma_uint32 ma_pcm_rb_get_channels(const ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return pRB->channels; +} + +MA_API ma_uint32 ma_pcm_rb_get_sample_rate(const ma_pcm_rb* pRB) +{ + if (pRB == NULL) { + return 0; + } + + return pRB->sampleRate; +} + +MA_API void ma_pcm_rb_set_sample_rate(ma_pcm_rb* pRB, ma_uint32 sampleRate) +{ + if (pRB == NULL) { + return; + } + + pRB->sampleRate = sampleRate; +} + MA_API ma_result ma_duplex_rb_init(ma_format captureFormat, ma_uint32 captureChannels, ma_uint32 sampleRate, ma_uint32 captureInternalSampleRate, ma_uint32 captureInternalPeriodSizeInFrames, const ma_allocation_callbacks* pAllocationCallbacks, ma_duplex_rb* pRB) @@ -58273,7 +58783,7 @@ MA_API ma_result ma_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, size_t { ma_vfs_callbacks* pCallbacks = (ma_vfs_callbacks*)pVFS; ma_result result; - size_t bytesRead; + size_t bytesRead = 0; if (pBytesRead != NULL) { *pBytesRead = 0; @@ -58451,7 +58961,11 @@ MA_API ma_result ma_vfs_open_and_read_file_w(ma_vfs* pVFS, const wchar_t* pFileP } -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) +#if !defined(MA_USE_WIN32_FILEIO) && (defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) && !defined(MA_POSIX)) + #define MA_USE_WIN32_FILEIO +#endif + +#if defined(MA_USE_WIN32_FILEIO) static void ma_default_vfs__get_open_settings_win32(ma_uint32 openMode, DWORD* pDesiredAccess, DWORD* pShareMode, DWORD* pCreationDisposition) { *pDesiredAccess = 0; @@ -58920,7 +59434,7 @@ static ma_result ma_default_vfs_open(ma_vfs* pVFS, const char* pFilePath, ma_uin return MA_INVALID_ARGS; } -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) +#if defined(MA_USE_WIN32_FILEIO) return ma_default_vfs_open__win32(pVFS, pFilePath, openMode, pFile); #else return ma_default_vfs_open__stdio(pVFS, pFilePath, openMode, pFile); @@ -58939,7 +59453,7 @@ static ma_result ma_default_vfs_open_w(ma_vfs* pVFS, const wchar_t* pFilePath, m return MA_INVALID_ARGS; } -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) +#if defined(MA_USE_WIN32_FILEIO) return ma_default_vfs_open_w__win32(pVFS, pFilePath, openMode, pFile); #else return ma_default_vfs_open_w__stdio(pVFS, pFilePath, openMode, pFile); @@ -58952,7 +59466,7 @@ static ma_result ma_default_vfs_close(ma_vfs* pVFS, ma_vfs_file file) return MA_INVALID_ARGS; } -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) +#if defined(MA_USE_WIN32_FILEIO) return ma_default_vfs_close__win32(pVFS, file); #else return ma_default_vfs_close__stdio(pVFS, file); @@ -58969,7 +59483,7 @@ static ma_result ma_default_vfs_read(ma_vfs* pVFS, ma_vfs_file file, void* pDst, return MA_INVALID_ARGS; } -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) +#if defined(MA_USE_WIN32_FILEIO) return ma_default_vfs_read__win32(pVFS, file, pDst, sizeInBytes, pBytesRead); #else return ma_default_vfs_read__stdio(pVFS, file, pDst, sizeInBytes, pBytesRead); @@ -58986,7 +59500,7 @@ static ma_result ma_default_vfs_write(ma_vfs* pVFS, ma_vfs_file file, const void return MA_INVALID_ARGS; } -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) +#if defined(MA_USE_WIN32_FILEIO) return ma_default_vfs_write__win32(pVFS, file, pSrc, sizeInBytes, pBytesWritten); #else return ma_default_vfs_write__stdio(pVFS, file, pSrc, sizeInBytes, pBytesWritten); @@ -58999,7 +59513,7 @@ static ma_result ma_default_vfs_seek(ma_vfs* pVFS, ma_vfs_file file, ma_int64 of return MA_INVALID_ARGS; } -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) +#if defined(MA_USE_WIN32_FILEIO) return ma_default_vfs_seek__win32(pVFS, file, offset, origin); #else return ma_default_vfs_seek__stdio(pVFS, file, offset, origin); @@ -59018,7 +59532,7 @@ static ma_result ma_default_vfs_tell(ma_vfs* pVFS, ma_vfs_file file, ma_int64* p return MA_INVALID_ARGS; } -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) +#if defined(MA_USE_WIN32_FILEIO) return ma_default_vfs_tell__win32(pVFS, file, pCursor); #else return ma_default_vfs_tell__stdio(pVFS, file, pCursor); @@ -59037,7 +59551,7 @@ static ma_result ma_default_vfs_info(ma_vfs* pVFS, ma_vfs_file file, ma_file_inf return MA_INVALID_ARGS; } -#if defined(MA_WIN32) && defined(MA_WIN32_DESKTOP) && !defined(MA_NO_WIN32_FILEIO) +#if defined(MA_USE_WIN32_FILEIO) return ma_default_vfs_info__win32(pVFS, file, pInfo); #else return ma_default_vfs_info__stdio(pVFS, file, pInfo); @@ -62672,6 +63186,81 @@ static ma_result ma_stbvorbis_post_init(ma_stbvorbis* pVorbis) return MA_SUCCESS; } + +static ma_result ma_stbvorbis_init_internal_decoder_push(ma_stbvorbis* pVorbis) +{ + ma_result result; + stb_vorbis* stb; + size_t dataSize = 0; + size_t dataCapacity = 0; + ma_uint8* pData = NULL; /* <-- Must be initialized to NULL. */ + + for (;;) { + int vorbisError; + int consumedDataSize; /* <-- Fill by stb_vorbis_open_pushdata(). */ + size_t bytesRead; + ma_uint8* pNewData; + + /* Allocate memory for the new chunk. */ + dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE; + pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity, &pVorbis->allocationCallbacks); + if (pNewData == NULL) { + ma_free(pData, &pVorbis->allocationCallbacks); + return MA_OUT_OF_MEMORY; + } + + pData = pNewData; + + /* Read in the next chunk. */ + result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pData, dataSize), (dataCapacity - dataSize), &bytesRead); + dataSize += bytesRead; + + if (result != MA_SUCCESS) { + ma_free(pData, &pVorbis->allocationCallbacks); + return result; + } + + /* We have a maximum of 31 bits with stb_vorbis. */ + if (dataSize > INT_MAX) { + ma_free(pData, &pVorbis->allocationCallbacks); + return MA_TOO_BIG; + } + + stb = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL); + if (stb != NULL) { + /* + Successfully opened the Vorbis decoder. We might have some leftover unprocessed + data so we'll need to move that down to the front. + */ + dataSize -= (size_t)consumedDataSize; /* Consume the data. */ + MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize); + + /* + We need to track the start point so we can seek back to the start of the audio + data when seeking. + */ + pVorbis->push.audioStartOffsetInBytes = consumedDataSize; + + break; + } else { + /* Failed to open the decoder. */ + if (vorbisError == VORBIS_need_more_data) { + continue; + } else { + ma_free(pData, &pVorbis->allocationCallbacks); + return MA_ERROR; /* Failed to open the stb_vorbis decoder. */ + } + } + } + + MA_ASSERT(stb != NULL); + pVorbis->stb = stb; + pVorbis->push.pData = pData; + pVorbis->push.dataSize = dataSize; + pVorbis->push.dataCapacity = dataCapacity; + + return MA_SUCCESS; +} #endif MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_tell_proc onTell, void* pReadSeekTellUserData, const ma_decoding_backend_config* pConfig, const ma_allocation_callbacks* pAllocationCallbacks, ma_stbvorbis* pVorbis) @@ -62700,81 +63289,17 @@ MA_API ma_result ma_stbvorbis_init(ma_read_proc onRead, ma_seek_proc onSeek, ma_ pushing API. In order for us to be able to successfully initialize the decoder we need to supply it with enough data. We need to keep loading data until we have enough. */ - stb_vorbis* stb; - size_t dataSize = 0; - size_t dataCapacity = 0; - ma_uint8* pData = NULL; /* <-- Must be initialized to NULL. */ - - for (;;) { - int vorbisError; - int consumedDataSize; /* <-- Fill by stb_vorbis_open_pushdata(). */ - size_t bytesRead; - ma_uint8* pNewData; - - /* Allocate memory for the new chunk. */ - dataCapacity += MA_VORBIS_DATA_CHUNK_SIZE; - pNewData = (ma_uint8*)ma_realloc(pData, dataCapacity, pAllocationCallbacks); - if (pNewData == NULL) { - ma_free(pData, pAllocationCallbacks); - return MA_OUT_OF_MEMORY; - } - - pData = pNewData; - - /* Read in the next chunk. */ - result = pVorbis->onRead(pVorbis->pReadSeekTellUserData, ma_offset_ptr(pData, dataSize), (dataCapacity - dataSize), &bytesRead); - dataSize += bytesRead; - - if (result != MA_SUCCESS) { - ma_free(pData, pAllocationCallbacks); - return result; - } - - /* We have a maximum of 31 bits with stb_vorbis. */ - if (dataSize > INT_MAX) { - ma_free(pData, pAllocationCallbacks); - return MA_TOO_BIG; - } - - stb = stb_vorbis_open_pushdata(pData, (int)dataSize, &consumedDataSize, &vorbisError, NULL); - if (stb != NULL) { - /* - Successfully opened the Vorbis decoder. We might have some leftover unprocessed - data so we'll need to move that down to the front. - */ - dataSize -= (size_t)consumedDataSize; /* Consume the data. */ - MA_MOVE_MEMORY(pData, ma_offset_ptr(pData, consumedDataSize), dataSize); - - /* - We need to track the start point so we can seek back to the start of the audio - data when seeking. - */ - pVorbis->push.audioStartOffsetInBytes = consumedDataSize; - - break; - } else { - /* Failed to open the decoder. */ - if (vorbisError == VORBIS_need_more_data) { - continue; - } else { - ma_free(pData, pAllocationCallbacks); - return MA_ERROR; /* Failed to open the stb_vorbis decoder. */ - } - } + result = ma_stbvorbis_init_internal_decoder_push(pVorbis); + if (result != MA_SUCCESS) { + return result; } - MA_ASSERT(stb != NULL); - pVorbis->stb = stb; - pVorbis->push.pData = pData; - pVorbis->push.dataSize = dataSize; - pVorbis->push.dataCapacity = dataCapacity; - pVorbis->usingPushMode = MA_TRUE; result = ma_stbvorbis_post_init(pVorbis); if (result != MA_SUCCESS) { stb_vorbis_close(pVorbis->stb); - ma_free(pData, pAllocationCallbacks); + ma_free(pVorbis->push.pData, pAllocationCallbacks); return result; } @@ -63076,28 +63601,39 @@ MA_API ma_result ma_stbvorbis_seek_to_pcm_frame(ma_stbvorbis* pVorbis, ma_uint64 ma_result result; float buffer[4096]; - /* - This is terribly inefficient because stb_vorbis does not have a good seeking solution with it's push API. Currently this just performs - a full decode right from the start of the stream. Later on I'll need to write a layer that goes through all of the Ogg pages until we - find the one containing the sample we need. Then we know exactly where to seek for stb_vorbis. + /* If we're seeking backwards, we need to seek back to the start and then brute-force forward. */ + if (frameIndex < pVorbis->cursor) { + if (frameIndex > 0x7FFFFFFF) { + return MA_INVALID_ARGS; /* Trying to seek beyond the 32-bit maximum of stb_vorbis. */ + } - TODO: Use seeking logic documented for stb_vorbis_flush_pushdata(). - */ + /* + This is wildly inefficient due to me having trouble getting sample exact seeking working + robustly with stb_vorbis_flush_pushdata(). The only way I can think to make this work + perfectly is to reinitialize the decoder. Note that we only enter this path when seeking + backwards. This will hopefully be removed once we get our own Vorbis decoder implemented. + */ + stb_vorbis_close(pVorbis->stb); + ma_free(pVorbis->push.pData, &pVorbis->allocationCallbacks); - /* Seek to the start of the audio data in the file to begin with. */ - result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, pVorbis->push.audioStartOffsetInBytes, ma_seek_origin_start); - if (result != MA_SUCCESS) { - return result; + MA_ZERO_OBJECT(&pVorbis->push); + + /* Seek to the start of the file. */ + result = pVorbis->onSeek(pVorbis->pReadSeekTellUserData, 0, ma_seek_origin_start); + if (result != MA_SUCCESS) { + return result; + } + + result = ma_stbvorbis_init_internal_decoder_push(pVorbis); + if (result != MA_SUCCESS) { + return result; + } + + /* At this point we should be sitting on the first frame. */ + pVorbis->cursor = 0; } - stb_vorbis_flush_pushdata(pVorbis->stb); - pVorbis->push.framesConsumed = 0; - pVorbis->push.framesRemaining = 0; - pVorbis->push.dataSize = 0; - - /* Move the cursor back to the start. We'll increment this in the loop below. */ - pVorbis->cursor = 0; - + /* We're just brute-forcing this for now. */ while (pVorbis->cursor < frameIndex) { ma_uint64 framesRead; ma_uint64 framesToRead = ma_countof(buffer)/pVorbis->channels; @@ -66769,7 +67305,7 @@ MA_API ma_result ma_resource_manager_init(const ma_resource_manager_config* pCon /* Create the job threads last to ensure the threads has access to valid data. */ for (iJobThread = 0; iJobThread < pResourceManager->config.jobThreadCount; iJobThread += 1) { - result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, 0, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks); + result = ma_thread_create(&pResourceManager->jobThreads[iJobThread], ma_thread_priority_normal, pResourceManager->config.jobThreadStackSize, ma_resource_manager_job_thread, pResourceManager, &pResourceManager->config.allocationCallbacks); if (result != MA_SUCCESS) { ma_mutex_uninit(&pResourceManager->dataBufferBSTLock); ma_job_queue_uninit(&pResourceManager->jobQueue, &pResourceManager->config.allocationCallbacks); @@ -67402,7 +67938,12 @@ static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(m job.data.resourceManager.loadDataBufferNode.pInitFence = pInitFence; job.data.resourceManager.loadDataBufferNode.pDoneFence = pDoneFence; - result = ma_resource_manager_post_job(pResourceManager, &job); + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + result = ma_job_process(&job); + } else { + result = ma_resource_manager_post_job(pResourceManager, &job); + } + if (result != MA_SUCCESS) { /* Failed to post job. Probably ran out of memory. */ ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER_NODE job. %s.\n", ma_result_description(result)); @@ -67415,12 +67956,13 @@ static ma_result ma_resource_manager_data_buffer_node_acquire_critical_section(m if (pDoneFence != NULL) { ma_fence_release(pDoneFence); } if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { - ma_resource_manager_inline_notification_init(pResourceManager, pInitNotification); + ma_resource_manager_inline_notification_uninit(pInitNotification); + } else { + /* These will have been freed by the job thread, but with WAIT_INIT they will already have happend sinced the job has already been handled. */ + ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); + ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); } - ma_free(pFilePathCopy, &pResourceManager->config.allocationCallbacks); - ma_free(pFilePathWCopy, &pResourceManager->config.allocationCallbacks); - ma_resource_manager_data_buffer_node_remove(pResourceManager, pDataBufferNode); ma_free(pDataBufferNode, &pResourceManager->config.allocationCallbacks); @@ -67859,7 +68401,13 @@ static ma_result ma_resource_manager_data_buffer_init_ex_internal(ma_resource_ma job.data.resourceManager.loadDataBuffer.loopPointEndInPCMFrames = pConfig->loopPointEndInPCMFrames; job.data.resourceManager.loadDataBuffer.isLooping = pConfig->isLooping; - result = ma_resource_manager_post_job(pResourceManager, &job); + /* If we need to wait for initialization to complete we can just process the job in place. */ + if ((flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_WAIT_INIT) != 0) { + result = ma_job_process(&job); + } else { + result = ma_resource_manager_post_job(pResourceManager, &job); + } + if (result != MA_SUCCESS) { /* We failed to post the job. Most likely there isn't enough room in the queue's buffer. */ ma_log_postf(ma_resource_manager_get_log(pResourceManager), MA_LOG_LEVEL_ERROR, "Failed to post MA_JOB_TYPE_RESOURCE_MANAGER_LOAD_DATA_BUFFER job. %s.\n", ma_result_description(result)); @@ -68464,7 +69012,7 @@ static ma_data_source_vtable g_ma_resource_manager_data_stream_vtable = ma_resource_manager_data_stream_cb__get_cursor_in_pcm_frames, ma_resource_manager_data_stream_cb__get_length_in_pcm_frames, ma_resource_manager_data_stream_cb__set_looping, - MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT + 0 /*MA_DATA_SOURCE_SELF_MANAGED_RANGE_AND_LOOP_POINT*/ }; static void ma_resource_manager_data_stream_set_absolute_cursor(ma_resource_manager_data_stream* pDataStream, ma_uint64 absoluteCursor) @@ -69558,6 +70106,12 @@ done: /* Increment the node's execution pointer so that the next jobs can be processed. This is how we keep decoding of pages in-order. */ c89atomic_fetch_add_32(&pDataBufferNode->executionPointer, 1); + + /* A busy result should be considered successful from the point of view of the job system. */ + if (result == MA_BUSY) { + result = MA_SUCCESS; + } + return result; } @@ -69839,7 +70393,7 @@ static ma_result ma_job_process__resource_manager__load_data_stream(ma_job* pJob goto done; } - /* Retrieve the total length of the file before marking the decoder are loaded. */ + /* Retrieve the total length of the file before marking the decoder as loaded. */ if ((pDataStream->flags & MA_RESOURCE_MANAGER_DATA_SOURCE_FLAG_UNKNOWN_LENGTH) == 0) { result = ma_decoder_get_length_in_pcm_frames(&pDataStream->decoder, &pDataStream->totalLengthInPCMFrames); if (result != MA_SUCCESS) { @@ -72962,6 +73516,27 @@ Engine **************************************************************************************************************************************************************/ #define MA_SEEK_TARGET_NONE (~(ma_uint64)0) + +static void ma_sound_set_at_end(ma_sound* pSound, ma_bool32 atEnd) +{ + MA_ASSERT(pSound != NULL); + c89atomic_exchange_32(&pSound->atEnd, atEnd); + + /* Fire any callbacks or events. */ + if (atEnd) { + if (pSound->endCallback != NULL) { + pSound->endCallback(pSound->pEndCallbackUserData, pSound); + } + } +} + +static ma_bool32 ma_sound_get_at_end(const ma_sound* pSound) +{ + MA_ASSERT(pSound != NULL); + return c89atomic_load_32(&pSound->atEnd); +} + + MA_API ma_engine_node_config ma_engine_node_config_init(ma_engine* pEngine, ma_engine_node_type type, ma_uint32 flags) { ma_engine_node_config config; @@ -73039,8 +73614,16 @@ static ma_result ma_engine_node_set_volume(ma_engine_node* pEngineNode, float vo return MA_INVALID_ARGS; } - /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for hodling our volume. */ - ma_spatializer_set_master_volume(&pEngineNode->spatializer, volume); + ma_atomic_float_set(&pEngineNode->volume, volume); + + /* If we're not smoothing we should bypass the volume gainer entirely. */ + if (pEngineNode->volumeSmoothTimeInPCMFrames == 0) { + /* We should always have an active spatializer because it can be enabled and disabled dynamically. We can just use that for hodling our volume. */ + ma_spatializer_set_master_volume(&pEngineNode->spatializer, volume); + } else { + /* We're using volume smoothing, so apply the master volume to the gainer. */ + ma_gainer_set_gain(&pEngineNode->volumeGainer, volume); + } return MA_SUCCESS; } @@ -73057,9 +73640,12 @@ static ma_result ma_engine_node_get_volume(const ma_engine_node* pEngineNode, fl return MA_INVALID_ARGS; } - return ma_spatializer_get_master_volume(&pEngineNode->spatializer, pVolume); + *pVolume = ma_atomic_float_get((ma_atomic_float*)&pEngineNode->volume); + + return MA_SUCCESS; } + static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNode, const float** ppFramesIn, ma_uint32* pFrameCountIn, float** ppFramesOut, ma_uint32* pFrameCountOut) { ma_uint32 frameCountIn; @@ -73072,6 +73658,7 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo ma_bool32 isFadingEnabled; ma_bool32 isSpatializationEnabled; ma_bool32 isPanningEnabled; + ma_bool32 isVolumeSmoothingEnabled; frameCountIn = *pFrameCountIn; frameCountOut = *pFrameCountOut; @@ -73082,10 +73669,11 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo totalFramesProcessedIn = 0; totalFramesProcessedOut = 0; - isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode); - isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1; - isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode); - isPanningEnabled = pEngineNode->panner.pan != 0 && channelsOut != 1; + isPitchingEnabled = ma_engine_node_is_pitching_enabled(pEngineNode); + isFadingEnabled = pEngineNode->fader.volumeBeg != 1 || pEngineNode->fader.volumeEnd != 1; + isSpatializationEnabled = ma_engine_node_is_spatialization_enabled(pEngineNode); + isPanningEnabled = pEngineNode->panner.pan != 0 && channelsOut != 1; + isVolumeSmoothingEnabled = pEngineNode->volumeSmoothTimeInPCMFrames > 0; /* Keep going while we've still got data available for processing. */ while (totalFramesProcessedOut < frameCountOut) { @@ -73161,6 +73749,19 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo } } + /* + If we're using smoothing, we won't be applying volume via the spatializer, but instead from a ma_gainer. In this case + we'll want to apply our volume now. + */ + if (isVolumeSmoothingEnabled) { + if (isWorkingBufferValid) { + ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pWorkingBuffer, framesJustProcessedOut); + } else { + ma_gainer_process_pcm_frames(&pEngineNode->volumeGainer, pWorkingBuffer, pRunningFramesIn, framesJustProcessedOut); + isWorkingBufferValid = MA_TRUE; + } + } + /* If at this point we still haven't actually done anything with the working buffer we need to just read straight from the input buffer. @@ -73192,11 +73793,21 @@ static void ma_engine_node_process_pcm_frames__general(ma_engine_node* pEngineNo if (channelsIn == channelsOut) { /* No channel conversion required. Just copy straight to the output buffer. */ - ma_copy_and_apply_volume_factor_f32(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, volume); + if (isVolumeSmoothingEnabled) { + /* Volume has already been applied. Just copy straight to the output buffer. */ + ma_copy_pcm_frames(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, ma_format_f32, channelsOut); + } else { + /* Volume has not been applied yet. Copy and apply volume in the same pass. */ + ma_copy_and_apply_volume_factor_f32(pRunningFramesOut, pWorkingBuffer, framesJustProcessedOut * channelsOut, volume); + } } else { /* Channel conversion required. TODO: Add support for channel maps here. */ ma_channel_map_apply_f32(pRunningFramesOut, NULL, channelsOut, pWorkingBuffer, NULL, channelsIn, framesJustProcessedOut, ma_channel_mix_mode_simple, pEngineNode->monoExpansionMode); - ma_apply_volume_factor_f32(pRunningFramesOut, framesJustProcessedOut * channelsOut, volume); + + /* If we're using smoothing, the volume will have already been applied. */ + if (!isVolumeSmoothingEnabled) { + ma_apply_volume_factor_f32(pRunningFramesOut, framesJustProcessedOut * channelsOut, volume); + } } } @@ -73299,7 +73910,7 @@ static void ma_engine_node_process_pcm_frames__sound(ma_node* pNode, const float /* If we reached the end of the sound we'll want to mark it as at the end and stop it. This should never be returned for looping sounds. */ if (result == MA_AT_END) { - c89atomic_exchange_32(&pSound->atEnd, MA_TRUE); /* This will be set to false in ma_sound_start(). */ + ma_sound_set_at_end(pSound, MA_TRUE); /* This will be set to false in ma_sound_start(). */ } pRunningFramesOut = ma_offset_pcm_frames_ptr_f32(ppFramesOut[0], totalFramesRead, ma_engine_get_channels(ma_sound_get_engine(pSound))); @@ -73420,6 +74031,7 @@ typedef struct size_t baseNodeOffset; size_t resamplerOffset; size_t spatializerOffset; + size_t gainerOffset; } ma_engine_node_heap_layout; static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pConfig, ma_engine_node_heap_layout* pHeapLayout) @@ -73429,6 +74041,7 @@ static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pCo ma_node_config baseNodeConfig; ma_linear_resampler_config resamplerConfig; ma_spatializer_config spatializerConfig; + ma_gainer_config gainerConfig; ma_uint32 channelsIn; ma_uint32 channelsOut; ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT}; /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */ @@ -73494,6 +74107,20 @@ static ma_result ma_engine_node_get_heap_layout(const ma_engine_node_config* pCo pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); + /* Gainer. Will not be used if we are not using smoothing. */ + if (pConfig->volumeSmoothTimeInPCMFrames > 0) { + gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames); + + result = ma_gainer_get_heap_size(&gainerConfig, &tempHeapSize); + if (result != MA_SUCCESS) { + return result; + } + + pHeapLayout->gainerOffset = pHeapLayout->sizeInBytes; + pHeapLayout->sizeInBytes += ma_align_64(tempHeapSize); + } + + return MA_SUCCESS; } @@ -73527,6 +74154,7 @@ MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* p ma_fader_config faderConfig; ma_spatializer_config spatializerConfig; ma_panner_config pannerConfig; + ma_gainer_config gainerConfig; ma_uint32 channelsIn; ma_uint32 channelsOut; ma_channel defaultStereoChannelMap[2] = {MA_CHANNEL_SIDE_LEFT, MA_CHANNEL_SIDE_RIGHT}; /* <-- Consistent with the default channel map of a stereo listener. Means channel conversion can run on a fast path. */ @@ -73549,15 +74177,17 @@ MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* p pEngineNode->_pHeap = pHeap; MA_ZERO_MEMORY(pHeap, heapLayout.sizeInBytes); - pEngineNode->pEngine = pConfig->pEngine; - pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine); - pEngineNode->monoExpansionMode = pConfig->monoExpansionMode; - pEngineNode->pitch = 1; - pEngineNode->oldPitch = 1; - pEngineNode->oldDopplerPitch = 1; - pEngineNode->isPitchDisabled = pConfig->isPitchDisabled; - pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled; - pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex; + pEngineNode->pEngine = pConfig->pEngine; + pEngineNode->sampleRate = (pConfig->sampleRate > 0) ? pConfig->sampleRate : ma_engine_get_sample_rate(pEngineNode->pEngine); + pEngineNode->volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames; + pEngineNode->monoExpansionMode = pConfig->monoExpansionMode; + ma_atomic_float_set(&pEngineNode->volume, 1); + pEngineNode->pitch = 1; + pEngineNode->oldPitch = 1; + pEngineNode->oldDopplerPitch = 1; + pEngineNode->isPitchDisabled = pConfig->isPitchDisabled; + pEngineNode->isSpatializationDisabled = pConfig->isSpatializationDisabled; + pEngineNode->pinnedListenerIndex = pConfig->pinnedListenerIndex; channelsIn = (pConfig->channelsIn != 0) ? pConfig->channelsIn : ma_engine_get_channels(pConfig->pEngine); channelsOut = (pConfig->channelsOut != 0) ? pConfig->channelsOut : ma_engine_get_channels(pConfig->pEngine); @@ -73637,6 +74267,18 @@ MA_API ma_result ma_engine_node_init_preallocated(const ma_engine_node_config* p goto error3; } + + /* We'll need a gainer for smoothing out volume changes if we have a non-zero smooth time. We apply this before converting to the output channel count. */ + if (pConfig->volumeSmoothTimeInPCMFrames > 0) { + gainerConfig = ma_gainer_config_init(channelsIn, pConfig->volumeSmoothTimeInPCMFrames); + + result = ma_gainer_init_preallocated(&gainerConfig, ma_offset_ptr(pHeap, heapLayout.gainerOffset), &pEngineNode->volumeGainer); + if (result != MA_SUCCESS) { + goto error3; + } + } + + return MA_SUCCESS; /* No need for allocation callbacks here because we use a preallocated heap. */ @@ -73685,6 +74327,10 @@ MA_API void ma_engine_node_uninit(ma_engine_node* pEngineNode, const ma_allocati ma_node_uninit(&pEngineNode->baseNode, pAllocationCallbacks); /* Now that the node has been uninitialized we can safely uninitialize the rest. */ + if (pEngineNode->volumeSmoothTimeInPCMFrames > 0) { + ma_gainer_uninit(&pEngineNode->volumeGainer, pAllocationCallbacks); + } + ma_spatializer_uninit(&pEngineNode->spatializer, pAllocationCallbacks); ma_linear_resampler_uninit(&pEngineNode->resampler, pAllocationCallbacks); @@ -73808,6 +74454,7 @@ MA_API ma_result ma_engine_init(const ma_engine_config* pConfig, ma_engine* pEng } pEngine->monoExpansionMode = engineConfig.monoExpansionMode; + pEngine->defaultVolumeSmoothTimeInPCMFrames = engineConfig.defaultVolumeSmoothTimeInPCMFrames; ma_allocation_callbacks_init_copy(&pEngine->allocationCallbacks, &engineConfig.allocationCallbacks); #if !defined(MA_NO_RESOURCE_MANAGER) @@ -74180,16 +74827,36 @@ MA_API ma_node* ma_engine_get_endpoint(ma_engine* pEngine) return ma_node_graph_get_endpoint(&pEngine->nodeGraph); } -MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine) +MA_API ma_uint64 ma_engine_get_time_in_pcm_frames(const ma_engine* pEngine) { return ma_node_graph_get_time(&pEngine->nodeGraph); } -MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime) +MA_API ma_uint64 ma_engine_get_time_in_milliseconds(const ma_engine* pEngine) +{ + return ma_engine_get_time_in_pcm_frames(pEngine) * 1000 / ma_engine_get_sample_rate(pEngine); +} + +MA_API ma_result ma_engine_set_time_in_pcm_frames(ma_engine* pEngine, ma_uint64 globalTime) { return ma_node_graph_set_time(&pEngine->nodeGraph, globalTime); } +MA_API ma_result ma_engine_set_time_in_milliseconds(ma_engine* pEngine, ma_uint64 globalTime) +{ + return ma_engine_set_time_in_pcm_frames(pEngine, globalTime * ma_engine_get_sample_rate(pEngine) / 1000); +} + +MA_API ma_uint64 ma_engine_get_time(const ma_engine* pEngine) +{ + return ma_engine_get_time_in_pcm_frames(pEngine); +} + +MA_API ma_result ma_engine_set_time(ma_engine* pEngine, ma_uint64 globalTime) +{ + return ma_engine_set_time_in_pcm_frames(pEngine, globalTime); +} + MA_API ma_uint32 ma_engine_get_channels(const ma_engine* pEngine) { return ma_node_graph_get_channels(&pEngine->nodeGraph); @@ -74603,9 +75270,14 @@ static ma_result ma_sound_init_from_data_source_internal(ma_engine* pEngine, con source that provides this information upfront. */ engineNodeConfig = ma_engine_node_config_init(pEngine, type, pConfig->flags); - engineNodeConfig.channelsIn = pConfig->channelsIn; - engineNodeConfig.channelsOut = pConfig->channelsOut; - engineNodeConfig.monoExpansionMode = pConfig->monoExpansionMode; + engineNodeConfig.channelsIn = pConfig->channelsIn; + engineNodeConfig.channelsOut = pConfig->channelsOut; + engineNodeConfig.volumeSmoothTimeInPCMFrames = pConfig->volumeSmoothTimeInPCMFrames; + engineNodeConfig.monoExpansionMode = pConfig->monoExpansionMode; + + if (engineNodeConfig.volumeSmoothTimeInPCMFrames == 0) { + engineNodeConfig.volumeSmoothTimeInPCMFrames = pEngine->defaultVolumeSmoothTimeInPCMFrames; + } /* If we're loading from a data source the input channel count needs to be the data source's native channel count. */ if (pConfig->pDataSource != NULL) { @@ -74791,7 +75463,7 @@ MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistin /* We need to make a clone of the data source. If the data source is not a data buffer (i.e. a stream) - the this will fail. + this will fail. */ pSound->pResourceManagerDataSource = (ma_resource_manager_data_source*)ma_malloc(sizeof(*pSound->pResourceManagerDataSource), &pEngine->allocationCallbacks); if (pSound->pResourceManagerDataSource == NULL) { @@ -74805,10 +75477,11 @@ MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistin } config = ma_sound_config_init_2(pEngine); - config.pDataSource = pSound->pResourceManagerDataSource; - config.flags = flags; - config.pInitialAttachment = pGroup; - config.monoExpansionMode = pExistingSound->engineNode.monoExpansionMode; + config.pDataSource = pSound->pResourceManagerDataSource; + config.flags = flags; + config.pInitialAttachment = pGroup; + config.monoExpansionMode = pExistingSound->engineNode.monoExpansionMode; + config.volumeSmoothTimeInPCMFrames = pExistingSound->engineNode.volumeSmoothTimeInPCMFrames; result = ma_sound_init_from_data_source_internal(pEngine, &config, pSound); if (result != MA_SUCCESS) { @@ -74818,6 +75491,9 @@ MA_API ma_result ma_sound_init_copy(ma_engine* pEngine, const ma_sound* pExistin return result; } + /* Make sure the sound is marked as the owner of the data source or else it will never get uninitialized. */ + pSound->ownsDataSource = MA_TRUE; + return MA_SUCCESS; } #endif @@ -74844,6 +75520,9 @@ MA_API ma_result ma_sound_init_ex(ma_engine* pEngine, const ma_sound_config* pCo return MA_INVALID_ARGS; } + pSound->endCallback = pConfig->endCallback; + pSound->pEndCallbackUserData = pConfig->pEndCallbackUserData; + /* We need to load the sound differently depending on whether or not we're loading from a file. */ #ifndef MA_NO_RESOURCE_MANAGER if (pConfig->pFilePath != NULL || pConfig->pFilePathW != NULL) { @@ -75407,7 +76086,7 @@ MA_API ma_bool32 ma_sound_is_playing(const ma_sound* pSound) return MA_FALSE; } - return ma_node_get_state_by_time(pSound, ma_engine_get_time(ma_sound_get_engine(pSound))) == ma_node_state_started; + return ma_node_get_state_by_time(pSound, ma_engine_get_time_in_pcm_frames(ma_sound_get_engine(pSound))) == ma_node_state_started; } MA_API ma_uint64 ma_sound_get_time_in_pcm_frames(const ma_sound* pSound) @@ -75459,7 +76138,7 @@ MA_API ma_bool32 ma_sound_at_end(const ma_sound* pSound) return MA_FALSE; } - return c89atomic_load_32(&pSound->atEnd); + return ma_sound_get_at_end(pSound); } MA_API ma_result ma_sound_seek_to_pcm_frame(ma_sound* pSound, ma_uint64 frameIndex) @@ -75568,6 +76247,23 @@ MA_API ma_result ma_sound_get_length_in_seconds(ma_sound* pSound, float* pLength return ma_data_source_get_length_in_seconds(pSound->pDataSource, pLength); } +MA_API ma_result ma_sound_set_end_callback(ma_sound* pSound, ma_sound_end_proc callback, void* pUserData) +{ + if (pSound == NULL) { + return MA_INVALID_ARGS; + } + + /* The notion of an end is only valid for sounds that are backed by a data source. */ + if (pSound->pDataSource == NULL) { + return MA_INVALID_OPERATION; + } + + pSound->endCallback = callback; + pSound->pEndCallbackUserData = pUserData; + + return MA_SUCCESS; +} + MA_API ma_result ma_sound_group_init(ma_engine* pEngine, ma_uint32 flags, ma_sound_group* pParentGroup, ma_sound_group* pGroup) { @@ -76597,10 +77293,14 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_smpl_to_metadata_obj(drwav__metadata_pars { drwav_uint8 smplHeaderData[DRWAV_SMPL_BYTES]; drwav_uint64 totalBytesRead = 0; - size_t bytesJustRead = drwav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); + size_t bytesJustRead; + if (pMetadata == NULL) { + return 0; + } + bytesJustRead = drwav__metadata_parser_read(pParser, smplHeaderData, sizeof(smplHeaderData), &totalBytesRead); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); DRWAV_ASSERT(pChunkHeader != NULL); - if (bytesJustRead == sizeof(smplHeaderData)) { + if (pMetadata != NULL && bytesJustRead == sizeof(smplHeaderData)) { drwav_uint32 iSampleLoop; pMetadata->type = drwav_metadata_type_smpl; pMetadata->data.smpl.manufacturerId = drwav_bytes_to_u32(smplHeaderData + 0); @@ -76641,7 +77341,11 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parse { drwav_uint8 cueHeaderSectionData[DRWAV_CUE_BYTES]; drwav_uint64 totalBytesRead = 0; - size_t bytesJustRead = drwav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); + size_t bytesJustRead; + if (pMetadata == NULL) { + return 0; + } + bytesJustRead = drwav__metadata_parser_read(pParser, cueHeaderSectionData, sizeof(cueHeaderSectionData), &totalBytesRead); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesJustRead == sizeof(cueHeaderSectionData)) { pMetadata->type = drwav_metadata_type_cue; @@ -76676,7 +77380,11 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_cue_to_metadata_obj(drwav__metadata_parse DRWAV_PRIVATE drwav_uint64 drwav__read_inst_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) { drwav_uint8 instData[DRWAV_INST_BYTES]; - drwav_uint64 bytesRead = drwav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); + drwav_uint64 bytesRead; + if (pMetadata == NULL) { + return 0; + } + bytesRead = drwav__metadata_parser_read(pParser, instData, sizeof(instData), NULL); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesRead == sizeof(instData)) { pMetadata->type = drwav_metadata_type_inst; @@ -76693,7 +77401,11 @@ DRWAV_PRIVATE drwav_uint64 drwav__read_inst_to_metadata_obj(drwav__metadata_pars DRWAV_PRIVATE drwav_uint64 drwav__read_acid_to_metadata_obj(drwav__metadata_parser* pParser, drwav_metadata* pMetadata) { drwav_uint8 acidData[DRWAV_ACID_BYTES]; - drwav_uint64 bytesRead = drwav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); + drwav_uint64 bytesRead; + if (pMetadata == NULL) { + return 0; + } + bytesRead = drwav__metadata_parser_read(pParser, acidData, sizeof(acidData), NULL); DRWAV_ASSERT(pParser->stage == drwav__metadata_parser_stage_read); if (bytesRead == sizeof(acidData)) { pMetadata->type = drwav_metadata_type_acid; @@ -92670,7 +93382,7 @@ For more information, please refer to =============================================================================== ALTERNATIVE 2 - MIT No Attribution =============================================================================== -Copyright 2020 David Reid +Copyright 2023 David Reid Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in From 76e39e502c826574f2ad85977258b2269bb3087d Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 17 May 2023 23:14:14 +0200 Subject: [PATCH 0082/1350] Update rtextures.c --- src/rtextures.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtextures.c b/src/rtextures.c index 5c8029e5a..6bca43d15 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -2046,7 +2046,7 @@ void ImageFlipHorizontal(Image *image) { for (int x = 0; x < image->width; x++) { - // OPTION 1: Move pixels with memcopy() + // OPTION 1: Move pixels with memcpy() //memcpy(flippedData + (y*image->width + x)*bytesPerPixel, ((unsigned char *)image->data) + (y*image->width + (image->width - 1 - x))*bytesPerPixel, bytesPerPixel); // OPTION 2: Just copy data pixel by pixel From fe6973a4f6aeaf0979783c7d865da3a70c0f55cc Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 18 May 2023 11:59:35 +0200 Subject: [PATCH 0083/1350] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b4e187cf7..dae2f2fab 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ - + **raylib is a simple and easy-to-use library to enjoy videogames programming.** From a3e78c5453e5df92cda57a178101a143753cdc51 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 18 May 2023 12:00:51 +0200 Subject: [PATCH 0084/1350] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dae2f2fab..90de36ca5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ - + **raylib is a simple and easy-to-use library to enjoy videogames programming.** From 51387dfbfba7f9e8d1698183ef279c5785fd0d71 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 18 May 2023 16:14:18 +0200 Subject: [PATCH 0085/1350] tweak --- src/rtext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtext.c b/src/rtext.c index 1f9ce1b65..dce838c29 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1340,7 +1340,7 @@ int TextToInteger(const char *text) text++; } - for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); ++i) value = value*10 + (int)(text[i] - '0'); + for (int i = 0; ((text[i] >= '0') && (text[i] <= '9')); i++) value = value*10 + (int)(text[i] - '0'); return value*sign; } From f31df7521a9e59f7a1af3f923630900ad74eb1bc Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 21 May 2023 00:14:09 +0200 Subject: [PATCH 0086/1350] REVIEWED: `GenImagePerlinNoise()`, no change --- src/rtextures.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index 6bca43d15..2d8056d64 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -833,18 +833,23 @@ Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float { for (int x = 0; x < width; x++) { - float nx = (float)(x + offsetX)*scale/(float)width; - float ny = (float)(y + offsetY)*scale/(float)height; - + float nx = (float)(x + offsetX)*(scale/(float)width); + float ny = (float)(y + offsetY)*(scale/(float)height); + + // Basic perlin noise implementation (not used) + //float p = (stb_perlin_noise3(nx, ny, 0.0f, 0, 0, 0); + + // Calculate a better perlin noise using fbm (fractal brownian motion) // Typical values to start playing with: // lacunarity = ~2.0 -- spacing between successive octaves (use exactly 2.0 for wrapping output) // gain = 0.5 -- relative weighting applied to each successive octave // octaves = 6 -- number of "octaves" of noise3() to sum + float p = stb_perlin_fbm_noise3(nx, ny, 1.0f, 2.0f, 0.5f, 6); + + // We need to normalize the data from [-1..1] to [0..1] + float np = (p + 1.0f)/2.0f; - // NOTE: We need to translate the data from [-1..1] to [0..1] - float p = (stb_perlin_fbm_noise3(nx, ny, 1.0f, 2.0f, 0.5f, 6) + 1.0f)/2.0f; - - int intensity = (int)(p*255.0f); + int intensity = (int)(np*255.0f); pixels[y*width + x] = (Color){ intensity, intensity, intensity, 255 }; } } From 3a841ac130f90c607c29aa79288b943bbfe569b1 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 21 May 2023 10:28:04 +0200 Subject: [PATCH 0087/1350] REVIEWED: `GenImagePerlinNoise()`, clamp values #3071 --- src/rtextures.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rtextures.c b/src/rtextures.c index 2d8056d64..875942586 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -846,6 +846,10 @@ Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float // octaves = 6 -- number of "octaves" of noise3() to sum float p = stb_perlin_fbm_noise3(nx, ny, 1.0f, 2.0f, 0.5f, 6); + // Clamp between -1.0f and 1.0f + if (p < -1.0f) p = -1.0f; + if (p > 1.0f) p = 1.0f; + // We need to normalize the data from [-1..1] to [0..1] float np = (p + 1.0f)/2.0f; From 1b4634702ca9f1e76477833618ccfea9093f03e5 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 21 May 2023 11:20:42 +0200 Subject: [PATCH 0088/1350] Minor tweak --- src/rcore.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 127abcf38..7a31b5ed6 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -5101,7 +5101,6 @@ void PollInputEvents(void) // Get current gamepad state // NOTE: There is no callback available, so we get it manually - // Get remapped buttons GLFWgamepadstate state = { 0 }; glfwGetGamepadState(i, &state); // This remapps all gamepads so they have their buttons mapped like an xbox controller @@ -5109,7 +5108,7 @@ void PollInputEvents(void) for (int k = 0; (buttons != NULL) && (k < GLFW_GAMEPAD_BUTTON_DPAD_LEFT + 1) && (k < MAX_GAMEPAD_BUTTONS); k++) { - GamepadButton button = -1; + int button = -1; // GamepadButton enum values assigned switch (k) { From e96dc46d38fcc73cc278ef7a1ad95ce63c6ce989 Mon Sep 17 00:00:00 2001 From: Dane Madsen Date: Sun, 21 May 2023 19:33:47 +1000 Subject: [PATCH 0089/1350] Replaced GenImageGradientH and GenImageGradientV with GenImageLinearGradient (#3074) * Replaced GenImageGradientH and GenImageGradientV with GenImageLinearGradient * renamed GenImageLinearGradient to GenImageGradientLinear --- examples/textures/textures_image_generation.c | 25 +- parser/output/raylib_api.json | 38 +- parser/output/raylib_api.lua | 29 +- parser/output/raylib_api.txt | 568 +++++++++--------- parser/output/raylib_api.xml | 22 +- projects/Geany/raylib.c.tags | 3 +- .../raylib_npp_parser/raylib_npp.xml | 17 +- .../raylib_npp_parser/raylib_to_parse.h | 3 +- src/raylib.h | 3 +- src/rtextures.c | 57 +- 10 files changed, 358 insertions(+), 407 deletions(-) diff --git a/examples/textures/textures_image_generation.c b/examples/textures/textures_image_generation.c index 1ab08ae84..8049a578e 100644 --- a/examples/textures/textures_image_generation.c +++ b/examples/textures/textures_image_generation.c @@ -13,7 +13,7 @@ #include "raylib.h" -#define NUM_TEXTURES 6 // Currently we have 7 generation algorithms +#define NUM_TEXTURES 7 // Currently we have 7 generation algorithms //------------------------------------------------------------------------------------ // Program main entry point @@ -27,8 +27,9 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [textures] example - procedural images generation"); - Image verticalGradient = GenImageGradientV(screenWidth, screenHeight, RED, BLUE); - Image horizontalGradient = GenImageGradientH(screenWidth, screenHeight, RED, BLUE); + Image verticalGradient = GenImageGradientLinear(screenWidth, screenHeight, 0, RED, BLUE); + Image horizontalGradient = GenImageGradientLinear(screenWidth, screenHeight, 90, RED, BLUE); + Image diagonalGradient = GenImageGradientLinear(screenWidth, screenHeight, 45, RED, BLUE); Image radialGradient = GenImageGradientRadial(screenWidth, screenHeight, 0.0f, WHITE, BLACK); Image checked = GenImageChecked(screenWidth, screenHeight, 32, 32, RED, BLUE); Image whiteNoise = GenImageWhiteNoise(screenWidth, screenHeight, 0.5f); @@ -38,10 +39,11 @@ int main(void) textures[0] = LoadTextureFromImage(verticalGradient); textures[1] = LoadTextureFromImage(horizontalGradient); - textures[2] = LoadTextureFromImage(radialGradient); - textures[3] = LoadTextureFromImage(checked); - textures[4] = LoadTextureFromImage(whiteNoise); - textures[5] = LoadTextureFromImage(cellular); + textures[2] = LoadTextureFromImage(diagonalGradient); + textures[3] = LoadTextureFromImage(radialGradient); + textures[4] = LoadTextureFromImage(checked); + textures[5] = LoadTextureFromImage(whiteNoise); + textures[6] = LoadTextureFromImage(cellular); // Unload image data (CPU RAM) UnloadImage(verticalGradient); @@ -83,10 +85,11 @@ int main(void) { case 0: DrawText("VERTICAL GRADIENT", 560, 10, 20, RAYWHITE); break; case 1: DrawText("HORIZONTAL GRADIENT", 540, 10, 20, RAYWHITE); break; - case 2: DrawText("RADIAL GRADIENT", 580, 10, 20, LIGHTGRAY); break; - case 3: DrawText("CHECKED", 680, 10, 20, RAYWHITE); break; - case 4: DrawText("WHITE NOISE", 640, 10, 20, RED); break; - case 5: DrawText("CELLULAR", 670, 10, 20, RAYWHITE); break; + case 2: DrawText("DIAGONAL GRADIENT", 540, 10, 20, RAYWHITE); break; + case 3: DrawText("RADIAL GRADIENT", 580, 10, 20, LIGHTGRAY); break; + case 4: DrawText("CHECKED", 680, 10, 20, RAYWHITE); break; + case 5: DrawText("WHITE NOISE", 640, 10, 20, RED); break; + case 6: DrawText("CELLULAR", 670, 10, 20, RAYWHITE); break; default: break; } diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index 62d9f4401..5af2b9aef 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -15,7 +15,7 @@ { "name": "RAYLIB_VERSION_MINOR", "type": "INT", - "value": 5, + "value": 6, "description": "" }, { @@ -27,7 +27,7 @@ { "name": "RAYLIB_VERSION", "type": "STRING", - "value": "4.5", + "value": "4.6-dev", "description": "" }, { @@ -1032,6 +1032,11 @@ "type": "Transform **", "name": "framePoses", "description": "Poses array by frame" + }, + { + "type": "char[32]", + "name": "name", + "description": "Animation name" } ] }, @@ -6236,8 +6241,8 @@ ] }, { - "name": "GenImageGradientV", - "description": "Generate image: vertical gradient", + "name": "GenImageGradientLinear", + "description": "Generate image: linear gradient", "returnType": "Image", "params": [ { @@ -6248,36 +6253,17 @@ "type": "int", "name": "height" }, - { - "type": "Color", - "name": "top" - }, - { - "type": "Color", - "name": "bottom" - } - ] - }, - { - "name": "GenImageGradientH", - "description": "Generate image: horizontal gradient", - "returnType": "Image", - "params": [ { "type": "int", - "name": "width" - }, - { - "type": "int", - "name": "height" + "name": "direction" }, { "type": "Color", - "name": "left" + "name": "start" }, { "type": "Color", - "name": "right" + "name": "end" } ] }, diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index 0a1b95856..e7101f51b 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -15,7 +15,7 @@ return { { name = "RAYLIB_VERSION_MINOR", type = "INT", - value = 5, + value = 6, description = "" }, { @@ -27,7 +27,7 @@ return { { name = "RAYLIB_VERSION", type = "STRING", - value = "4.5", + value = "4.6-dev", description = "" }, { @@ -1032,6 +1032,11 @@ return { type = "Transform **", name = "framePoses", description = "Poses array by frame" + }, + { + type = "char[32]", + name = "name", + description = "Animation name" } } }, @@ -4991,25 +4996,15 @@ return { } }, { - name = "GenImageGradientV", - description = "Generate image: vertical gradient", + name = "GenImageGradientLinear", + description = "Generate image: linear gradient", returnType = "Image", params = { {type = "int", name = "width"}, {type = "int", name = "height"}, - {type = "Color", name = "top"}, - {type = "Color", name = "bottom"} - } - }, - { - name = "GenImageGradientH", - description = "Generate image: horizontal gradient", - returnType = "Image", - params = { - {type = "int", name = "width"}, - {type = "int", name = "height"}, - {type = "Color", name = "left"}, - {type = "Color", name = "right"} + {type = "int", name = "direction"}, + {type = "Color", name = "start"}, + {type = "Color", name = "end"} } }, { diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index 4b13bc3b4..2df71232e 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -14,7 +14,7 @@ Define 002: RAYLIB_VERSION_MAJOR Define 003: RAYLIB_VERSION_MINOR Name: RAYLIB_VERSION_MINOR Type: INT - Value: 5 + Value: 6 Description: Define 004: RAYLIB_VERSION_PATCH Name: RAYLIB_VERSION_PATCH @@ -24,7 +24,7 @@ Define 004: RAYLIB_VERSION_PATCH Define 005: RAYLIB_VERSION Name: RAYLIB_VERSION Type: STRING - Value: "4.5" + Value: "4.6-dev" Description: Define 006: __declspec(x) Name: __declspec(x) @@ -456,13 +456,14 @@ Struct 21: Model (9 fields) Field[7]: int boneCount // Number of bones Field[8]: BoneInfo * bones // Bones information (skeleton) Field[9]: Transform * bindPose // Bones base transformation (pose) -Struct 22: ModelAnimation (4 fields) +Struct 22: ModelAnimation (5 fields) Name: ModelAnimation Description: ModelAnimation Field[1]: int boneCount // Number of bones Field[2]: int frameCount // Number of animation frames Field[3]: BoneInfo * bones // Bones information (skeleton) Field[4]: Transform ** framePoses // Poses array by frame + Field[5]: char[32] name // Animation name Struct 23: Ray (2 fields) Name: Ray Description: Ray, ray for raycasting @@ -963,7 +964,7 @@ Callback 006: AudioCallback() (2 input parameters) Param[1]: bufferData (type: void *) Param[2]: frames (type: unsigned int) -Functions found: 517 +Functions found: 516 Function 001: InitWindow() (3 input parameters) Name: InitWindow @@ -2415,23 +2416,16 @@ Function 245: GenImageColor() (3 input parameters) Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: color (type: Color) -Function 246: GenImageGradientV() (4 input parameters) - Name: GenImageGradientV +Function 246: GenImageGradientLinear() (5 input parameters) + Name: GenImageGradientLinear Return type: Image - Description: Generate image: vertical gradient + Description: Generate image: linear gradient Param[1]: width (type: int) Param[2]: height (type: int) - Param[3]: top (type: Color) - Param[4]: bottom (type: Color) -Function 247: GenImageGradientH() (4 input parameters) - Name: GenImageGradientH - Return type: Image - Description: Generate image: horizontal gradient - Param[1]: width (type: int) - Param[2]: height (type: int) - Param[3]: left (type: Color) - Param[4]: right (type: Color) -Function 248: GenImageGradientRadial() (5 input parameters) + Param[3]: direction (type: int) + Param[4]: start (type: Color) + Param[5]: end (type: Color) +Function 247: GenImageGradientRadial() (5 input parameters) Name: GenImageGradientRadial Return type: Image Description: Generate image: radial gradient @@ -2440,7 +2434,7 @@ Function 248: GenImageGradientRadial() (5 input parameters) Param[3]: density (type: float) Param[4]: inner (type: Color) Param[5]: outer (type: Color) -Function 249: GenImageChecked() (6 input parameters) +Function 248: GenImageChecked() (6 input parameters) Name: GenImageChecked Return type: Image Description: Generate image: checked @@ -2450,14 +2444,14 @@ Function 249: GenImageChecked() (6 input parameters) Param[4]: checksY (type: int) Param[5]: col1 (type: Color) Param[6]: col2 (type: Color) -Function 250: GenImageWhiteNoise() (3 input parameters) +Function 249: GenImageWhiteNoise() (3 input parameters) Name: GenImageWhiteNoise Return type: Image Description: Generate image: white noise Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: factor (type: float) -Function 251: GenImagePerlinNoise() (5 input parameters) +Function 250: GenImagePerlinNoise() (5 input parameters) Name: GenImagePerlinNoise Return type: Image Description: Generate image: perlin noise @@ -2466,39 +2460,39 @@ Function 251: GenImagePerlinNoise() (5 input parameters) Param[3]: offsetX (type: int) Param[4]: offsetY (type: int) Param[5]: scale (type: float) -Function 252: GenImageCellular() (3 input parameters) +Function 251: GenImageCellular() (3 input parameters) Name: GenImageCellular Return type: Image Description: Generate image: cellular algorithm, bigger tileSize means bigger cells Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: tileSize (type: int) -Function 253: GenImageText() (3 input parameters) +Function 252: GenImageText() (3 input parameters) Name: GenImageText Return type: Image Description: Generate image: grayscale image from text data Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: text (type: const char *) -Function 254: ImageCopy() (1 input parameters) +Function 253: ImageCopy() (1 input parameters) Name: ImageCopy Return type: Image Description: Create an image duplicate (useful for transformations) Param[1]: image (type: Image) -Function 255: ImageFromImage() (2 input parameters) +Function 254: ImageFromImage() (2 input parameters) Name: ImageFromImage Return type: Image Description: Create an image from another image piece Param[1]: image (type: Image) Param[2]: rec (type: Rectangle) -Function 256: ImageText() (3 input parameters) +Function 255: ImageText() (3 input parameters) Name: ImageText Return type: Image Description: Create an image from text (default font) Param[1]: text (type: const char *) Param[2]: fontSize (type: int) Param[3]: color (type: Color) -Function 257: ImageTextEx() (5 input parameters) +Function 256: ImageTextEx() (5 input parameters) Name: ImageTextEx Return type: Image Description: Create an image from text (custom sprite font) @@ -2507,69 +2501,69 @@ Function 257: ImageTextEx() (5 input parameters) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) Param[5]: tint (type: Color) -Function 258: ImageFormat() (2 input parameters) +Function 257: ImageFormat() (2 input parameters) Name: ImageFormat Return type: void Description: Convert image data to desired format Param[1]: image (type: Image *) Param[2]: newFormat (type: int) -Function 259: ImageToPOT() (2 input parameters) +Function 258: ImageToPOT() (2 input parameters) Name: ImageToPOT Return type: void Description: Convert image to POT (power-of-two) Param[1]: image (type: Image *) Param[2]: fill (type: Color) -Function 260: ImageCrop() (2 input parameters) +Function 259: ImageCrop() (2 input parameters) Name: ImageCrop Return type: void Description: Crop an image to a defined rectangle Param[1]: image (type: Image *) Param[2]: crop (type: Rectangle) -Function 261: ImageAlphaCrop() (2 input parameters) +Function 260: ImageAlphaCrop() (2 input parameters) Name: ImageAlphaCrop Return type: void Description: Crop image depending on alpha value Param[1]: image (type: Image *) Param[2]: threshold (type: float) -Function 262: ImageAlphaClear() (3 input parameters) +Function 261: ImageAlphaClear() (3 input parameters) Name: ImageAlphaClear Return type: void Description: Clear alpha channel to desired color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: threshold (type: float) -Function 263: ImageAlphaMask() (2 input parameters) +Function 262: ImageAlphaMask() (2 input parameters) Name: ImageAlphaMask Return type: void Description: Apply alpha mask to image Param[1]: image (type: Image *) Param[2]: alphaMask (type: Image) -Function 264: ImageAlphaPremultiply() (1 input parameters) +Function 263: ImageAlphaPremultiply() (1 input parameters) Name: ImageAlphaPremultiply Return type: void Description: Premultiply alpha channel Param[1]: image (type: Image *) -Function 265: ImageBlurGaussian() (2 input parameters) +Function 264: ImageBlurGaussian() (2 input parameters) Name: ImageBlurGaussian Return type: void Description: Apply Gaussian blur using a box blur approximation Param[1]: image (type: Image *) Param[2]: blurSize (type: int) -Function 266: ImageResize() (3 input parameters) +Function 265: ImageResize() (3 input parameters) Name: ImageResize Return type: void Description: Resize image (Bicubic scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 267: ImageResizeNN() (3 input parameters) +Function 266: ImageResizeNN() (3 input parameters) Name: ImageResizeNN Return type: void Description: Resize image (Nearest-Neighbor scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 268: ImageResizeCanvas() (6 input parameters) +Function 267: ImageResizeCanvas() (6 input parameters) Name: ImageResizeCanvas Return type: void Description: Resize canvas and fill with color @@ -2579,12 +2573,12 @@ Function 268: ImageResizeCanvas() (6 input parameters) Param[4]: offsetX (type: int) Param[5]: offsetY (type: int) Param[6]: fill (type: Color) -Function 269: ImageMipmaps() (1 input parameters) +Function 268: ImageMipmaps() (1 input parameters) Name: ImageMipmaps Return type: void Description: Compute all mipmap levels for a provided image Param[1]: image (type: Image *) -Function 270: ImageDither() (5 input parameters) +Function 269: ImageDither() (5 input parameters) Name: ImageDither Return type: void Description: Dither image data to 16bpp or lower (Floyd-Steinberg dithering) @@ -2593,103 +2587,103 @@ Function 270: ImageDither() (5 input parameters) Param[3]: gBpp (type: int) Param[4]: bBpp (type: int) Param[5]: aBpp (type: int) -Function 271: ImageFlipVertical() (1 input parameters) +Function 270: ImageFlipVertical() (1 input parameters) Name: ImageFlipVertical Return type: void Description: Flip image vertically Param[1]: image (type: Image *) -Function 272: ImageFlipHorizontal() (1 input parameters) +Function 271: ImageFlipHorizontal() (1 input parameters) Name: ImageFlipHorizontal Return type: void Description: Flip image horizontally Param[1]: image (type: Image *) -Function 273: ImageRotateCW() (1 input parameters) +Function 272: ImageRotateCW() (1 input parameters) Name: ImageRotateCW Return type: void Description: Rotate image clockwise 90deg Param[1]: image (type: Image *) -Function 274: ImageRotateCCW() (1 input parameters) +Function 273: ImageRotateCCW() (1 input parameters) Name: ImageRotateCCW Return type: void Description: Rotate image counter-clockwise 90deg Param[1]: image (type: Image *) -Function 275: ImageColorTint() (2 input parameters) +Function 274: ImageColorTint() (2 input parameters) Name: ImageColorTint Return type: void Description: Modify image color: tint Param[1]: image (type: Image *) Param[2]: color (type: Color) -Function 276: ImageColorInvert() (1 input parameters) +Function 275: ImageColorInvert() (1 input parameters) Name: ImageColorInvert Return type: void Description: Modify image color: invert Param[1]: image (type: Image *) -Function 277: ImageColorGrayscale() (1 input parameters) +Function 276: ImageColorGrayscale() (1 input parameters) Name: ImageColorGrayscale Return type: void Description: Modify image color: grayscale Param[1]: image (type: Image *) -Function 278: ImageColorContrast() (2 input parameters) +Function 277: ImageColorContrast() (2 input parameters) Name: ImageColorContrast Return type: void Description: Modify image color: contrast (-100 to 100) Param[1]: image (type: Image *) Param[2]: contrast (type: float) -Function 279: ImageColorBrightness() (2 input parameters) +Function 278: ImageColorBrightness() (2 input parameters) Name: ImageColorBrightness Return type: void Description: Modify image color: brightness (-255 to 255) Param[1]: image (type: Image *) Param[2]: brightness (type: int) -Function 280: ImageColorReplace() (3 input parameters) +Function 279: ImageColorReplace() (3 input parameters) Name: ImageColorReplace Return type: void Description: Modify image color: replace color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: replace (type: Color) -Function 281: LoadImageColors() (1 input parameters) +Function 280: LoadImageColors() (1 input parameters) Name: LoadImageColors Return type: Color * Description: Load color data from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) -Function 282: LoadImagePalette() (3 input parameters) +Function 281: LoadImagePalette() (3 input parameters) Name: LoadImagePalette Return type: Color * Description: Load colors palette from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) Param[2]: maxPaletteSize (type: int) Param[3]: colorCount (type: int *) -Function 283: UnloadImageColors() (1 input parameters) +Function 282: UnloadImageColors() (1 input parameters) Name: UnloadImageColors Return type: void Description: Unload color data loaded with LoadImageColors() Param[1]: colors (type: Color *) -Function 284: UnloadImagePalette() (1 input parameters) +Function 283: UnloadImagePalette() (1 input parameters) Name: UnloadImagePalette Return type: void Description: Unload colors palette loaded with LoadImagePalette() Param[1]: colors (type: Color *) -Function 285: GetImageAlphaBorder() (2 input parameters) +Function 284: GetImageAlphaBorder() (2 input parameters) Name: GetImageAlphaBorder Return type: Rectangle Description: Get image alpha border rectangle Param[1]: image (type: Image) Param[2]: threshold (type: float) -Function 286: GetImageColor() (3 input parameters) +Function 285: GetImageColor() (3 input parameters) Name: GetImageColor Return type: Color Description: Get image pixel color at (x, y) position Param[1]: image (type: Image) Param[2]: x (type: int) Param[3]: y (type: int) -Function 287: ImageClearBackground() (2 input parameters) +Function 286: ImageClearBackground() (2 input parameters) Name: ImageClearBackground Return type: void Description: Clear image background with given color Param[1]: dst (type: Image *) Param[2]: color (type: Color) -Function 288: ImageDrawPixel() (4 input parameters) +Function 287: ImageDrawPixel() (4 input parameters) Name: ImageDrawPixel Return type: void Description: Draw pixel within an image @@ -2697,14 +2691,14 @@ Function 288: ImageDrawPixel() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: color (type: Color) -Function 289: ImageDrawPixelV() (3 input parameters) +Function 288: ImageDrawPixelV() (3 input parameters) Name: ImageDrawPixelV Return type: void Description: Draw pixel within an image (Vector version) Param[1]: dst (type: Image *) Param[2]: position (type: Vector2) Param[3]: color (type: Color) -Function 290: ImageDrawLine() (6 input parameters) +Function 289: ImageDrawLine() (6 input parameters) Name: ImageDrawLine Return type: void Description: Draw line within an image @@ -2714,7 +2708,7 @@ Function 290: ImageDrawLine() (6 input parameters) Param[4]: endPosX (type: int) Param[5]: endPosY (type: int) Param[6]: color (type: Color) -Function 291: ImageDrawLineV() (4 input parameters) +Function 290: ImageDrawLineV() (4 input parameters) Name: ImageDrawLineV Return type: void Description: Draw line within an image (Vector version) @@ -2722,7 +2716,7 @@ Function 291: ImageDrawLineV() (4 input parameters) Param[2]: start (type: Vector2) Param[3]: end (type: Vector2) Param[4]: color (type: Color) -Function 292: ImageDrawCircle() (5 input parameters) +Function 291: ImageDrawCircle() (5 input parameters) Name: ImageDrawCircle Return type: void Description: Draw a filled circle within an image @@ -2731,7 +2725,7 @@ Function 292: ImageDrawCircle() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 293: ImageDrawCircleV() (4 input parameters) +Function 292: ImageDrawCircleV() (4 input parameters) Name: ImageDrawCircleV Return type: void Description: Draw a filled circle within an image (Vector version) @@ -2739,7 +2733,7 @@ Function 293: ImageDrawCircleV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 294: ImageDrawCircleLines() (5 input parameters) +Function 293: ImageDrawCircleLines() (5 input parameters) Name: ImageDrawCircleLines Return type: void Description: Draw circle outline within an image @@ -2748,7 +2742,7 @@ Function 294: ImageDrawCircleLines() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 295: ImageDrawCircleLinesV() (4 input parameters) +Function 294: ImageDrawCircleLinesV() (4 input parameters) Name: ImageDrawCircleLinesV Return type: void Description: Draw circle outline within an image (Vector version) @@ -2756,7 +2750,7 @@ Function 295: ImageDrawCircleLinesV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 296: ImageDrawRectangle() (6 input parameters) +Function 295: ImageDrawRectangle() (6 input parameters) Name: ImageDrawRectangle Return type: void Description: Draw rectangle within an image @@ -2766,7 +2760,7 @@ Function 296: ImageDrawRectangle() (6 input parameters) Param[4]: width (type: int) Param[5]: height (type: int) Param[6]: color (type: Color) -Function 297: ImageDrawRectangleV() (4 input parameters) +Function 296: ImageDrawRectangleV() (4 input parameters) Name: ImageDrawRectangleV Return type: void Description: Draw rectangle within an image (Vector version) @@ -2774,14 +2768,14 @@ Function 297: ImageDrawRectangleV() (4 input parameters) Param[2]: position (type: Vector2) Param[3]: size (type: Vector2) Param[4]: color (type: Color) -Function 298: ImageDrawRectangleRec() (3 input parameters) +Function 297: ImageDrawRectangleRec() (3 input parameters) Name: ImageDrawRectangleRec Return type: void Description: Draw rectangle within an image Param[1]: dst (type: Image *) Param[2]: rec (type: Rectangle) Param[3]: color (type: Color) -Function 299: ImageDrawRectangleLines() (4 input parameters) +Function 298: ImageDrawRectangleLines() (4 input parameters) Name: ImageDrawRectangleLines Return type: void Description: Draw rectangle lines within an image @@ -2789,7 +2783,7 @@ Function 299: ImageDrawRectangleLines() (4 input parameters) Param[2]: rec (type: Rectangle) Param[3]: thick (type: int) Param[4]: color (type: Color) -Function 300: ImageDraw() (5 input parameters) +Function 299: ImageDraw() (5 input parameters) Name: ImageDraw Return type: void Description: Draw a source image within a destination image (tint applied to source) @@ -2798,7 +2792,7 @@ Function 300: ImageDraw() (5 input parameters) Param[3]: srcRec (type: Rectangle) Param[4]: dstRec (type: Rectangle) Param[5]: tint (type: Color) -Function 301: ImageDrawText() (6 input parameters) +Function 300: ImageDrawText() (6 input parameters) Name: ImageDrawText Return type: void Description: Draw text (using default font) within an image (destination) @@ -2808,7 +2802,7 @@ Function 301: ImageDrawText() (6 input parameters) Param[4]: posY (type: int) Param[5]: fontSize (type: int) Param[6]: color (type: Color) -Function 302: ImageDrawTextEx() (7 input parameters) +Function 301: ImageDrawTextEx() (7 input parameters) Name: ImageDrawTextEx Return type: void Description: Draw text (custom sprite font) within an image (destination) @@ -2819,79 +2813,79 @@ Function 302: ImageDrawTextEx() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 303: LoadTexture() (1 input parameters) +Function 302: LoadTexture() (1 input parameters) Name: LoadTexture Return type: Texture2D Description: Load texture from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 304: LoadTextureFromImage() (1 input parameters) +Function 303: LoadTextureFromImage() (1 input parameters) Name: LoadTextureFromImage Return type: Texture2D Description: Load texture from image data Param[1]: image (type: Image) -Function 305: LoadTextureCubemap() (2 input parameters) +Function 304: LoadTextureCubemap() (2 input parameters) Name: LoadTextureCubemap Return type: TextureCubemap Description: Load cubemap from image, multiple image cubemap layouts supported Param[1]: image (type: Image) Param[2]: layout (type: int) -Function 306: LoadRenderTexture() (2 input parameters) +Function 305: LoadRenderTexture() (2 input parameters) Name: LoadRenderTexture Return type: RenderTexture2D Description: Load texture for rendering (framebuffer) Param[1]: width (type: int) Param[2]: height (type: int) -Function 307: IsTextureReady() (1 input parameters) +Function 306: IsTextureReady() (1 input parameters) Name: IsTextureReady Return type: bool Description: Check if a texture is ready Param[1]: texture (type: Texture2D) -Function 308: UnloadTexture() (1 input parameters) +Function 307: UnloadTexture() (1 input parameters) Name: UnloadTexture Return type: void Description: Unload texture from GPU memory (VRAM) Param[1]: texture (type: Texture2D) -Function 309: IsRenderTextureReady() (1 input parameters) +Function 308: IsRenderTextureReady() (1 input parameters) Name: IsRenderTextureReady Return type: bool Description: Check if a render texture is ready Param[1]: target (type: RenderTexture2D) -Function 310: UnloadRenderTexture() (1 input parameters) +Function 309: UnloadRenderTexture() (1 input parameters) Name: UnloadRenderTexture Return type: void Description: Unload render texture from GPU memory (VRAM) Param[1]: target (type: RenderTexture2D) -Function 311: UpdateTexture() (2 input parameters) +Function 310: UpdateTexture() (2 input parameters) Name: UpdateTexture Return type: void Description: Update GPU texture with new data Param[1]: texture (type: Texture2D) Param[2]: pixels (type: const void *) -Function 312: UpdateTextureRec() (3 input parameters) +Function 311: UpdateTextureRec() (3 input parameters) Name: UpdateTextureRec Return type: void Description: Update GPU texture rectangle with new data Param[1]: texture (type: Texture2D) Param[2]: rec (type: Rectangle) Param[3]: pixels (type: const void *) -Function 313: GenTextureMipmaps() (1 input parameters) +Function 312: GenTextureMipmaps() (1 input parameters) Name: GenTextureMipmaps Return type: void Description: Generate GPU mipmaps for a texture Param[1]: texture (type: Texture2D *) -Function 314: SetTextureFilter() (2 input parameters) +Function 313: SetTextureFilter() (2 input parameters) Name: SetTextureFilter Return type: void Description: Set texture scaling filter mode Param[1]: texture (type: Texture2D) Param[2]: filter (type: int) -Function 315: SetTextureWrap() (2 input parameters) +Function 314: SetTextureWrap() (2 input parameters) Name: SetTextureWrap Return type: void Description: Set texture wrapping mode Param[1]: texture (type: Texture2D) Param[2]: wrap (type: int) -Function 316: DrawTexture() (4 input parameters) +Function 315: DrawTexture() (4 input parameters) Name: DrawTexture Return type: void Description: Draw a Texture2D @@ -2899,14 +2893,14 @@ Function 316: DrawTexture() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: tint (type: Color) -Function 317: DrawTextureV() (3 input parameters) +Function 316: DrawTextureV() (3 input parameters) Name: DrawTextureV Return type: void Description: Draw a Texture2D with position defined as Vector2 Param[1]: texture (type: Texture2D) Param[2]: position (type: Vector2) Param[3]: tint (type: Color) -Function 318: DrawTextureEx() (5 input parameters) +Function 317: DrawTextureEx() (5 input parameters) Name: DrawTextureEx Return type: void Description: Draw a Texture2D with extended parameters @@ -2915,7 +2909,7 @@ Function 318: DrawTextureEx() (5 input parameters) Param[3]: rotation (type: float) Param[4]: scale (type: float) Param[5]: tint (type: Color) -Function 319: DrawTextureRec() (4 input parameters) +Function 318: DrawTextureRec() (4 input parameters) Name: DrawTextureRec Return type: void Description: Draw a part of a texture defined by a rectangle @@ -2923,7 +2917,7 @@ Function 319: DrawTextureRec() (4 input parameters) Param[2]: source (type: Rectangle) Param[3]: position (type: Vector2) Param[4]: tint (type: Color) -Function 320: DrawTexturePro() (6 input parameters) +Function 319: DrawTexturePro() (6 input parameters) Name: DrawTexturePro Return type: void Description: Draw a part of a texture defined by a rectangle with 'pro' parameters @@ -2933,7 +2927,7 @@ Function 320: DrawTexturePro() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 321: DrawTextureNPatch() (6 input parameters) +Function 320: DrawTextureNPatch() (6 input parameters) Name: DrawTextureNPatch Return type: void Description: Draws a texture (or part of it) that stretches or shrinks nicely @@ -2943,106 +2937,106 @@ Function 321: DrawTextureNPatch() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 322: Fade() (2 input parameters) +Function 321: Fade() (2 input parameters) Name: Fade Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 323: ColorToInt() (1 input parameters) +Function 322: ColorToInt() (1 input parameters) Name: ColorToInt Return type: int Description: Get hexadecimal value for a Color Param[1]: color (type: Color) -Function 324: ColorNormalize() (1 input parameters) +Function 323: ColorNormalize() (1 input parameters) Name: ColorNormalize Return type: Vector4 Description: Get Color normalized as float [0..1] Param[1]: color (type: Color) -Function 325: ColorFromNormalized() (1 input parameters) +Function 324: ColorFromNormalized() (1 input parameters) Name: ColorFromNormalized Return type: Color Description: Get Color from normalized values [0..1] Param[1]: normalized (type: Vector4) -Function 326: ColorToHSV() (1 input parameters) +Function 325: ColorToHSV() (1 input parameters) Name: ColorToHSV Return type: Vector3 Description: Get HSV values for a Color, hue [0..360], saturation/value [0..1] Param[1]: color (type: Color) -Function 327: ColorFromHSV() (3 input parameters) +Function 326: ColorFromHSV() (3 input parameters) Name: ColorFromHSV Return type: Color Description: Get a Color from HSV values, hue [0..360], saturation/value [0..1] Param[1]: hue (type: float) Param[2]: saturation (type: float) Param[3]: value (type: float) -Function 328: ColorTint() (2 input parameters) +Function 327: ColorTint() (2 input parameters) Name: ColorTint Return type: Color Description: Get color multiplied with another color Param[1]: color (type: Color) Param[2]: tint (type: Color) -Function 329: ColorBrightness() (2 input parameters) +Function 328: ColorBrightness() (2 input parameters) Name: ColorBrightness Return type: Color Description: Get color with brightness correction, brightness factor goes from -1.0f to 1.0f Param[1]: color (type: Color) Param[2]: factor (type: float) -Function 330: ColorContrast() (2 input parameters) +Function 329: ColorContrast() (2 input parameters) Name: ColorContrast Return type: Color Description: Get color with contrast correction, contrast values between -1.0f and 1.0f Param[1]: color (type: Color) Param[2]: contrast (type: float) -Function 331: ColorAlpha() (2 input parameters) +Function 330: ColorAlpha() (2 input parameters) Name: ColorAlpha Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 332: ColorAlphaBlend() (3 input parameters) +Function 331: ColorAlphaBlend() (3 input parameters) Name: ColorAlphaBlend Return type: Color Description: Get src alpha-blended into dst color with tint Param[1]: dst (type: Color) Param[2]: src (type: Color) Param[3]: tint (type: Color) -Function 333: GetColor() (1 input parameters) +Function 332: GetColor() (1 input parameters) Name: GetColor Return type: Color Description: Get Color structure from hexadecimal value Param[1]: hexValue (type: unsigned int) -Function 334: GetPixelColor() (2 input parameters) +Function 333: GetPixelColor() (2 input parameters) Name: GetPixelColor Return type: Color Description: Get Color from a source pixel pointer of certain format Param[1]: srcPtr (type: void *) Param[2]: format (type: int) -Function 335: SetPixelColor() (3 input parameters) +Function 334: SetPixelColor() (3 input parameters) Name: SetPixelColor Return type: void Description: Set color formatted into destination pixel pointer Param[1]: dstPtr (type: void *) Param[2]: color (type: Color) Param[3]: format (type: int) -Function 336: GetPixelDataSize() (3 input parameters) +Function 335: GetPixelDataSize() (3 input parameters) Name: GetPixelDataSize Return type: int Description: Get pixel data size in bytes for certain format Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: format (type: int) -Function 337: GetFontDefault() (0 input parameters) +Function 336: GetFontDefault() (0 input parameters) Name: GetFontDefault Return type: Font Description: Get the default Font No input parameters -Function 338: LoadFont() (1 input parameters) +Function 337: LoadFont() (1 input parameters) Name: LoadFont Return type: Font Description: Load font from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 339: LoadFontEx() (4 input parameters) +Function 338: LoadFontEx() (4 input parameters) Name: LoadFontEx Return type: Font Description: Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set @@ -3050,14 +3044,14 @@ Function 339: LoadFontEx() (4 input parameters) Param[2]: fontSize (type: int) Param[3]: fontChars (type: int *) Param[4]: glyphCount (type: int) -Function 340: LoadFontFromImage() (3 input parameters) +Function 339: LoadFontFromImage() (3 input parameters) Name: LoadFontFromImage Return type: Font Description: Load font from Image (XNA style) Param[1]: image (type: Image) Param[2]: key (type: Color) Param[3]: firstChar (type: int) -Function 341: LoadFontFromMemory() (6 input parameters) +Function 340: LoadFontFromMemory() (6 input parameters) Name: LoadFontFromMemory Return type: Font Description: Load font from memory buffer, fileType refers to extension: i.e. '.ttf' @@ -3067,12 +3061,12 @@ Function 341: LoadFontFromMemory() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: fontChars (type: int *) Param[6]: glyphCount (type: int) -Function 342: IsFontReady() (1 input parameters) +Function 341: IsFontReady() (1 input parameters) Name: IsFontReady Return type: bool Description: Check if a font is ready Param[1]: font (type: Font) -Function 343: LoadFontData() (6 input parameters) +Function 342: LoadFontData() (6 input parameters) Name: LoadFontData Return type: GlyphInfo * Description: Load font data for further use @@ -3082,7 +3076,7 @@ Function 343: LoadFontData() (6 input parameters) Param[4]: fontChars (type: int *) Param[5]: glyphCount (type: int) Param[6]: type (type: int) -Function 344: GenImageFontAtlas() (6 input parameters) +Function 343: GenImageFontAtlas() (6 input parameters) Name: GenImageFontAtlas Return type: Image Description: Generate image font atlas using chars info @@ -3092,30 +3086,30 @@ Function 344: GenImageFontAtlas() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: padding (type: int) Param[6]: packMethod (type: int) -Function 345: UnloadFontData() (2 input parameters) +Function 344: UnloadFontData() (2 input parameters) Name: UnloadFontData Return type: void Description: Unload font chars info data (RAM) Param[1]: chars (type: GlyphInfo *) Param[2]: glyphCount (type: int) -Function 346: UnloadFont() (1 input parameters) +Function 345: UnloadFont() (1 input parameters) Name: UnloadFont Return type: void Description: Unload font from GPU memory (VRAM) Param[1]: font (type: Font) -Function 347: ExportFontAsCode() (2 input parameters) +Function 346: ExportFontAsCode() (2 input parameters) Name: ExportFontAsCode Return type: bool Description: Export font as code file, returns true on success Param[1]: font (type: Font) Param[2]: fileName (type: const char *) -Function 348: DrawFPS() (2 input parameters) +Function 347: DrawFPS() (2 input parameters) Name: DrawFPS Return type: void Description: Draw current FPS Param[1]: posX (type: int) Param[2]: posY (type: int) -Function 349: DrawText() (5 input parameters) +Function 348: DrawText() (5 input parameters) Name: DrawText Return type: void Description: Draw text (using default font) @@ -3124,7 +3118,7 @@ Function 349: DrawText() (5 input parameters) Param[3]: posY (type: int) Param[4]: fontSize (type: int) Param[5]: color (type: Color) -Function 350: DrawTextEx() (6 input parameters) +Function 349: DrawTextEx() (6 input parameters) Name: DrawTextEx Return type: void Description: Draw text using font and additional parameters @@ -3134,7 +3128,7 @@ Function 350: DrawTextEx() (6 input parameters) Param[4]: fontSize (type: float) Param[5]: spacing (type: float) Param[6]: tint (type: Color) -Function 351: DrawTextPro() (8 input parameters) +Function 350: DrawTextPro() (8 input parameters) Name: DrawTextPro Return type: void Description: Draw text using Font and pro parameters (rotation) @@ -3146,7 +3140,7 @@ Function 351: DrawTextPro() (8 input parameters) Param[6]: fontSize (type: float) Param[7]: spacing (type: float) Param[8]: tint (type: Color) -Function 352: DrawTextCodepoint() (5 input parameters) +Function 351: DrawTextCodepoint() (5 input parameters) Name: DrawTextCodepoint Return type: void Description: Draw one character (codepoint) @@ -3155,7 +3149,7 @@ Function 352: DrawTextCodepoint() (5 input parameters) Param[3]: position (type: Vector2) Param[4]: fontSize (type: float) Param[5]: tint (type: Color) -Function 353: DrawTextCodepoints() (7 input parameters) +Function 352: DrawTextCodepoints() (7 input parameters) Name: DrawTextCodepoints Return type: void Description: Draw multiple character (codepoint) @@ -3166,13 +3160,13 @@ Function 353: DrawTextCodepoints() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 354: MeasureText() (2 input parameters) +Function 353: MeasureText() (2 input parameters) Name: MeasureText Return type: int Description: Measure string width for default font Param[1]: text (type: const char *) Param[2]: fontSize (type: int) -Function 355: MeasureTextEx() (4 input parameters) +Function 354: MeasureTextEx() (4 input parameters) Name: MeasureTextEx Return type: Vector2 Description: Measure string size for Font @@ -3180,180 +3174,180 @@ Function 355: MeasureTextEx() (4 input parameters) Param[2]: text (type: const char *) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) -Function 356: GetGlyphIndex() (2 input parameters) +Function 355: GetGlyphIndex() (2 input parameters) Name: GetGlyphIndex Return type: int Description: Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 357: GetGlyphInfo() (2 input parameters) +Function 356: GetGlyphInfo() (2 input parameters) Name: GetGlyphInfo Return type: GlyphInfo Description: Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 358: GetGlyphAtlasRec() (2 input parameters) +Function 357: GetGlyphAtlasRec() (2 input parameters) Name: GetGlyphAtlasRec Return type: Rectangle Description: Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 359: LoadUTF8() (2 input parameters) +Function 358: LoadUTF8() (2 input parameters) Name: LoadUTF8 Return type: char * Description: Load UTF-8 text encoded from codepoints array Param[1]: codepoints (type: const int *) Param[2]: length (type: int) -Function 360: UnloadUTF8() (1 input parameters) +Function 359: UnloadUTF8() (1 input parameters) Name: UnloadUTF8 Return type: void Description: Unload UTF-8 text encoded from codepoints array Param[1]: text (type: char *) -Function 361: LoadCodepoints() (2 input parameters) +Function 360: LoadCodepoints() (2 input parameters) Name: LoadCodepoints Return type: int * Description: Load all codepoints from a UTF-8 text string, codepoints count returned by parameter Param[1]: text (type: const char *) Param[2]: count (type: int *) -Function 362: UnloadCodepoints() (1 input parameters) +Function 361: UnloadCodepoints() (1 input parameters) Name: UnloadCodepoints Return type: void Description: Unload codepoints data from memory Param[1]: codepoints (type: int *) -Function 363: GetCodepointCount() (1 input parameters) +Function 362: GetCodepointCount() (1 input parameters) Name: GetCodepointCount Return type: int Description: Get total number of codepoints in a UTF-8 encoded string Param[1]: text (type: const char *) -Function 364: GetCodepoint() (2 input parameters) +Function 363: GetCodepoint() (2 input parameters) Name: GetCodepoint Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 365: GetCodepointNext() (2 input parameters) +Function 364: GetCodepointNext() (2 input parameters) Name: GetCodepointNext Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 366: GetCodepointPrevious() (2 input parameters) +Function 365: GetCodepointPrevious() (2 input parameters) Name: GetCodepointPrevious Return type: int Description: Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 367: CodepointToUTF8() (2 input parameters) +Function 366: CodepointToUTF8() (2 input parameters) Name: CodepointToUTF8 Return type: const char * Description: Encode one codepoint into UTF-8 byte array (array length returned as parameter) Param[1]: codepoint (type: int) Param[2]: utf8Size (type: int *) -Function 368: TextCopy() (2 input parameters) +Function 367: TextCopy() (2 input parameters) Name: TextCopy Return type: int Description: Copy one string to another, returns bytes copied Param[1]: dst (type: char *) Param[2]: src (type: const char *) -Function 369: TextIsEqual() (2 input parameters) +Function 368: TextIsEqual() (2 input parameters) Name: TextIsEqual Return type: bool Description: Check if two text string are equal Param[1]: text1 (type: const char *) Param[2]: text2 (type: const char *) -Function 370: TextLength() (1 input parameters) +Function 369: TextLength() (1 input parameters) Name: TextLength Return type: unsigned int Description: Get text length, checks for '\0' ending Param[1]: text (type: const char *) -Function 371: TextFormat() (2 input parameters) +Function 370: TextFormat() (2 input parameters) Name: TextFormat Return type: const char * Description: Text formatting with variables (sprintf() style) Param[1]: text (type: const char *) Param[2]: args (type: ...) -Function 372: TextSubtext() (3 input parameters) +Function 371: TextSubtext() (3 input parameters) Name: TextSubtext Return type: const char * Description: Get a piece of a text string Param[1]: text (type: const char *) Param[2]: position (type: int) Param[3]: length (type: int) -Function 373: TextReplace() (3 input parameters) +Function 372: TextReplace() (3 input parameters) Name: TextReplace Return type: char * Description: Replace text string (WARNING: memory must be freed!) Param[1]: text (type: char *) Param[2]: replace (type: const char *) Param[3]: by (type: const char *) -Function 374: TextInsert() (3 input parameters) +Function 373: TextInsert() (3 input parameters) Name: TextInsert Return type: char * Description: Insert text in a position (WARNING: memory must be freed!) Param[1]: text (type: const char *) Param[2]: insert (type: const char *) Param[3]: position (type: int) -Function 375: TextJoin() (3 input parameters) +Function 374: TextJoin() (3 input parameters) Name: TextJoin Return type: const char * Description: Join text strings with delimiter Param[1]: textList (type: const char **) Param[2]: count (type: int) Param[3]: delimiter (type: const char *) -Function 376: TextSplit() (3 input parameters) +Function 375: TextSplit() (3 input parameters) Name: TextSplit Return type: const char ** Description: Split text into multiple strings Param[1]: text (type: const char *) Param[2]: delimiter (type: char) Param[3]: count (type: int *) -Function 377: TextAppend() (3 input parameters) +Function 376: TextAppend() (3 input parameters) Name: TextAppend Return type: void Description: Append text at specific position and move cursor! Param[1]: text (type: char *) Param[2]: append (type: const char *) Param[3]: position (type: int *) -Function 378: TextFindIndex() (2 input parameters) +Function 377: TextFindIndex() (2 input parameters) Name: TextFindIndex Return type: int Description: Find first text occurrence within a string Param[1]: text (type: const char *) Param[2]: find (type: const char *) -Function 379: TextToUpper() (1 input parameters) +Function 378: TextToUpper() (1 input parameters) Name: TextToUpper Return type: const char * Description: Get upper case version of provided string Param[1]: text (type: const char *) -Function 380: TextToLower() (1 input parameters) +Function 379: TextToLower() (1 input parameters) Name: TextToLower Return type: const char * Description: Get lower case version of provided string Param[1]: text (type: const char *) -Function 381: TextToPascal() (1 input parameters) +Function 380: TextToPascal() (1 input parameters) Name: TextToPascal Return type: const char * Description: Get Pascal case notation version of provided string Param[1]: text (type: const char *) -Function 382: TextToInteger() (1 input parameters) +Function 381: TextToInteger() (1 input parameters) Name: TextToInteger Return type: int Description: Get integer value from text (negative values not supported) Param[1]: text (type: const char *) -Function 383: DrawLine3D() (3 input parameters) +Function 382: DrawLine3D() (3 input parameters) Name: DrawLine3D Return type: void Description: Draw a line in 3D world space Param[1]: startPos (type: Vector3) Param[2]: endPos (type: Vector3) Param[3]: color (type: Color) -Function 384: DrawPoint3D() (2 input parameters) +Function 383: DrawPoint3D() (2 input parameters) Name: DrawPoint3D Return type: void Description: Draw a point in 3D space, actually a small line Param[1]: position (type: Vector3) Param[2]: color (type: Color) -Function 385: DrawCircle3D() (5 input parameters) +Function 384: DrawCircle3D() (5 input parameters) Name: DrawCircle3D Return type: void Description: Draw a circle in 3D world space @@ -3362,7 +3356,7 @@ Function 385: DrawCircle3D() (5 input parameters) Param[3]: rotationAxis (type: Vector3) Param[4]: rotationAngle (type: float) Param[5]: color (type: Color) -Function 386: DrawTriangle3D() (4 input parameters) +Function 385: DrawTriangle3D() (4 input parameters) Name: DrawTriangle3D Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -3370,14 +3364,14 @@ Function 386: DrawTriangle3D() (4 input parameters) Param[2]: v2 (type: Vector3) Param[3]: v3 (type: Vector3) Param[4]: color (type: Color) -Function 387: DrawTriangleStrip3D() (3 input parameters) +Function 386: DrawTriangleStrip3D() (3 input parameters) Name: DrawTriangleStrip3D Return type: void Description: Draw a triangle strip defined by points Param[1]: points (type: Vector3 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 388: DrawCube() (5 input parameters) +Function 387: DrawCube() (5 input parameters) Name: DrawCube Return type: void Description: Draw cube @@ -3386,14 +3380,14 @@ Function 388: DrawCube() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 389: DrawCubeV() (3 input parameters) +Function 388: DrawCubeV() (3 input parameters) Name: DrawCubeV Return type: void Description: Draw cube (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 390: DrawCubeWires() (5 input parameters) +Function 389: DrawCubeWires() (5 input parameters) Name: DrawCubeWires Return type: void Description: Draw cube wires @@ -3402,21 +3396,21 @@ Function 390: DrawCubeWires() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 391: DrawCubeWiresV() (3 input parameters) +Function 390: DrawCubeWiresV() (3 input parameters) Name: DrawCubeWiresV Return type: void Description: Draw cube wires (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 392: DrawSphere() (3 input parameters) +Function 391: DrawSphere() (3 input parameters) Name: DrawSphere Return type: void Description: Draw sphere Param[1]: centerPos (type: Vector3) Param[2]: radius (type: float) Param[3]: color (type: Color) -Function 393: DrawSphereEx() (5 input parameters) +Function 392: DrawSphereEx() (5 input parameters) Name: DrawSphereEx Return type: void Description: Draw sphere with extended parameters @@ -3425,7 +3419,7 @@ Function 393: DrawSphereEx() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 394: DrawSphereWires() (5 input parameters) +Function 393: DrawSphereWires() (5 input parameters) Name: DrawSphereWires Return type: void Description: Draw sphere wires @@ -3434,7 +3428,7 @@ Function 394: DrawSphereWires() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 395: DrawCylinder() (6 input parameters) +Function 394: DrawCylinder() (6 input parameters) Name: DrawCylinder Return type: void Description: Draw a cylinder/cone @@ -3444,7 +3438,7 @@ Function 395: DrawCylinder() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 396: DrawCylinderEx() (6 input parameters) +Function 395: DrawCylinderEx() (6 input parameters) Name: DrawCylinderEx Return type: void Description: Draw a cylinder with base at startPos and top at endPos @@ -3454,7 +3448,7 @@ Function 396: DrawCylinderEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 397: DrawCylinderWires() (6 input parameters) +Function 396: DrawCylinderWires() (6 input parameters) Name: DrawCylinderWires Return type: void Description: Draw a cylinder/cone wires @@ -3464,7 +3458,7 @@ Function 397: DrawCylinderWires() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 398: DrawCylinderWiresEx() (6 input parameters) +Function 397: DrawCylinderWiresEx() (6 input parameters) Name: DrawCylinderWiresEx Return type: void Description: Draw a cylinder wires with base at startPos and top at endPos @@ -3474,7 +3468,7 @@ Function 398: DrawCylinderWiresEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 399: DrawCapsule() (6 input parameters) +Function 398: DrawCapsule() (6 input parameters) Name: DrawCapsule Return type: void Description: Draw a capsule with the center of its sphere caps at startPos and endPos @@ -3484,7 +3478,7 @@ Function 399: DrawCapsule() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 400: DrawCapsuleWires() (6 input parameters) +Function 399: DrawCapsuleWires() (6 input parameters) Name: DrawCapsuleWires Return type: void Description: Draw capsule wireframe with the center of its sphere caps at startPos and endPos @@ -3494,51 +3488,51 @@ Function 400: DrawCapsuleWires() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 401: DrawPlane() (3 input parameters) +Function 400: DrawPlane() (3 input parameters) Name: DrawPlane Return type: void Description: Draw a plane XZ Param[1]: centerPos (type: Vector3) Param[2]: size (type: Vector2) Param[3]: color (type: Color) -Function 402: DrawRay() (2 input parameters) +Function 401: DrawRay() (2 input parameters) Name: DrawRay Return type: void Description: Draw a ray line Param[1]: ray (type: Ray) Param[2]: color (type: Color) -Function 403: DrawGrid() (2 input parameters) +Function 402: DrawGrid() (2 input parameters) Name: DrawGrid Return type: void Description: Draw a grid (centered at (0, 0, 0)) Param[1]: slices (type: int) Param[2]: spacing (type: float) -Function 404: LoadModel() (1 input parameters) +Function 403: LoadModel() (1 input parameters) Name: LoadModel Return type: Model Description: Load model from files (meshes and materials) Param[1]: fileName (type: const char *) -Function 405: LoadModelFromMesh() (1 input parameters) +Function 404: LoadModelFromMesh() (1 input parameters) Name: LoadModelFromMesh Return type: Model Description: Load model from generated mesh (default material) Param[1]: mesh (type: Mesh) -Function 406: IsModelReady() (1 input parameters) +Function 405: IsModelReady() (1 input parameters) Name: IsModelReady Return type: bool Description: Check if a model is ready Param[1]: model (type: Model) -Function 407: UnloadModel() (1 input parameters) +Function 406: UnloadModel() (1 input parameters) Name: UnloadModel Return type: void Description: Unload model (including meshes) from memory (RAM and/or VRAM) Param[1]: model (type: Model) -Function 408: GetModelBoundingBox() (1 input parameters) +Function 407: GetModelBoundingBox() (1 input parameters) Name: GetModelBoundingBox Return type: BoundingBox Description: Compute model bounding box limits (considers all meshes) Param[1]: model (type: Model) -Function 409: DrawModel() (4 input parameters) +Function 408: DrawModel() (4 input parameters) Name: DrawModel Return type: void Description: Draw a model (with texture if set) @@ -3546,7 +3540,7 @@ Function 409: DrawModel() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 410: DrawModelEx() (6 input parameters) +Function 409: DrawModelEx() (6 input parameters) Name: DrawModelEx Return type: void Description: Draw a model with extended parameters @@ -3556,7 +3550,7 @@ Function 410: DrawModelEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 411: DrawModelWires() (4 input parameters) +Function 410: DrawModelWires() (4 input parameters) Name: DrawModelWires Return type: void Description: Draw a model wires (with texture if set) @@ -3564,7 +3558,7 @@ Function 411: DrawModelWires() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 412: DrawModelWiresEx() (6 input parameters) +Function 411: DrawModelWiresEx() (6 input parameters) Name: DrawModelWiresEx Return type: void Description: Draw a model wires (with texture if set) with extended parameters @@ -3574,13 +3568,13 @@ Function 412: DrawModelWiresEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 413: DrawBoundingBox() (2 input parameters) +Function 412: DrawBoundingBox() (2 input parameters) Name: DrawBoundingBox Return type: void Description: Draw bounding box (wires) Param[1]: box (type: BoundingBox) Param[2]: color (type: Color) -Function 414: DrawBillboard() (5 input parameters) +Function 413: DrawBillboard() (5 input parameters) Name: DrawBillboard Return type: void Description: Draw a billboard texture @@ -3589,7 +3583,7 @@ Function 414: DrawBillboard() (5 input parameters) Param[3]: position (type: Vector3) Param[4]: size (type: float) Param[5]: tint (type: Color) -Function 415: DrawBillboardRec() (6 input parameters) +Function 414: DrawBillboardRec() (6 input parameters) Name: DrawBillboardRec Return type: void Description: Draw a billboard texture defined by source @@ -3599,7 +3593,7 @@ Function 415: DrawBillboardRec() (6 input parameters) Param[4]: position (type: Vector3) Param[5]: size (type: Vector2) Param[6]: tint (type: Color) -Function 416: DrawBillboardPro() (9 input parameters) +Function 415: DrawBillboardPro() (9 input parameters) Name: DrawBillboardPro Return type: void Description: Draw a billboard texture defined by source and rotation @@ -3612,13 +3606,13 @@ Function 416: DrawBillboardPro() (9 input parameters) Param[7]: origin (type: Vector2) Param[8]: rotation (type: float) Param[9]: tint (type: Color) -Function 417: UploadMesh() (2 input parameters) +Function 416: UploadMesh() (2 input parameters) Name: UploadMesh Return type: void Description: Upload mesh vertex data in GPU and provide VAO/VBO ids Param[1]: mesh (type: Mesh *) Param[2]: dynamic (type: bool) -Function 418: UpdateMeshBuffer() (5 input parameters) +Function 417: UpdateMeshBuffer() (5 input parameters) Name: UpdateMeshBuffer Return type: void Description: Update mesh vertex data in GPU for a specific buffer index @@ -3627,19 +3621,19 @@ Function 418: UpdateMeshBuffer() (5 input parameters) Param[3]: data (type: const void *) Param[4]: dataSize (type: int) Param[5]: offset (type: int) -Function 419: UnloadMesh() (1 input parameters) +Function 418: UnloadMesh() (1 input parameters) Name: UnloadMesh Return type: void Description: Unload mesh data from CPU and GPU Param[1]: mesh (type: Mesh) -Function 420: DrawMesh() (3 input parameters) +Function 419: DrawMesh() (3 input parameters) Name: DrawMesh Return type: void Description: Draw a 3d mesh with material and transform Param[1]: mesh (type: Mesh) Param[2]: material (type: Material) Param[3]: transform (type: Matrix) -Function 421: DrawMeshInstanced() (4 input parameters) +Function 420: DrawMeshInstanced() (4 input parameters) Name: DrawMeshInstanced Return type: void Description: Draw multiple mesh instances with material and different transforms @@ -3647,29 +3641,29 @@ Function 421: DrawMeshInstanced() (4 input parameters) Param[2]: material (type: Material) Param[3]: transforms (type: const Matrix *) Param[4]: instances (type: int) -Function 422: ExportMesh() (2 input parameters) +Function 421: ExportMesh() (2 input parameters) Name: ExportMesh Return type: bool Description: Export mesh data to file, returns true on success Param[1]: mesh (type: Mesh) Param[2]: fileName (type: const char *) -Function 423: GetMeshBoundingBox() (1 input parameters) +Function 422: GetMeshBoundingBox() (1 input parameters) Name: GetMeshBoundingBox Return type: BoundingBox Description: Compute mesh bounding box limits Param[1]: mesh (type: Mesh) -Function 424: GenMeshTangents() (1 input parameters) +Function 423: GenMeshTangents() (1 input parameters) Name: GenMeshTangents Return type: void Description: Compute mesh tangents Param[1]: mesh (type: Mesh *) -Function 425: GenMeshPoly() (2 input parameters) +Function 424: GenMeshPoly() (2 input parameters) Name: GenMeshPoly Return type: Mesh Description: Generate polygonal mesh Param[1]: sides (type: int) Param[2]: radius (type: float) -Function 426: GenMeshPlane() (4 input parameters) +Function 425: GenMeshPlane() (4 input parameters) Name: GenMeshPlane Return type: Mesh Description: Generate plane mesh (with subdivisions) @@ -3677,42 +3671,42 @@ Function 426: GenMeshPlane() (4 input parameters) Param[2]: length (type: float) Param[3]: resX (type: int) Param[4]: resZ (type: int) -Function 427: GenMeshCube() (3 input parameters) +Function 426: GenMeshCube() (3 input parameters) Name: GenMeshCube Return type: Mesh Description: Generate cuboid mesh Param[1]: width (type: float) Param[2]: height (type: float) Param[3]: length (type: float) -Function 428: GenMeshSphere() (3 input parameters) +Function 427: GenMeshSphere() (3 input parameters) Name: GenMeshSphere Return type: Mesh Description: Generate sphere mesh (standard sphere) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 429: GenMeshHemiSphere() (3 input parameters) +Function 428: GenMeshHemiSphere() (3 input parameters) Name: GenMeshHemiSphere Return type: Mesh Description: Generate half-sphere mesh (no bottom cap) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 430: GenMeshCylinder() (3 input parameters) +Function 429: GenMeshCylinder() (3 input parameters) Name: GenMeshCylinder Return type: Mesh Description: Generate cylinder mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 431: GenMeshCone() (3 input parameters) +Function 430: GenMeshCone() (3 input parameters) Name: GenMeshCone Return type: Mesh Description: Generate cone/pyramid mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 432: GenMeshTorus() (4 input parameters) +Function 431: GenMeshTorus() (4 input parameters) Name: GenMeshTorus Return type: Mesh Description: Generate torus mesh @@ -3720,7 +3714,7 @@ Function 432: GenMeshTorus() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 433: GenMeshKnot() (4 input parameters) +Function 432: GenMeshKnot() (4 input parameters) Name: GenMeshKnot Return type: Mesh Description: Generate trefoil knot mesh @@ -3728,84 +3722,84 @@ Function 433: GenMeshKnot() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 434: GenMeshHeightmap() (2 input parameters) +Function 433: GenMeshHeightmap() (2 input parameters) Name: GenMeshHeightmap Return type: Mesh Description: Generate heightmap mesh from image data Param[1]: heightmap (type: Image) Param[2]: size (type: Vector3) -Function 435: GenMeshCubicmap() (2 input parameters) +Function 434: GenMeshCubicmap() (2 input parameters) Name: GenMeshCubicmap Return type: Mesh Description: Generate cubes-based map mesh from image data Param[1]: cubicmap (type: Image) Param[2]: cubeSize (type: Vector3) -Function 436: LoadMaterials() (2 input parameters) +Function 435: LoadMaterials() (2 input parameters) Name: LoadMaterials Return type: Material * Description: Load materials from model file Param[1]: fileName (type: const char *) Param[2]: materialCount (type: int *) -Function 437: LoadMaterialDefault() (0 input parameters) +Function 436: LoadMaterialDefault() (0 input parameters) Name: LoadMaterialDefault Return type: Material Description: Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) No input parameters -Function 438: IsMaterialReady() (1 input parameters) +Function 437: IsMaterialReady() (1 input parameters) Name: IsMaterialReady Return type: bool Description: Check if a material is ready Param[1]: material (type: Material) -Function 439: UnloadMaterial() (1 input parameters) +Function 438: UnloadMaterial() (1 input parameters) Name: UnloadMaterial Return type: void Description: Unload material from GPU memory (VRAM) Param[1]: material (type: Material) -Function 440: SetMaterialTexture() (3 input parameters) +Function 439: SetMaterialTexture() (3 input parameters) Name: SetMaterialTexture Return type: void Description: Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) Param[1]: material (type: Material *) Param[2]: mapType (type: int) Param[3]: texture (type: Texture2D) -Function 441: SetModelMeshMaterial() (3 input parameters) +Function 440: SetModelMeshMaterial() (3 input parameters) Name: SetModelMeshMaterial Return type: void Description: Set material for a mesh Param[1]: model (type: Model *) Param[2]: meshId (type: int) Param[3]: materialId (type: int) -Function 442: LoadModelAnimations() (2 input parameters) +Function 441: LoadModelAnimations() (2 input parameters) Name: LoadModelAnimations Return type: ModelAnimation * Description: Load model animations from file Param[1]: fileName (type: const char *) Param[2]: animCount (type: unsigned int *) -Function 443: UpdateModelAnimation() (3 input parameters) +Function 442: UpdateModelAnimation() (3 input parameters) Name: UpdateModelAnimation Return type: void Description: Update model animation pose Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) Param[3]: frame (type: int) -Function 444: UnloadModelAnimation() (1 input parameters) +Function 443: UnloadModelAnimation() (1 input parameters) Name: UnloadModelAnimation Return type: void Description: Unload animation data Param[1]: anim (type: ModelAnimation) -Function 445: UnloadModelAnimations() (2 input parameters) +Function 444: UnloadModelAnimations() (2 input parameters) Name: UnloadModelAnimations Return type: void Description: Unload animation array data Param[1]: animations (type: ModelAnimation *) Param[2]: count (type: unsigned int) -Function 446: IsModelAnimationValid() (2 input parameters) +Function 445: IsModelAnimationValid() (2 input parameters) Name: IsModelAnimationValid Return type: bool Description: Check model animation skeleton match Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) -Function 447: CheckCollisionSpheres() (4 input parameters) +Function 446: CheckCollisionSpheres() (4 input parameters) Name: CheckCollisionSpheres Return type: bool Description: Check collision between two spheres @@ -3813,40 +3807,40 @@ Function 447: CheckCollisionSpheres() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector3) Param[4]: radius2 (type: float) -Function 448: CheckCollisionBoxes() (2 input parameters) +Function 447: CheckCollisionBoxes() (2 input parameters) Name: CheckCollisionBoxes Return type: bool Description: Check collision between two bounding boxes Param[1]: box1 (type: BoundingBox) Param[2]: box2 (type: BoundingBox) -Function 449: CheckCollisionBoxSphere() (3 input parameters) +Function 448: CheckCollisionBoxSphere() (3 input parameters) Name: CheckCollisionBoxSphere Return type: bool Description: Check collision between box and sphere Param[1]: box (type: BoundingBox) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 450: GetRayCollisionSphere() (3 input parameters) +Function 449: GetRayCollisionSphere() (3 input parameters) Name: GetRayCollisionSphere Return type: RayCollision Description: Get collision info between ray and sphere Param[1]: ray (type: Ray) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 451: GetRayCollisionBox() (2 input parameters) +Function 450: GetRayCollisionBox() (2 input parameters) Name: GetRayCollisionBox Return type: RayCollision Description: Get collision info between ray and box Param[1]: ray (type: Ray) Param[2]: box (type: BoundingBox) -Function 452: GetRayCollisionMesh() (3 input parameters) +Function 451: GetRayCollisionMesh() (3 input parameters) Name: GetRayCollisionMesh Return type: RayCollision Description: Get collision info between ray and mesh Param[1]: ray (type: Ray) Param[2]: mesh (type: Mesh) Param[3]: transform (type: Matrix) -Function 453: GetRayCollisionTriangle() (4 input parameters) +Function 452: GetRayCollisionTriangle() (4 input parameters) Name: GetRayCollisionTriangle Return type: RayCollision Description: Get collision info between ray and triangle @@ -3854,7 +3848,7 @@ Function 453: GetRayCollisionTriangle() (4 input parameters) Param[2]: p1 (type: Vector3) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) -Function 454: GetRayCollisionQuad() (5 input parameters) +Function 453: GetRayCollisionQuad() (5 input parameters) Name: GetRayCollisionQuad Return type: RayCollision Description: Get collision info between ray and quad @@ -3863,143 +3857,143 @@ Function 454: GetRayCollisionQuad() (5 input parameters) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) Param[5]: p4 (type: Vector3) -Function 455: InitAudioDevice() (0 input parameters) +Function 454: InitAudioDevice() (0 input parameters) Name: InitAudioDevice Return type: void Description: Initialize audio device and context No input parameters -Function 456: CloseAudioDevice() (0 input parameters) +Function 455: CloseAudioDevice() (0 input parameters) Name: CloseAudioDevice Return type: void Description: Close the audio device and context No input parameters -Function 457: IsAudioDeviceReady() (0 input parameters) +Function 456: IsAudioDeviceReady() (0 input parameters) Name: IsAudioDeviceReady Return type: bool Description: Check if audio device has been initialized successfully No input parameters -Function 458: SetMasterVolume() (1 input parameters) +Function 457: SetMasterVolume() (1 input parameters) Name: SetMasterVolume Return type: void Description: Set master volume (listener) Param[1]: volume (type: float) -Function 459: LoadWave() (1 input parameters) +Function 458: LoadWave() (1 input parameters) Name: LoadWave Return type: Wave Description: Load wave data from file Param[1]: fileName (type: const char *) -Function 460: LoadWaveFromMemory() (3 input parameters) +Function 459: LoadWaveFromMemory() (3 input parameters) Name: LoadWaveFromMemory Return type: Wave Description: Load wave from memory buffer, fileType refers to extension: i.e. '.wav' Param[1]: fileType (type: const char *) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 461: IsWaveReady() (1 input parameters) +Function 460: IsWaveReady() (1 input parameters) Name: IsWaveReady Return type: bool Description: Checks if wave data is ready Param[1]: wave (type: Wave) -Function 462: LoadSound() (1 input parameters) +Function 461: LoadSound() (1 input parameters) Name: LoadSound Return type: Sound Description: Load sound from file Param[1]: fileName (type: const char *) -Function 463: LoadSoundFromWave() (1 input parameters) +Function 462: LoadSoundFromWave() (1 input parameters) Name: LoadSoundFromWave Return type: Sound Description: Load sound from wave data Param[1]: wave (type: Wave) -Function 464: IsSoundReady() (1 input parameters) +Function 463: IsSoundReady() (1 input parameters) Name: IsSoundReady Return type: bool Description: Checks if a sound is ready Param[1]: sound (type: Sound) -Function 465: UpdateSound() (3 input parameters) +Function 464: UpdateSound() (3 input parameters) Name: UpdateSound Return type: void Description: Update sound buffer with new data Param[1]: sound (type: Sound) Param[2]: data (type: const void *) Param[3]: sampleCount (type: int) -Function 466: UnloadWave() (1 input parameters) +Function 465: UnloadWave() (1 input parameters) Name: UnloadWave Return type: void Description: Unload wave data Param[1]: wave (type: Wave) -Function 467: UnloadSound() (1 input parameters) +Function 466: UnloadSound() (1 input parameters) Name: UnloadSound Return type: void Description: Unload sound Param[1]: sound (type: Sound) -Function 468: ExportWave() (2 input parameters) +Function 467: ExportWave() (2 input parameters) Name: ExportWave Return type: bool Description: Export wave data to file, returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 469: ExportWaveAsCode() (2 input parameters) +Function 468: ExportWaveAsCode() (2 input parameters) Name: ExportWaveAsCode Return type: bool Description: Export wave sample data to code (.h), returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 470: PlaySound() (1 input parameters) +Function 469: PlaySound() (1 input parameters) Name: PlaySound Return type: void Description: Play a sound Param[1]: sound (type: Sound) -Function 471: StopSound() (1 input parameters) +Function 470: StopSound() (1 input parameters) Name: StopSound Return type: void Description: Stop playing a sound Param[1]: sound (type: Sound) -Function 472: PauseSound() (1 input parameters) +Function 471: PauseSound() (1 input parameters) Name: PauseSound Return type: void Description: Pause a sound Param[1]: sound (type: Sound) -Function 473: ResumeSound() (1 input parameters) +Function 472: ResumeSound() (1 input parameters) Name: ResumeSound Return type: void Description: Resume a paused sound Param[1]: sound (type: Sound) -Function 474: IsSoundPlaying() (1 input parameters) +Function 473: IsSoundPlaying() (1 input parameters) Name: IsSoundPlaying Return type: bool Description: Check if a sound is currently playing Param[1]: sound (type: Sound) -Function 475: SetSoundVolume() (2 input parameters) +Function 474: SetSoundVolume() (2 input parameters) Name: SetSoundVolume Return type: void Description: Set volume for a sound (1.0 is max level) Param[1]: sound (type: Sound) Param[2]: volume (type: float) -Function 476: SetSoundPitch() (2 input parameters) +Function 475: SetSoundPitch() (2 input parameters) Name: SetSoundPitch Return type: void Description: Set pitch for a sound (1.0 is base level) Param[1]: sound (type: Sound) Param[2]: pitch (type: float) -Function 477: SetSoundPan() (2 input parameters) +Function 476: SetSoundPan() (2 input parameters) Name: SetSoundPan Return type: void Description: Set pan for a sound (0.5 is center) Param[1]: sound (type: Sound) Param[2]: pan (type: float) -Function 478: WaveCopy() (1 input parameters) +Function 477: WaveCopy() (1 input parameters) Name: WaveCopy Return type: Wave Description: Copy a wave to a new wave Param[1]: wave (type: Wave) -Function 479: WaveCrop() (3 input parameters) +Function 478: WaveCrop() (3 input parameters) Name: WaveCrop Return type: void Description: Crop a wave to defined samples range Param[1]: wave (type: Wave *) Param[2]: initSample (type: int) Param[3]: finalSample (type: int) -Function 480: WaveFormat() (4 input parameters) +Function 479: WaveFormat() (4 input parameters) Name: WaveFormat Return type: void Description: Convert wave data to desired format @@ -4007,203 +4001,203 @@ Function 480: WaveFormat() (4 input parameters) Param[2]: sampleRate (type: int) Param[3]: sampleSize (type: int) Param[4]: channels (type: int) -Function 481: LoadWaveSamples() (1 input parameters) +Function 480: LoadWaveSamples() (1 input parameters) Name: LoadWaveSamples Return type: float * Description: Load samples data from wave as a 32bit float data array Param[1]: wave (type: Wave) -Function 482: UnloadWaveSamples() (1 input parameters) +Function 481: UnloadWaveSamples() (1 input parameters) Name: UnloadWaveSamples Return type: void Description: Unload samples data loaded with LoadWaveSamples() Param[1]: samples (type: float *) -Function 483: LoadMusicStream() (1 input parameters) +Function 482: LoadMusicStream() (1 input parameters) Name: LoadMusicStream Return type: Music Description: Load music stream from file Param[1]: fileName (type: const char *) -Function 484: LoadMusicStreamFromMemory() (3 input parameters) +Function 483: LoadMusicStreamFromMemory() (3 input parameters) Name: LoadMusicStreamFromMemory Return type: Music Description: Load music stream from data Param[1]: fileType (type: const char *) Param[2]: data (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 485: IsMusicReady() (1 input parameters) +Function 484: IsMusicReady() (1 input parameters) Name: IsMusicReady Return type: bool Description: Checks if a music stream is ready Param[1]: music (type: Music) -Function 486: UnloadMusicStream() (1 input parameters) +Function 485: UnloadMusicStream() (1 input parameters) Name: UnloadMusicStream Return type: void Description: Unload music stream Param[1]: music (type: Music) -Function 487: PlayMusicStream() (1 input parameters) +Function 486: PlayMusicStream() (1 input parameters) Name: PlayMusicStream Return type: void Description: Start music playing Param[1]: music (type: Music) -Function 488: IsMusicStreamPlaying() (1 input parameters) +Function 487: IsMusicStreamPlaying() (1 input parameters) Name: IsMusicStreamPlaying Return type: bool Description: Check if music is playing Param[1]: music (type: Music) -Function 489: UpdateMusicStream() (1 input parameters) +Function 488: UpdateMusicStream() (1 input parameters) Name: UpdateMusicStream Return type: void Description: Updates buffers for music streaming Param[1]: music (type: Music) -Function 490: StopMusicStream() (1 input parameters) +Function 489: StopMusicStream() (1 input parameters) Name: StopMusicStream Return type: void Description: Stop music playing Param[1]: music (type: Music) -Function 491: PauseMusicStream() (1 input parameters) +Function 490: PauseMusicStream() (1 input parameters) Name: PauseMusicStream Return type: void Description: Pause music playing Param[1]: music (type: Music) -Function 492: ResumeMusicStream() (1 input parameters) +Function 491: ResumeMusicStream() (1 input parameters) Name: ResumeMusicStream Return type: void Description: Resume playing paused music Param[1]: music (type: Music) -Function 493: SeekMusicStream() (2 input parameters) +Function 492: SeekMusicStream() (2 input parameters) Name: SeekMusicStream Return type: void Description: Seek music to a position (in seconds) Param[1]: music (type: Music) Param[2]: position (type: float) -Function 494: SetMusicVolume() (2 input parameters) +Function 493: SetMusicVolume() (2 input parameters) Name: SetMusicVolume Return type: void Description: Set volume for music (1.0 is max level) Param[1]: music (type: Music) Param[2]: volume (type: float) -Function 495: SetMusicPitch() (2 input parameters) +Function 494: SetMusicPitch() (2 input parameters) Name: SetMusicPitch Return type: void Description: Set pitch for a music (1.0 is base level) Param[1]: music (type: Music) Param[2]: pitch (type: float) -Function 496: SetMusicPan() (2 input parameters) +Function 495: SetMusicPan() (2 input parameters) Name: SetMusicPan Return type: void Description: Set pan for a music (0.5 is center) Param[1]: music (type: Music) Param[2]: pan (type: float) -Function 497: GetMusicTimeLength() (1 input parameters) +Function 496: GetMusicTimeLength() (1 input parameters) Name: GetMusicTimeLength Return type: float Description: Get music time length (in seconds) Param[1]: music (type: Music) -Function 498: GetMusicTimePlayed() (1 input parameters) +Function 497: GetMusicTimePlayed() (1 input parameters) Name: GetMusicTimePlayed Return type: float Description: Get current music time played (in seconds) Param[1]: music (type: Music) -Function 499: LoadAudioStream() (3 input parameters) +Function 498: LoadAudioStream() (3 input parameters) Name: LoadAudioStream Return type: AudioStream Description: Load audio stream (to stream raw audio pcm data) Param[1]: sampleRate (type: unsigned int) Param[2]: sampleSize (type: unsigned int) Param[3]: channels (type: unsigned int) -Function 500: IsAudioStreamReady() (1 input parameters) +Function 499: IsAudioStreamReady() (1 input parameters) Name: IsAudioStreamReady Return type: bool Description: Checks if an audio stream is ready Param[1]: stream (type: AudioStream) -Function 501: UnloadAudioStream() (1 input parameters) +Function 500: UnloadAudioStream() (1 input parameters) Name: UnloadAudioStream Return type: void Description: Unload audio stream and free memory Param[1]: stream (type: AudioStream) -Function 502: UpdateAudioStream() (3 input parameters) +Function 501: UpdateAudioStream() (3 input parameters) Name: UpdateAudioStream Return type: void Description: Update audio stream buffers with data Param[1]: stream (type: AudioStream) Param[2]: data (type: const void *) Param[3]: frameCount (type: int) -Function 503: IsAudioStreamProcessed() (1 input parameters) +Function 502: IsAudioStreamProcessed() (1 input parameters) Name: IsAudioStreamProcessed Return type: bool Description: Check if any audio stream buffers requires refill Param[1]: stream (type: AudioStream) -Function 504: PlayAudioStream() (1 input parameters) +Function 503: PlayAudioStream() (1 input parameters) Name: PlayAudioStream Return type: void Description: Play audio stream Param[1]: stream (type: AudioStream) -Function 505: PauseAudioStream() (1 input parameters) +Function 504: PauseAudioStream() (1 input parameters) Name: PauseAudioStream Return type: void Description: Pause audio stream Param[1]: stream (type: AudioStream) -Function 506: ResumeAudioStream() (1 input parameters) +Function 505: ResumeAudioStream() (1 input parameters) Name: ResumeAudioStream Return type: void Description: Resume audio stream Param[1]: stream (type: AudioStream) -Function 507: IsAudioStreamPlaying() (1 input parameters) +Function 506: IsAudioStreamPlaying() (1 input parameters) Name: IsAudioStreamPlaying Return type: bool Description: Check if audio stream is playing Param[1]: stream (type: AudioStream) -Function 508: StopAudioStream() (1 input parameters) +Function 507: StopAudioStream() (1 input parameters) Name: StopAudioStream Return type: void Description: Stop audio stream Param[1]: stream (type: AudioStream) -Function 509: SetAudioStreamVolume() (2 input parameters) +Function 508: SetAudioStreamVolume() (2 input parameters) Name: SetAudioStreamVolume Return type: void Description: Set volume for audio stream (1.0 is max level) Param[1]: stream (type: AudioStream) Param[2]: volume (type: float) -Function 510: SetAudioStreamPitch() (2 input parameters) +Function 509: SetAudioStreamPitch() (2 input parameters) Name: SetAudioStreamPitch Return type: void Description: Set pitch for audio stream (1.0 is base level) Param[1]: stream (type: AudioStream) Param[2]: pitch (type: float) -Function 511: SetAudioStreamPan() (2 input parameters) +Function 510: SetAudioStreamPan() (2 input parameters) Name: SetAudioStreamPan Return type: void Description: Set pan for audio stream (0.5 is centered) Param[1]: stream (type: AudioStream) Param[2]: pan (type: float) -Function 512: SetAudioStreamBufferSizeDefault() (1 input parameters) +Function 511: SetAudioStreamBufferSizeDefault() (1 input parameters) Name: SetAudioStreamBufferSizeDefault Return type: void Description: Default size for new audio streams Param[1]: size (type: int) -Function 513: SetAudioStreamCallback() (2 input parameters) +Function 512: SetAudioStreamCallback() (2 input parameters) Name: SetAudioStreamCallback Return type: void Description: Audio thread callback to request new data Param[1]: stream (type: AudioStream) Param[2]: callback (type: AudioCallback) -Function 514: AttachAudioStreamProcessor() (2 input parameters) +Function 513: AttachAudioStreamProcessor() (2 input parameters) Name: AttachAudioStreamProcessor Return type: void Description: Attach audio stream processor to stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 515: DetachAudioStreamProcessor() (2 input parameters) +Function 514: DetachAudioStreamProcessor() (2 input parameters) Name: DetachAudioStreamProcessor Return type: void Description: Detach audio stream processor from stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 516: AttachAudioMixedProcessor() (1 input parameters) +Function 515: AttachAudioMixedProcessor() (1 input parameters) Name: AttachAudioMixedProcessor Return type: void Description: Attach audio stream processor to the entire audio pipeline Param[1]: processor (type: AudioCallback) -Function 517: DetachAudioMixedProcessor() (1 input parameters) +Function 516: DetachAudioMixedProcessor() (1 input parameters) Name: DetachAudioMixedProcessor Return type: void Description: Detach audio stream processor from the entire audio pipeline diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index 45305951f..1c77f0266 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -3,9 +3,9 @@ - + - + @@ -210,11 +210,12 @@ - + + @@ -655,7 +656,7 @@ - + @@ -1548,17 +1549,12 @@ - + - - - - - - - - + + + diff --git a/projects/Geany/raylib.c.tags b/projects/Geany/raylib.c.tags index 78e6e7241..d41809983 100644 --- a/projects/Geany/raylib.c.tags +++ b/projects/Geany/raylib.c.tags @@ -211,8 +211,7 @@ ImageColorContrast|void|(Image *image, float contrast);| ImageColorBrightness|void|(Image *image, int brightness);| ImageColorReplace|void|(Image *image, Color color, Color replace);| GenImageColor|Image|(int width, int height, Color color);| -GenImageGradientV|Image|(int width, int height, Color top, Color bottom);| -GenImageGradientH|Image|(int width, int height, Color left, Color right);| +GenImageGradientLinear|Image|(int width, int height, int direction, Color start, Color end);| GenImageGradientRadial|Image|(int width, int height, float density, Color inner, Color outer);| GenImageChecked|Image|(int width, int height, int checksX, int checksY, Color col1, Color col2);| GenImageWhiteNoise|Image|(int width, int height, float factor);| diff --git a/projects/Notepad++/raylib_npp_parser/raylib_npp.xml b/projects/Notepad++/raylib_npp_parser/raylib_npp.xml index e764a1b63..890501f2c 100644 --- a/projects/Notepad++/raylib_npp_parser/raylib_npp.xml +++ b/projects/Notepad++/raylib_npp_parser/raylib_npp.xml @@ -1378,20 +1378,13 @@ - - + + - - - - - - - - - - + + + diff --git a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h index 96732e319..3f8450130 100644 --- a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h +++ b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h @@ -317,8 +317,7 @@ RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Image generation functions RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color -RLAPI Image GenImageGradientV(int width, int height, Color top, Color bottom); // Generate image: vertical gradient -RLAPI Image GenImageGradientH(int width, int height, Color left, Color right); // Generate image: horizontal gradient +RLAPI Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end); // Generate image: linear gradient RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise diff --git a/src/raylib.h b/src/raylib.h index 2a5a2f63f..edc81c341 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1239,8 +1239,7 @@ RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Image generation functions RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color -RLAPI Image GenImageGradientV(int width, int height, Color top, Color bottom); // Generate image: vertical gradient -RLAPI Image GenImageGradientH(int width, int height, Color left, Color right); // Generate image: horizontal gradient +RLAPI Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end); // Generate image: linear gradient RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise diff --git a/src/rtextures.c b/src/rtextures.c index 875942586..d1cebe0d4 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -684,48 +684,35 @@ Image GenImageColor(int width, int height, Color color) } #if defined(SUPPORT_IMAGE_GENERATION) -// Generate image: vertical gradient -Image GenImageGradientV(int width, int height, Color top, Color bottom) +// Generate image: linear gradient +// The direction value specifies the direction of the gradient (in degrees) +// with 0 being vertical (from top to bottom), 90 being horizontal (from left to right). +// The gradient effectively rotates counter-clockwise by the specified amount. +Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end) { Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color)); - for (int j = 0; j < height; j++) + float radianDirection = (float)(90 - direction) / 180.f * 3.14159f; + float cosDir = cos(radianDirection); + float sinDir = sin(radianDirection); + + int i, j; + for (i = 0; i < width; i++) { - float factor = (float)j/(float)height; - for (int i = 0; i < width; i++) + for (j = 0; j < height; j++) { - pixels[j*width + i].r = (int)((float)bottom.r*factor + (float)top.r*(1.f - factor)); - pixels[j*width + i].g = (int)((float)bottom.g*factor + (float)top.g*(1.f - factor)); - pixels[j*width + i].b = (int)((float)bottom.b*factor + (float)top.b*(1.f - factor)); - pixels[j*width + i].a = (int)((float)bottom.a*factor + (float)top.a*(1.f - factor)); - } - } + // Calculate the relative position of the pixel along the gradient direction + float pos = (i * cosDir + j * sinDir) / (width * cosDir + height * sinDir); - Image image = { - .data = pixels, - .width = width, - .height = height, - .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, - .mipmaps = 1 - }; + float factor = pos; + factor = (factor > 1.f) ? 1.f : factor; // Clamp to [0,1] + factor = (factor < 0.f) ? 0.f : factor; // Clamp to [0,1] - return image; -} - -// Generate image: horizontal gradient -Image GenImageGradientH(int width, int height, Color left, Color right) -{ - Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color)); - - for (int i = 0; i < width; i++) - { - float factor = (float)i/(float)width; - for (int j = 0; j < height; j++) - { - pixels[j*width + i].r = (int)((float)right.r*factor + (float)left.r*(1.f - factor)); - pixels[j*width + i].g = (int)((float)right.g*factor + (float)left.g*(1.f - factor)); - pixels[j*width + i].b = (int)((float)right.b*factor + (float)left.b*(1.f - factor)); - pixels[j*width + i].a = (int)((float)right.a*factor + (float)left.a*(1.f - factor)); + // Generate the color for this pixel + pixels[j * width + i].r = (int)((float)end.r*factor + (float)start.r*(1.f - factor)); + pixels[j * width + i].g = (int)((float)end.g*factor + (float)start.g*(1.f - factor)); + pixels[j * width + i].b = (int)((float)end.b*factor + (float)start.b*(1.f - factor)); + pixels[j * width + i].a = (int)((float)end.a*factor + (float)start.a*(1.f - factor)); } } From 84ae26cdc0ab22e7721552647cd6c30d8a478bae Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 21 May 2023 11:35:25 +0200 Subject: [PATCH 0090/1350] Update raylib.h --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index edc81c341..02d8e4445 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1239,7 +1239,7 @@ RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Image generation functions RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color -RLAPI Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end); // Generate image: linear gradient +RLAPI Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end); // Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise From a4a6d4da8a26d131bc3f3e643f6fe51de281d15f Mon Sep 17 00:00:00 2001 From: Dane Madsen Date: Mon, 22 May 2023 23:20:28 +1000 Subject: [PATCH 0091/1350] Add GenImageGradientSquare (#3077) * Add GenImageGradientSquare to allow square gradients * Fix GenImageGradientSquare and add to textures_image_generation example * Remove params from GenImageGradientSquare --- examples/textures/textures_image_generation.c | 23 +- parser/output/raylib_api.json | 29 +- parser/output/raylib_api.lua | 14 +- parser/output/raylib_api.txt | 551 +++++++++--------- parser/output/raylib_api.xml | 11 +- projects/Geany/raylib.c.tags | 1 + .../raylib_npp_parser/raylib_npp.xml | 9 + .../raylib_npp_parser/raylib_to_parse.h | 1 + src/raylib.h | 1 + src/rtextures.c | 49 ++ 10 files changed, 407 insertions(+), 282 deletions(-) diff --git a/examples/textures/textures_image_generation.c b/examples/textures/textures_image_generation.c index 8049a578e..359dc236a 100644 --- a/examples/textures/textures_image_generation.c +++ b/examples/textures/textures_image_generation.c @@ -13,7 +13,7 @@ #include "raylib.h" -#define NUM_TEXTURES 7 // Currently we have 7 generation algorithms +#define NUM_TEXTURES 9 // Currently we have 8 generation algorithms but some are have multiple purposes (Linear and Square Gradients) //------------------------------------------------------------------------------------ // Program main entry point @@ -31,8 +31,10 @@ int main(void) Image horizontalGradient = GenImageGradientLinear(screenWidth, screenHeight, 90, RED, BLUE); Image diagonalGradient = GenImageGradientLinear(screenWidth, screenHeight, 45, RED, BLUE); Image radialGradient = GenImageGradientRadial(screenWidth, screenHeight, 0.0f, WHITE, BLACK); + Image squareGradient = GenImageGradientSquare(screenWidth, screenHeight, 0.0f, WHITE, BLACK); Image checked = GenImageChecked(screenWidth, screenHeight, 32, 32, RED, BLUE); Image whiteNoise = GenImageWhiteNoise(screenWidth, screenHeight, 0.5f); + Image perlinNoise = GenImagePerlinNoise(screenWidth, screenHeight, 50, 50, 4.0f); Image cellular = GenImageCellular(screenWidth, screenHeight, 32); Texture2D textures[NUM_TEXTURES] = { 0 }; @@ -41,16 +43,21 @@ int main(void) textures[1] = LoadTextureFromImage(horizontalGradient); textures[2] = LoadTextureFromImage(diagonalGradient); textures[3] = LoadTextureFromImage(radialGradient); - textures[4] = LoadTextureFromImage(checked); - textures[5] = LoadTextureFromImage(whiteNoise); - textures[6] = LoadTextureFromImage(cellular); + textures[4] = LoadTextureFromImage(squareGradient); + textures[5] = LoadTextureFromImage(checked); + textures[6] = LoadTextureFromImage(whiteNoise); + textures[7] = LoadTextureFromImage(perlinNoise); + textures[8] = LoadTextureFromImage(cellular); // Unload image data (CPU RAM) UnloadImage(verticalGradient); UnloadImage(horizontalGradient); + UnloadImage(diagonalGradient); UnloadImage(radialGradient); + UnloadImage(squareGradient); UnloadImage(checked); UnloadImage(whiteNoise); + UnloadImage(perlinNoise); UnloadImage(cellular); int currentTexture = 0; @@ -87,9 +94,11 @@ int main(void) case 1: DrawText("HORIZONTAL GRADIENT", 540, 10, 20, RAYWHITE); break; case 2: DrawText("DIAGONAL GRADIENT", 540, 10, 20, RAYWHITE); break; case 3: DrawText("RADIAL GRADIENT", 580, 10, 20, LIGHTGRAY); break; - case 4: DrawText("CHECKED", 680, 10, 20, RAYWHITE); break; - case 5: DrawText("WHITE NOISE", 640, 10, 20, RED); break; - case 6: DrawText("CELLULAR", 670, 10, 20, RAYWHITE); break; + case 4: DrawText("SQUARE GRADIENT", 580, 10, 20, LIGHTGRAY); break; + case 5: DrawText("CHECKED", 680, 10, 20, RAYWHITE); break; + case 6: DrawText("WHITE NOISE", 640, 10, 20, RED); break; + case 7: DrawText("PERLIN NOISE", 640, 10, 20, RED); break; + case 8: DrawText("CELLULAR", 670, 10, 20, RAYWHITE); break; default: break; } diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index 5af2b9aef..4438ab0da 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -6242,7 +6242,7 @@ }, { "name": "GenImageGradientLinear", - "description": "Generate image: linear gradient", + "description": "Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient", "returnType": "Image", "params": [ { @@ -6294,6 +6294,33 @@ } ] }, + { + "name": "GenImageGradientSquare", + "description": "Generate image: square gradient", + "returnType": "Image", + "params": [ + { + "type": "int", + "name": "width" + }, + { + "type": "int", + "name": "height" + }, + { + "type": "float", + "name": "density" + }, + { + "type": "Color", + "name": "inner" + }, + { + "type": "Color", + "name": "outer" + } + ] + }, { "name": "GenImageChecked", "description": "Generate image: checked", diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index e7101f51b..79b3a9283 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -4997,7 +4997,7 @@ return { }, { name = "GenImageGradientLinear", - description = "Generate image: linear gradient", + description = "Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient", returnType = "Image", params = { {type = "int", name = "width"}, @@ -5019,6 +5019,18 @@ return { {type = "Color", name = "outer"} } }, + { + name = "GenImageGradientSquare", + description = "Generate image: square gradient", + returnType = "Image", + params = { + {type = "int", name = "width"}, + {type = "int", name = "height"}, + {type = "float", name = "density"}, + {type = "Color", name = "inner"}, + {type = "Color", name = "outer"} + } + }, { name = "GenImageChecked", description = "Generate image: checked", diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index 2df71232e..c53cb77ec 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -964,7 +964,7 @@ Callback 006: AudioCallback() (2 input parameters) Param[1]: bufferData (type: void *) Param[2]: frames (type: unsigned int) -Functions found: 516 +Functions found: 517 Function 001: InitWindow() (3 input parameters) Name: InitWindow @@ -2419,7 +2419,7 @@ Function 245: GenImageColor() (3 input parameters) Function 246: GenImageGradientLinear() (5 input parameters) Name: GenImageGradientLinear Return type: Image - Description: Generate image: linear gradient + Description: Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: direction (type: int) @@ -2434,7 +2434,16 @@ Function 247: GenImageGradientRadial() (5 input parameters) Param[3]: density (type: float) Param[4]: inner (type: Color) Param[5]: outer (type: Color) -Function 248: GenImageChecked() (6 input parameters) +Function 248: GenImageGradientSquare() (5 input parameters) + Name: GenImageGradientSquare + Return type: Image + Description: Generate image: square gradient + Param[1]: width (type: int) + Param[2]: height (type: int) + Param[3]: density (type: float) + Param[4]: inner (type: Color) + Param[5]: outer (type: Color) +Function 249: GenImageChecked() (6 input parameters) Name: GenImageChecked Return type: Image Description: Generate image: checked @@ -2444,14 +2453,14 @@ Function 248: GenImageChecked() (6 input parameters) Param[4]: checksY (type: int) Param[5]: col1 (type: Color) Param[6]: col2 (type: Color) -Function 249: GenImageWhiteNoise() (3 input parameters) +Function 250: GenImageWhiteNoise() (3 input parameters) Name: GenImageWhiteNoise Return type: Image Description: Generate image: white noise Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: factor (type: float) -Function 250: GenImagePerlinNoise() (5 input parameters) +Function 251: GenImagePerlinNoise() (5 input parameters) Name: GenImagePerlinNoise Return type: Image Description: Generate image: perlin noise @@ -2460,39 +2469,39 @@ Function 250: GenImagePerlinNoise() (5 input parameters) Param[3]: offsetX (type: int) Param[4]: offsetY (type: int) Param[5]: scale (type: float) -Function 251: GenImageCellular() (3 input parameters) +Function 252: GenImageCellular() (3 input parameters) Name: GenImageCellular Return type: Image Description: Generate image: cellular algorithm, bigger tileSize means bigger cells Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: tileSize (type: int) -Function 252: GenImageText() (3 input parameters) +Function 253: GenImageText() (3 input parameters) Name: GenImageText Return type: Image Description: Generate image: grayscale image from text data Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: text (type: const char *) -Function 253: ImageCopy() (1 input parameters) +Function 254: ImageCopy() (1 input parameters) Name: ImageCopy Return type: Image Description: Create an image duplicate (useful for transformations) Param[1]: image (type: Image) -Function 254: ImageFromImage() (2 input parameters) +Function 255: ImageFromImage() (2 input parameters) Name: ImageFromImage Return type: Image Description: Create an image from another image piece Param[1]: image (type: Image) Param[2]: rec (type: Rectangle) -Function 255: ImageText() (3 input parameters) +Function 256: ImageText() (3 input parameters) Name: ImageText Return type: Image Description: Create an image from text (default font) Param[1]: text (type: const char *) Param[2]: fontSize (type: int) Param[3]: color (type: Color) -Function 256: ImageTextEx() (5 input parameters) +Function 257: ImageTextEx() (5 input parameters) Name: ImageTextEx Return type: Image Description: Create an image from text (custom sprite font) @@ -2501,69 +2510,69 @@ Function 256: ImageTextEx() (5 input parameters) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) Param[5]: tint (type: Color) -Function 257: ImageFormat() (2 input parameters) +Function 258: ImageFormat() (2 input parameters) Name: ImageFormat Return type: void Description: Convert image data to desired format Param[1]: image (type: Image *) Param[2]: newFormat (type: int) -Function 258: ImageToPOT() (2 input parameters) +Function 259: ImageToPOT() (2 input parameters) Name: ImageToPOT Return type: void Description: Convert image to POT (power-of-two) Param[1]: image (type: Image *) Param[2]: fill (type: Color) -Function 259: ImageCrop() (2 input parameters) +Function 260: ImageCrop() (2 input parameters) Name: ImageCrop Return type: void Description: Crop an image to a defined rectangle Param[1]: image (type: Image *) Param[2]: crop (type: Rectangle) -Function 260: ImageAlphaCrop() (2 input parameters) +Function 261: ImageAlphaCrop() (2 input parameters) Name: ImageAlphaCrop Return type: void Description: Crop image depending on alpha value Param[1]: image (type: Image *) Param[2]: threshold (type: float) -Function 261: ImageAlphaClear() (3 input parameters) +Function 262: ImageAlphaClear() (3 input parameters) Name: ImageAlphaClear Return type: void Description: Clear alpha channel to desired color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: threshold (type: float) -Function 262: ImageAlphaMask() (2 input parameters) +Function 263: ImageAlphaMask() (2 input parameters) Name: ImageAlphaMask Return type: void Description: Apply alpha mask to image Param[1]: image (type: Image *) Param[2]: alphaMask (type: Image) -Function 263: ImageAlphaPremultiply() (1 input parameters) +Function 264: ImageAlphaPremultiply() (1 input parameters) Name: ImageAlphaPremultiply Return type: void Description: Premultiply alpha channel Param[1]: image (type: Image *) -Function 264: ImageBlurGaussian() (2 input parameters) +Function 265: ImageBlurGaussian() (2 input parameters) Name: ImageBlurGaussian Return type: void Description: Apply Gaussian blur using a box blur approximation Param[1]: image (type: Image *) Param[2]: blurSize (type: int) -Function 265: ImageResize() (3 input parameters) +Function 266: ImageResize() (3 input parameters) Name: ImageResize Return type: void Description: Resize image (Bicubic scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 266: ImageResizeNN() (3 input parameters) +Function 267: ImageResizeNN() (3 input parameters) Name: ImageResizeNN Return type: void Description: Resize image (Nearest-Neighbor scaling algorithm) Param[1]: image (type: Image *) Param[2]: newWidth (type: int) Param[3]: newHeight (type: int) -Function 267: ImageResizeCanvas() (6 input parameters) +Function 268: ImageResizeCanvas() (6 input parameters) Name: ImageResizeCanvas Return type: void Description: Resize canvas and fill with color @@ -2573,12 +2582,12 @@ Function 267: ImageResizeCanvas() (6 input parameters) Param[4]: offsetX (type: int) Param[5]: offsetY (type: int) Param[6]: fill (type: Color) -Function 268: ImageMipmaps() (1 input parameters) +Function 269: ImageMipmaps() (1 input parameters) Name: ImageMipmaps Return type: void Description: Compute all mipmap levels for a provided image Param[1]: image (type: Image *) -Function 269: ImageDither() (5 input parameters) +Function 270: ImageDither() (5 input parameters) Name: ImageDither Return type: void Description: Dither image data to 16bpp or lower (Floyd-Steinberg dithering) @@ -2587,103 +2596,103 @@ Function 269: ImageDither() (5 input parameters) Param[3]: gBpp (type: int) Param[4]: bBpp (type: int) Param[5]: aBpp (type: int) -Function 270: ImageFlipVertical() (1 input parameters) +Function 271: ImageFlipVertical() (1 input parameters) Name: ImageFlipVertical Return type: void Description: Flip image vertically Param[1]: image (type: Image *) -Function 271: ImageFlipHorizontal() (1 input parameters) +Function 272: ImageFlipHorizontal() (1 input parameters) Name: ImageFlipHorizontal Return type: void Description: Flip image horizontally Param[1]: image (type: Image *) -Function 272: ImageRotateCW() (1 input parameters) +Function 273: ImageRotateCW() (1 input parameters) Name: ImageRotateCW Return type: void Description: Rotate image clockwise 90deg Param[1]: image (type: Image *) -Function 273: ImageRotateCCW() (1 input parameters) +Function 274: ImageRotateCCW() (1 input parameters) Name: ImageRotateCCW Return type: void Description: Rotate image counter-clockwise 90deg Param[1]: image (type: Image *) -Function 274: ImageColorTint() (2 input parameters) +Function 275: ImageColorTint() (2 input parameters) Name: ImageColorTint Return type: void Description: Modify image color: tint Param[1]: image (type: Image *) Param[2]: color (type: Color) -Function 275: ImageColorInvert() (1 input parameters) +Function 276: ImageColorInvert() (1 input parameters) Name: ImageColorInvert Return type: void Description: Modify image color: invert Param[1]: image (type: Image *) -Function 276: ImageColorGrayscale() (1 input parameters) +Function 277: ImageColorGrayscale() (1 input parameters) Name: ImageColorGrayscale Return type: void Description: Modify image color: grayscale Param[1]: image (type: Image *) -Function 277: ImageColorContrast() (2 input parameters) +Function 278: ImageColorContrast() (2 input parameters) Name: ImageColorContrast Return type: void Description: Modify image color: contrast (-100 to 100) Param[1]: image (type: Image *) Param[2]: contrast (type: float) -Function 278: ImageColorBrightness() (2 input parameters) +Function 279: ImageColorBrightness() (2 input parameters) Name: ImageColorBrightness Return type: void Description: Modify image color: brightness (-255 to 255) Param[1]: image (type: Image *) Param[2]: brightness (type: int) -Function 279: ImageColorReplace() (3 input parameters) +Function 280: ImageColorReplace() (3 input parameters) Name: ImageColorReplace Return type: void Description: Modify image color: replace color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: replace (type: Color) -Function 280: LoadImageColors() (1 input parameters) +Function 281: LoadImageColors() (1 input parameters) Name: LoadImageColors Return type: Color * Description: Load color data from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) -Function 281: LoadImagePalette() (3 input parameters) +Function 282: LoadImagePalette() (3 input parameters) Name: LoadImagePalette Return type: Color * Description: Load colors palette from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) Param[2]: maxPaletteSize (type: int) Param[3]: colorCount (type: int *) -Function 282: UnloadImageColors() (1 input parameters) +Function 283: UnloadImageColors() (1 input parameters) Name: UnloadImageColors Return type: void Description: Unload color data loaded with LoadImageColors() Param[1]: colors (type: Color *) -Function 283: UnloadImagePalette() (1 input parameters) +Function 284: UnloadImagePalette() (1 input parameters) Name: UnloadImagePalette Return type: void Description: Unload colors palette loaded with LoadImagePalette() Param[1]: colors (type: Color *) -Function 284: GetImageAlphaBorder() (2 input parameters) +Function 285: GetImageAlphaBorder() (2 input parameters) Name: GetImageAlphaBorder Return type: Rectangle Description: Get image alpha border rectangle Param[1]: image (type: Image) Param[2]: threshold (type: float) -Function 285: GetImageColor() (3 input parameters) +Function 286: GetImageColor() (3 input parameters) Name: GetImageColor Return type: Color Description: Get image pixel color at (x, y) position Param[1]: image (type: Image) Param[2]: x (type: int) Param[3]: y (type: int) -Function 286: ImageClearBackground() (2 input parameters) +Function 287: ImageClearBackground() (2 input parameters) Name: ImageClearBackground Return type: void Description: Clear image background with given color Param[1]: dst (type: Image *) Param[2]: color (type: Color) -Function 287: ImageDrawPixel() (4 input parameters) +Function 288: ImageDrawPixel() (4 input parameters) Name: ImageDrawPixel Return type: void Description: Draw pixel within an image @@ -2691,14 +2700,14 @@ Function 287: ImageDrawPixel() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: color (type: Color) -Function 288: ImageDrawPixelV() (3 input parameters) +Function 289: ImageDrawPixelV() (3 input parameters) Name: ImageDrawPixelV Return type: void Description: Draw pixel within an image (Vector version) Param[1]: dst (type: Image *) Param[2]: position (type: Vector2) Param[3]: color (type: Color) -Function 289: ImageDrawLine() (6 input parameters) +Function 290: ImageDrawLine() (6 input parameters) Name: ImageDrawLine Return type: void Description: Draw line within an image @@ -2708,7 +2717,7 @@ Function 289: ImageDrawLine() (6 input parameters) Param[4]: endPosX (type: int) Param[5]: endPosY (type: int) Param[6]: color (type: Color) -Function 290: ImageDrawLineV() (4 input parameters) +Function 291: ImageDrawLineV() (4 input parameters) Name: ImageDrawLineV Return type: void Description: Draw line within an image (Vector version) @@ -2716,7 +2725,7 @@ Function 290: ImageDrawLineV() (4 input parameters) Param[2]: start (type: Vector2) Param[3]: end (type: Vector2) Param[4]: color (type: Color) -Function 291: ImageDrawCircle() (5 input parameters) +Function 292: ImageDrawCircle() (5 input parameters) Name: ImageDrawCircle Return type: void Description: Draw a filled circle within an image @@ -2725,7 +2734,7 @@ Function 291: ImageDrawCircle() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 292: ImageDrawCircleV() (4 input parameters) +Function 293: ImageDrawCircleV() (4 input parameters) Name: ImageDrawCircleV Return type: void Description: Draw a filled circle within an image (Vector version) @@ -2733,7 +2742,7 @@ Function 292: ImageDrawCircleV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 293: ImageDrawCircleLines() (5 input parameters) +Function 294: ImageDrawCircleLines() (5 input parameters) Name: ImageDrawCircleLines Return type: void Description: Draw circle outline within an image @@ -2742,7 +2751,7 @@ Function 293: ImageDrawCircleLines() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 294: ImageDrawCircleLinesV() (4 input parameters) +Function 295: ImageDrawCircleLinesV() (4 input parameters) Name: ImageDrawCircleLinesV Return type: void Description: Draw circle outline within an image (Vector version) @@ -2750,7 +2759,7 @@ Function 294: ImageDrawCircleLinesV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 295: ImageDrawRectangle() (6 input parameters) +Function 296: ImageDrawRectangle() (6 input parameters) Name: ImageDrawRectangle Return type: void Description: Draw rectangle within an image @@ -2760,7 +2769,7 @@ Function 295: ImageDrawRectangle() (6 input parameters) Param[4]: width (type: int) Param[5]: height (type: int) Param[6]: color (type: Color) -Function 296: ImageDrawRectangleV() (4 input parameters) +Function 297: ImageDrawRectangleV() (4 input parameters) Name: ImageDrawRectangleV Return type: void Description: Draw rectangle within an image (Vector version) @@ -2768,14 +2777,14 @@ Function 296: ImageDrawRectangleV() (4 input parameters) Param[2]: position (type: Vector2) Param[3]: size (type: Vector2) Param[4]: color (type: Color) -Function 297: ImageDrawRectangleRec() (3 input parameters) +Function 298: ImageDrawRectangleRec() (3 input parameters) Name: ImageDrawRectangleRec Return type: void Description: Draw rectangle within an image Param[1]: dst (type: Image *) Param[2]: rec (type: Rectangle) Param[3]: color (type: Color) -Function 298: ImageDrawRectangleLines() (4 input parameters) +Function 299: ImageDrawRectangleLines() (4 input parameters) Name: ImageDrawRectangleLines Return type: void Description: Draw rectangle lines within an image @@ -2783,7 +2792,7 @@ Function 298: ImageDrawRectangleLines() (4 input parameters) Param[2]: rec (type: Rectangle) Param[3]: thick (type: int) Param[4]: color (type: Color) -Function 299: ImageDraw() (5 input parameters) +Function 300: ImageDraw() (5 input parameters) Name: ImageDraw Return type: void Description: Draw a source image within a destination image (tint applied to source) @@ -2792,7 +2801,7 @@ Function 299: ImageDraw() (5 input parameters) Param[3]: srcRec (type: Rectangle) Param[4]: dstRec (type: Rectangle) Param[5]: tint (type: Color) -Function 300: ImageDrawText() (6 input parameters) +Function 301: ImageDrawText() (6 input parameters) Name: ImageDrawText Return type: void Description: Draw text (using default font) within an image (destination) @@ -2802,7 +2811,7 @@ Function 300: ImageDrawText() (6 input parameters) Param[4]: posY (type: int) Param[5]: fontSize (type: int) Param[6]: color (type: Color) -Function 301: ImageDrawTextEx() (7 input parameters) +Function 302: ImageDrawTextEx() (7 input parameters) Name: ImageDrawTextEx Return type: void Description: Draw text (custom sprite font) within an image (destination) @@ -2813,79 +2822,79 @@ Function 301: ImageDrawTextEx() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 302: LoadTexture() (1 input parameters) +Function 303: LoadTexture() (1 input parameters) Name: LoadTexture Return type: Texture2D Description: Load texture from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 303: LoadTextureFromImage() (1 input parameters) +Function 304: LoadTextureFromImage() (1 input parameters) Name: LoadTextureFromImage Return type: Texture2D Description: Load texture from image data Param[1]: image (type: Image) -Function 304: LoadTextureCubemap() (2 input parameters) +Function 305: LoadTextureCubemap() (2 input parameters) Name: LoadTextureCubemap Return type: TextureCubemap Description: Load cubemap from image, multiple image cubemap layouts supported Param[1]: image (type: Image) Param[2]: layout (type: int) -Function 305: LoadRenderTexture() (2 input parameters) +Function 306: LoadRenderTexture() (2 input parameters) Name: LoadRenderTexture Return type: RenderTexture2D Description: Load texture for rendering (framebuffer) Param[1]: width (type: int) Param[2]: height (type: int) -Function 306: IsTextureReady() (1 input parameters) +Function 307: IsTextureReady() (1 input parameters) Name: IsTextureReady Return type: bool Description: Check if a texture is ready Param[1]: texture (type: Texture2D) -Function 307: UnloadTexture() (1 input parameters) +Function 308: UnloadTexture() (1 input parameters) Name: UnloadTexture Return type: void Description: Unload texture from GPU memory (VRAM) Param[1]: texture (type: Texture2D) -Function 308: IsRenderTextureReady() (1 input parameters) +Function 309: IsRenderTextureReady() (1 input parameters) Name: IsRenderTextureReady Return type: bool Description: Check if a render texture is ready Param[1]: target (type: RenderTexture2D) -Function 309: UnloadRenderTexture() (1 input parameters) +Function 310: UnloadRenderTexture() (1 input parameters) Name: UnloadRenderTexture Return type: void Description: Unload render texture from GPU memory (VRAM) Param[1]: target (type: RenderTexture2D) -Function 310: UpdateTexture() (2 input parameters) +Function 311: UpdateTexture() (2 input parameters) Name: UpdateTexture Return type: void Description: Update GPU texture with new data Param[1]: texture (type: Texture2D) Param[2]: pixels (type: const void *) -Function 311: UpdateTextureRec() (3 input parameters) +Function 312: UpdateTextureRec() (3 input parameters) Name: UpdateTextureRec Return type: void Description: Update GPU texture rectangle with new data Param[1]: texture (type: Texture2D) Param[2]: rec (type: Rectangle) Param[3]: pixels (type: const void *) -Function 312: GenTextureMipmaps() (1 input parameters) +Function 313: GenTextureMipmaps() (1 input parameters) Name: GenTextureMipmaps Return type: void Description: Generate GPU mipmaps for a texture Param[1]: texture (type: Texture2D *) -Function 313: SetTextureFilter() (2 input parameters) +Function 314: SetTextureFilter() (2 input parameters) Name: SetTextureFilter Return type: void Description: Set texture scaling filter mode Param[1]: texture (type: Texture2D) Param[2]: filter (type: int) -Function 314: SetTextureWrap() (2 input parameters) +Function 315: SetTextureWrap() (2 input parameters) Name: SetTextureWrap Return type: void Description: Set texture wrapping mode Param[1]: texture (type: Texture2D) Param[2]: wrap (type: int) -Function 315: DrawTexture() (4 input parameters) +Function 316: DrawTexture() (4 input parameters) Name: DrawTexture Return type: void Description: Draw a Texture2D @@ -2893,14 +2902,14 @@ Function 315: DrawTexture() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: tint (type: Color) -Function 316: DrawTextureV() (3 input parameters) +Function 317: DrawTextureV() (3 input parameters) Name: DrawTextureV Return type: void Description: Draw a Texture2D with position defined as Vector2 Param[1]: texture (type: Texture2D) Param[2]: position (type: Vector2) Param[3]: tint (type: Color) -Function 317: DrawTextureEx() (5 input parameters) +Function 318: DrawTextureEx() (5 input parameters) Name: DrawTextureEx Return type: void Description: Draw a Texture2D with extended parameters @@ -2909,7 +2918,7 @@ Function 317: DrawTextureEx() (5 input parameters) Param[3]: rotation (type: float) Param[4]: scale (type: float) Param[5]: tint (type: Color) -Function 318: DrawTextureRec() (4 input parameters) +Function 319: DrawTextureRec() (4 input parameters) Name: DrawTextureRec Return type: void Description: Draw a part of a texture defined by a rectangle @@ -2917,7 +2926,7 @@ Function 318: DrawTextureRec() (4 input parameters) Param[2]: source (type: Rectangle) Param[3]: position (type: Vector2) Param[4]: tint (type: Color) -Function 319: DrawTexturePro() (6 input parameters) +Function 320: DrawTexturePro() (6 input parameters) Name: DrawTexturePro Return type: void Description: Draw a part of a texture defined by a rectangle with 'pro' parameters @@ -2927,7 +2936,7 @@ Function 319: DrawTexturePro() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 320: DrawTextureNPatch() (6 input parameters) +Function 321: DrawTextureNPatch() (6 input parameters) Name: DrawTextureNPatch Return type: void Description: Draws a texture (or part of it) that stretches or shrinks nicely @@ -2937,106 +2946,106 @@ Function 320: DrawTextureNPatch() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 321: Fade() (2 input parameters) +Function 322: Fade() (2 input parameters) Name: Fade Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 322: ColorToInt() (1 input parameters) +Function 323: ColorToInt() (1 input parameters) Name: ColorToInt Return type: int Description: Get hexadecimal value for a Color Param[1]: color (type: Color) -Function 323: ColorNormalize() (1 input parameters) +Function 324: ColorNormalize() (1 input parameters) Name: ColorNormalize Return type: Vector4 Description: Get Color normalized as float [0..1] Param[1]: color (type: Color) -Function 324: ColorFromNormalized() (1 input parameters) +Function 325: ColorFromNormalized() (1 input parameters) Name: ColorFromNormalized Return type: Color Description: Get Color from normalized values [0..1] Param[1]: normalized (type: Vector4) -Function 325: ColorToHSV() (1 input parameters) +Function 326: ColorToHSV() (1 input parameters) Name: ColorToHSV Return type: Vector3 Description: Get HSV values for a Color, hue [0..360], saturation/value [0..1] Param[1]: color (type: Color) -Function 326: ColorFromHSV() (3 input parameters) +Function 327: ColorFromHSV() (3 input parameters) Name: ColorFromHSV Return type: Color Description: Get a Color from HSV values, hue [0..360], saturation/value [0..1] Param[1]: hue (type: float) Param[2]: saturation (type: float) Param[3]: value (type: float) -Function 327: ColorTint() (2 input parameters) +Function 328: ColorTint() (2 input parameters) Name: ColorTint Return type: Color Description: Get color multiplied with another color Param[1]: color (type: Color) Param[2]: tint (type: Color) -Function 328: ColorBrightness() (2 input parameters) +Function 329: ColorBrightness() (2 input parameters) Name: ColorBrightness Return type: Color Description: Get color with brightness correction, brightness factor goes from -1.0f to 1.0f Param[1]: color (type: Color) Param[2]: factor (type: float) -Function 329: ColorContrast() (2 input parameters) +Function 330: ColorContrast() (2 input parameters) Name: ColorContrast Return type: Color Description: Get color with contrast correction, contrast values between -1.0f and 1.0f Param[1]: color (type: Color) Param[2]: contrast (type: float) -Function 330: ColorAlpha() (2 input parameters) +Function 331: ColorAlpha() (2 input parameters) Name: ColorAlpha Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 331: ColorAlphaBlend() (3 input parameters) +Function 332: ColorAlphaBlend() (3 input parameters) Name: ColorAlphaBlend Return type: Color Description: Get src alpha-blended into dst color with tint Param[1]: dst (type: Color) Param[2]: src (type: Color) Param[3]: tint (type: Color) -Function 332: GetColor() (1 input parameters) +Function 333: GetColor() (1 input parameters) Name: GetColor Return type: Color Description: Get Color structure from hexadecimal value Param[1]: hexValue (type: unsigned int) -Function 333: GetPixelColor() (2 input parameters) +Function 334: GetPixelColor() (2 input parameters) Name: GetPixelColor Return type: Color Description: Get Color from a source pixel pointer of certain format Param[1]: srcPtr (type: void *) Param[2]: format (type: int) -Function 334: SetPixelColor() (3 input parameters) +Function 335: SetPixelColor() (3 input parameters) Name: SetPixelColor Return type: void Description: Set color formatted into destination pixel pointer Param[1]: dstPtr (type: void *) Param[2]: color (type: Color) Param[3]: format (type: int) -Function 335: GetPixelDataSize() (3 input parameters) +Function 336: GetPixelDataSize() (3 input parameters) Name: GetPixelDataSize Return type: int Description: Get pixel data size in bytes for certain format Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: format (type: int) -Function 336: GetFontDefault() (0 input parameters) +Function 337: GetFontDefault() (0 input parameters) Name: GetFontDefault Return type: Font Description: Get the default Font No input parameters -Function 337: LoadFont() (1 input parameters) +Function 338: LoadFont() (1 input parameters) Name: LoadFont Return type: Font Description: Load font from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 338: LoadFontEx() (4 input parameters) +Function 339: LoadFontEx() (4 input parameters) Name: LoadFontEx Return type: Font Description: Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set @@ -3044,14 +3053,14 @@ Function 338: LoadFontEx() (4 input parameters) Param[2]: fontSize (type: int) Param[3]: fontChars (type: int *) Param[4]: glyphCount (type: int) -Function 339: LoadFontFromImage() (3 input parameters) +Function 340: LoadFontFromImage() (3 input parameters) Name: LoadFontFromImage Return type: Font Description: Load font from Image (XNA style) Param[1]: image (type: Image) Param[2]: key (type: Color) Param[3]: firstChar (type: int) -Function 340: LoadFontFromMemory() (6 input parameters) +Function 341: LoadFontFromMemory() (6 input parameters) Name: LoadFontFromMemory Return type: Font Description: Load font from memory buffer, fileType refers to extension: i.e. '.ttf' @@ -3061,12 +3070,12 @@ Function 340: LoadFontFromMemory() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: fontChars (type: int *) Param[6]: glyphCount (type: int) -Function 341: IsFontReady() (1 input parameters) +Function 342: IsFontReady() (1 input parameters) Name: IsFontReady Return type: bool Description: Check if a font is ready Param[1]: font (type: Font) -Function 342: LoadFontData() (6 input parameters) +Function 343: LoadFontData() (6 input parameters) Name: LoadFontData Return type: GlyphInfo * Description: Load font data for further use @@ -3076,7 +3085,7 @@ Function 342: LoadFontData() (6 input parameters) Param[4]: fontChars (type: int *) Param[5]: glyphCount (type: int) Param[6]: type (type: int) -Function 343: GenImageFontAtlas() (6 input parameters) +Function 344: GenImageFontAtlas() (6 input parameters) Name: GenImageFontAtlas Return type: Image Description: Generate image font atlas using chars info @@ -3086,30 +3095,30 @@ Function 343: GenImageFontAtlas() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: padding (type: int) Param[6]: packMethod (type: int) -Function 344: UnloadFontData() (2 input parameters) +Function 345: UnloadFontData() (2 input parameters) Name: UnloadFontData Return type: void Description: Unload font chars info data (RAM) Param[1]: chars (type: GlyphInfo *) Param[2]: glyphCount (type: int) -Function 345: UnloadFont() (1 input parameters) +Function 346: UnloadFont() (1 input parameters) Name: UnloadFont Return type: void Description: Unload font from GPU memory (VRAM) Param[1]: font (type: Font) -Function 346: ExportFontAsCode() (2 input parameters) +Function 347: ExportFontAsCode() (2 input parameters) Name: ExportFontAsCode Return type: bool Description: Export font as code file, returns true on success Param[1]: font (type: Font) Param[2]: fileName (type: const char *) -Function 347: DrawFPS() (2 input parameters) +Function 348: DrawFPS() (2 input parameters) Name: DrawFPS Return type: void Description: Draw current FPS Param[1]: posX (type: int) Param[2]: posY (type: int) -Function 348: DrawText() (5 input parameters) +Function 349: DrawText() (5 input parameters) Name: DrawText Return type: void Description: Draw text (using default font) @@ -3118,7 +3127,7 @@ Function 348: DrawText() (5 input parameters) Param[3]: posY (type: int) Param[4]: fontSize (type: int) Param[5]: color (type: Color) -Function 349: DrawTextEx() (6 input parameters) +Function 350: DrawTextEx() (6 input parameters) Name: DrawTextEx Return type: void Description: Draw text using font and additional parameters @@ -3128,7 +3137,7 @@ Function 349: DrawTextEx() (6 input parameters) Param[4]: fontSize (type: float) Param[5]: spacing (type: float) Param[6]: tint (type: Color) -Function 350: DrawTextPro() (8 input parameters) +Function 351: DrawTextPro() (8 input parameters) Name: DrawTextPro Return type: void Description: Draw text using Font and pro parameters (rotation) @@ -3140,7 +3149,7 @@ Function 350: DrawTextPro() (8 input parameters) Param[6]: fontSize (type: float) Param[7]: spacing (type: float) Param[8]: tint (type: Color) -Function 351: DrawTextCodepoint() (5 input parameters) +Function 352: DrawTextCodepoint() (5 input parameters) Name: DrawTextCodepoint Return type: void Description: Draw one character (codepoint) @@ -3149,7 +3158,7 @@ Function 351: DrawTextCodepoint() (5 input parameters) Param[3]: position (type: Vector2) Param[4]: fontSize (type: float) Param[5]: tint (type: Color) -Function 352: DrawTextCodepoints() (7 input parameters) +Function 353: DrawTextCodepoints() (7 input parameters) Name: DrawTextCodepoints Return type: void Description: Draw multiple character (codepoint) @@ -3160,13 +3169,13 @@ Function 352: DrawTextCodepoints() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 353: MeasureText() (2 input parameters) +Function 354: MeasureText() (2 input parameters) Name: MeasureText Return type: int Description: Measure string width for default font Param[1]: text (type: const char *) Param[2]: fontSize (type: int) -Function 354: MeasureTextEx() (4 input parameters) +Function 355: MeasureTextEx() (4 input parameters) Name: MeasureTextEx Return type: Vector2 Description: Measure string size for Font @@ -3174,180 +3183,180 @@ Function 354: MeasureTextEx() (4 input parameters) Param[2]: text (type: const char *) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) -Function 355: GetGlyphIndex() (2 input parameters) +Function 356: GetGlyphIndex() (2 input parameters) Name: GetGlyphIndex Return type: int Description: Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 356: GetGlyphInfo() (2 input parameters) +Function 357: GetGlyphInfo() (2 input parameters) Name: GetGlyphInfo Return type: GlyphInfo Description: Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 357: GetGlyphAtlasRec() (2 input parameters) +Function 358: GetGlyphAtlasRec() (2 input parameters) Name: GetGlyphAtlasRec Return type: Rectangle Description: Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 358: LoadUTF8() (2 input parameters) +Function 359: LoadUTF8() (2 input parameters) Name: LoadUTF8 Return type: char * Description: Load UTF-8 text encoded from codepoints array Param[1]: codepoints (type: const int *) Param[2]: length (type: int) -Function 359: UnloadUTF8() (1 input parameters) +Function 360: UnloadUTF8() (1 input parameters) Name: UnloadUTF8 Return type: void Description: Unload UTF-8 text encoded from codepoints array Param[1]: text (type: char *) -Function 360: LoadCodepoints() (2 input parameters) +Function 361: LoadCodepoints() (2 input parameters) Name: LoadCodepoints Return type: int * Description: Load all codepoints from a UTF-8 text string, codepoints count returned by parameter Param[1]: text (type: const char *) Param[2]: count (type: int *) -Function 361: UnloadCodepoints() (1 input parameters) +Function 362: UnloadCodepoints() (1 input parameters) Name: UnloadCodepoints Return type: void Description: Unload codepoints data from memory Param[1]: codepoints (type: int *) -Function 362: GetCodepointCount() (1 input parameters) +Function 363: GetCodepointCount() (1 input parameters) Name: GetCodepointCount Return type: int Description: Get total number of codepoints in a UTF-8 encoded string Param[1]: text (type: const char *) -Function 363: GetCodepoint() (2 input parameters) +Function 364: GetCodepoint() (2 input parameters) Name: GetCodepoint Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 364: GetCodepointNext() (2 input parameters) +Function 365: GetCodepointNext() (2 input parameters) Name: GetCodepointNext Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 365: GetCodepointPrevious() (2 input parameters) +Function 366: GetCodepointPrevious() (2 input parameters) Name: GetCodepointPrevious Return type: int Description: Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 366: CodepointToUTF8() (2 input parameters) +Function 367: CodepointToUTF8() (2 input parameters) Name: CodepointToUTF8 Return type: const char * Description: Encode one codepoint into UTF-8 byte array (array length returned as parameter) Param[1]: codepoint (type: int) Param[2]: utf8Size (type: int *) -Function 367: TextCopy() (2 input parameters) +Function 368: TextCopy() (2 input parameters) Name: TextCopy Return type: int Description: Copy one string to another, returns bytes copied Param[1]: dst (type: char *) Param[2]: src (type: const char *) -Function 368: TextIsEqual() (2 input parameters) +Function 369: TextIsEqual() (2 input parameters) Name: TextIsEqual Return type: bool Description: Check if two text string are equal Param[1]: text1 (type: const char *) Param[2]: text2 (type: const char *) -Function 369: TextLength() (1 input parameters) +Function 370: TextLength() (1 input parameters) Name: TextLength Return type: unsigned int Description: Get text length, checks for '\0' ending Param[1]: text (type: const char *) -Function 370: TextFormat() (2 input parameters) +Function 371: TextFormat() (2 input parameters) Name: TextFormat Return type: const char * Description: Text formatting with variables (sprintf() style) Param[1]: text (type: const char *) Param[2]: args (type: ...) -Function 371: TextSubtext() (3 input parameters) +Function 372: TextSubtext() (3 input parameters) Name: TextSubtext Return type: const char * Description: Get a piece of a text string Param[1]: text (type: const char *) Param[2]: position (type: int) Param[3]: length (type: int) -Function 372: TextReplace() (3 input parameters) +Function 373: TextReplace() (3 input parameters) Name: TextReplace Return type: char * Description: Replace text string (WARNING: memory must be freed!) Param[1]: text (type: char *) Param[2]: replace (type: const char *) Param[3]: by (type: const char *) -Function 373: TextInsert() (3 input parameters) +Function 374: TextInsert() (3 input parameters) Name: TextInsert Return type: char * Description: Insert text in a position (WARNING: memory must be freed!) Param[1]: text (type: const char *) Param[2]: insert (type: const char *) Param[3]: position (type: int) -Function 374: TextJoin() (3 input parameters) +Function 375: TextJoin() (3 input parameters) Name: TextJoin Return type: const char * Description: Join text strings with delimiter Param[1]: textList (type: const char **) Param[2]: count (type: int) Param[3]: delimiter (type: const char *) -Function 375: TextSplit() (3 input parameters) +Function 376: TextSplit() (3 input parameters) Name: TextSplit Return type: const char ** Description: Split text into multiple strings Param[1]: text (type: const char *) Param[2]: delimiter (type: char) Param[3]: count (type: int *) -Function 376: TextAppend() (3 input parameters) +Function 377: TextAppend() (3 input parameters) Name: TextAppend Return type: void Description: Append text at specific position and move cursor! Param[1]: text (type: char *) Param[2]: append (type: const char *) Param[3]: position (type: int *) -Function 377: TextFindIndex() (2 input parameters) +Function 378: TextFindIndex() (2 input parameters) Name: TextFindIndex Return type: int Description: Find first text occurrence within a string Param[1]: text (type: const char *) Param[2]: find (type: const char *) -Function 378: TextToUpper() (1 input parameters) +Function 379: TextToUpper() (1 input parameters) Name: TextToUpper Return type: const char * Description: Get upper case version of provided string Param[1]: text (type: const char *) -Function 379: TextToLower() (1 input parameters) +Function 380: TextToLower() (1 input parameters) Name: TextToLower Return type: const char * Description: Get lower case version of provided string Param[1]: text (type: const char *) -Function 380: TextToPascal() (1 input parameters) +Function 381: TextToPascal() (1 input parameters) Name: TextToPascal Return type: const char * Description: Get Pascal case notation version of provided string Param[1]: text (type: const char *) -Function 381: TextToInteger() (1 input parameters) +Function 382: TextToInteger() (1 input parameters) Name: TextToInteger Return type: int Description: Get integer value from text (negative values not supported) Param[1]: text (type: const char *) -Function 382: DrawLine3D() (3 input parameters) +Function 383: DrawLine3D() (3 input parameters) Name: DrawLine3D Return type: void Description: Draw a line in 3D world space Param[1]: startPos (type: Vector3) Param[2]: endPos (type: Vector3) Param[3]: color (type: Color) -Function 383: DrawPoint3D() (2 input parameters) +Function 384: DrawPoint3D() (2 input parameters) Name: DrawPoint3D Return type: void Description: Draw a point in 3D space, actually a small line Param[1]: position (type: Vector3) Param[2]: color (type: Color) -Function 384: DrawCircle3D() (5 input parameters) +Function 385: DrawCircle3D() (5 input parameters) Name: DrawCircle3D Return type: void Description: Draw a circle in 3D world space @@ -3356,7 +3365,7 @@ Function 384: DrawCircle3D() (5 input parameters) Param[3]: rotationAxis (type: Vector3) Param[4]: rotationAngle (type: float) Param[5]: color (type: Color) -Function 385: DrawTriangle3D() (4 input parameters) +Function 386: DrawTriangle3D() (4 input parameters) Name: DrawTriangle3D Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -3364,14 +3373,14 @@ Function 385: DrawTriangle3D() (4 input parameters) Param[2]: v2 (type: Vector3) Param[3]: v3 (type: Vector3) Param[4]: color (type: Color) -Function 386: DrawTriangleStrip3D() (3 input parameters) +Function 387: DrawTriangleStrip3D() (3 input parameters) Name: DrawTriangleStrip3D Return type: void Description: Draw a triangle strip defined by points Param[1]: points (type: Vector3 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 387: DrawCube() (5 input parameters) +Function 388: DrawCube() (5 input parameters) Name: DrawCube Return type: void Description: Draw cube @@ -3380,14 +3389,14 @@ Function 387: DrawCube() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 388: DrawCubeV() (3 input parameters) +Function 389: DrawCubeV() (3 input parameters) Name: DrawCubeV Return type: void Description: Draw cube (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 389: DrawCubeWires() (5 input parameters) +Function 390: DrawCubeWires() (5 input parameters) Name: DrawCubeWires Return type: void Description: Draw cube wires @@ -3396,21 +3405,21 @@ Function 389: DrawCubeWires() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 390: DrawCubeWiresV() (3 input parameters) +Function 391: DrawCubeWiresV() (3 input parameters) Name: DrawCubeWiresV Return type: void Description: Draw cube wires (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 391: DrawSphere() (3 input parameters) +Function 392: DrawSphere() (3 input parameters) Name: DrawSphere Return type: void Description: Draw sphere Param[1]: centerPos (type: Vector3) Param[2]: radius (type: float) Param[3]: color (type: Color) -Function 392: DrawSphereEx() (5 input parameters) +Function 393: DrawSphereEx() (5 input parameters) Name: DrawSphereEx Return type: void Description: Draw sphere with extended parameters @@ -3419,7 +3428,7 @@ Function 392: DrawSphereEx() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 393: DrawSphereWires() (5 input parameters) +Function 394: DrawSphereWires() (5 input parameters) Name: DrawSphereWires Return type: void Description: Draw sphere wires @@ -3428,7 +3437,7 @@ Function 393: DrawSphereWires() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 394: DrawCylinder() (6 input parameters) +Function 395: DrawCylinder() (6 input parameters) Name: DrawCylinder Return type: void Description: Draw a cylinder/cone @@ -3438,7 +3447,7 @@ Function 394: DrawCylinder() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 395: DrawCylinderEx() (6 input parameters) +Function 396: DrawCylinderEx() (6 input parameters) Name: DrawCylinderEx Return type: void Description: Draw a cylinder with base at startPos and top at endPos @@ -3448,7 +3457,7 @@ Function 395: DrawCylinderEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 396: DrawCylinderWires() (6 input parameters) +Function 397: DrawCylinderWires() (6 input parameters) Name: DrawCylinderWires Return type: void Description: Draw a cylinder/cone wires @@ -3458,7 +3467,7 @@ Function 396: DrawCylinderWires() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 397: DrawCylinderWiresEx() (6 input parameters) +Function 398: DrawCylinderWiresEx() (6 input parameters) Name: DrawCylinderWiresEx Return type: void Description: Draw a cylinder wires with base at startPos and top at endPos @@ -3468,7 +3477,7 @@ Function 397: DrawCylinderWiresEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 398: DrawCapsule() (6 input parameters) +Function 399: DrawCapsule() (6 input parameters) Name: DrawCapsule Return type: void Description: Draw a capsule with the center of its sphere caps at startPos and endPos @@ -3478,7 +3487,7 @@ Function 398: DrawCapsule() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 399: DrawCapsuleWires() (6 input parameters) +Function 400: DrawCapsuleWires() (6 input parameters) Name: DrawCapsuleWires Return type: void Description: Draw capsule wireframe with the center of its sphere caps at startPos and endPos @@ -3488,51 +3497,51 @@ Function 399: DrawCapsuleWires() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 400: DrawPlane() (3 input parameters) +Function 401: DrawPlane() (3 input parameters) Name: DrawPlane Return type: void Description: Draw a plane XZ Param[1]: centerPos (type: Vector3) Param[2]: size (type: Vector2) Param[3]: color (type: Color) -Function 401: DrawRay() (2 input parameters) +Function 402: DrawRay() (2 input parameters) Name: DrawRay Return type: void Description: Draw a ray line Param[1]: ray (type: Ray) Param[2]: color (type: Color) -Function 402: DrawGrid() (2 input parameters) +Function 403: DrawGrid() (2 input parameters) Name: DrawGrid Return type: void Description: Draw a grid (centered at (0, 0, 0)) Param[1]: slices (type: int) Param[2]: spacing (type: float) -Function 403: LoadModel() (1 input parameters) +Function 404: LoadModel() (1 input parameters) Name: LoadModel Return type: Model Description: Load model from files (meshes and materials) Param[1]: fileName (type: const char *) -Function 404: LoadModelFromMesh() (1 input parameters) +Function 405: LoadModelFromMesh() (1 input parameters) Name: LoadModelFromMesh Return type: Model Description: Load model from generated mesh (default material) Param[1]: mesh (type: Mesh) -Function 405: IsModelReady() (1 input parameters) +Function 406: IsModelReady() (1 input parameters) Name: IsModelReady Return type: bool Description: Check if a model is ready Param[1]: model (type: Model) -Function 406: UnloadModel() (1 input parameters) +Function 407: UnloadModel() (1 input parameters) Name: UnloadModel Return type: void Description: Unload model (including meshes) from memory (RAM and/or VRAM) Param[1]: model (type: Model) -Function 407: GetModelBoundingBox() (1 input parameters) +Function 408: GetModelBoundingBox() (1 input parameters) Name: GetModelBoundingBox Return type: BoundingBox Description: Compute model bounding box limits (considers all meshes) Param[1]: model (type: Model) -Function 408: DrawModel() (4 input parameters) +Function 409: DrawModel() (4 input parameters) Name: DrawModel Return type: void Description: Draw a model (with texture if set) @@ -3540,7 +3549,7 @@ Function 408: DrawModel() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 409: DrawModelEx() (6 input parameters) +Function 410: DrawModelEx() (6 input parameters) Name: DrawModelEx Return type: void Description: Draw a model with extended parameters @@ -3550,7 +3559,7 @@ Function 409: DrawModelEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 410: DrawModelWires() (4 input parameters) +Function 411: DrawModelWires() (4 input parameters) Name: DrawModelWires Return type: void Description: Draw a model wires (with texture if set) @@ -3558,7 +3567,7 @@ Function 410: DrawModelWires() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 411: DrawModelWiresEx() (6 input parameters) +Function 412: DrawModelWiresEx() (6 input parameters) Name: DrawModelWiresEx Return type: void Description: Draw a model wires (with texture if set) with extended parameters @@ -3568,13 +3577,13 @@ Function 411: DrawModelWiresEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 412: DrawBoundingBox() (2 input parameters) +Function 413: DrawBoundingBox() (2 input parameters) Name: DrawBoundingBox Return type: void Description: Draw bounding box (wires) Param[1]: box (type: BoundingBox) Param[2]: color (type: Color) -Function 413: DrawBillboard() (5 input parameters) +Function 414: DrawBillboard() (5 input parameters) Name: DrawBillboard Return type: void Description: Draw a billboard texture @@ -3583,7 +3592,7 @@ Function 413: DrawBillboard() (5 input parameters) Param[3]: position (type: Vector3) Param[4]: size (type: float) Param[5]: tint (type: Color) -Function 414: DrawBillboardRec() (6 input parameters) +Function 415: DrawBillboardRec() (6 input parameters) Name: DrawBillboardRec Return type: void Description: Draw a billboard texture defined by source @@ -3593,7 +3602,7 @@ Function 414: DrawBillboardRec() (6 input parameters) Param[4]: position (type: Vector3) Param[5]: size (type: Vector2) Param[6]: tint (type: Color) -Function 415: DrawBillboardPro() (9 input parameters) +Function 416: DrawBillboardPro() (9 input parameters) Name: DrawBillboardPro Return type: void Description: Draw a billboard texture defined by source and rotation @@ -3606,13 +3615,13 @@ Function 415: DrawBillboardPro() (9 input parameters) Param[7]: origin (type: Vector2) Param[8]: rotation (type: float) Param[9]: tint (type: Color) -Function 416: UploadMesh() (2 input parameters) +Function 417: UploadMesh() (2 input parameters) Name: UploadMesh Return type: void Description: Upload mesh vertex data in GPU and provide VAO/VBO ids Param[1]: mesh (type: Mesh *) Param[2]: dynamic (type: bool) -Function 417: UpdateMeshBuffer() (5 input parameters) +Function 418: UpdateMeshBuffer() (5 input parameters) Name: UpdateMeshBuffer Return type: void Description: Update mesh vertex data in GPU for a specific buffer index @@ -3621,19 +3630,19 @@ Function 417: UpdateMeshBuffer() (5 input parameters) Param[3]: data (type: const void *) Param[4]: dataSize (type: int) Param[5]: offset (type: int) -Function 418: UnloadMesh() (1 input parameters) +Function 419: UnloadMesh() (1 input parameters) Name: UnloadMesh Return type: void Description: Unload mesh data from CPU and GPU Param[1]: mesh (type: Mesh) -Function 419: DrawMesh() (3 input parameters) +Function 420: DrawMesh() (3 input parameters) Name: DrawMesh Return type: void Description: Draw a 3d mesh with material and transform Param[1]: mesh (type: Mesh) Param[2]: material (type: Material) Param[3]: transform (type: Matrix) -Function 420: DrawMeshInstanced() (4 input parameters) +Function 421: DrawMeshInstanced() (4 input parameters) Name: DrawMeshInstanced Return type: void Description: Draw multiple mesh instances with material and different transforms @@ -3641,29 +3650,29 @@ Function 420: DrawMeshInstanced() (4 input parameters) Param[2]: material (type: Material) Param[3]: transforms (type: const Matrix *) Param[4]: instances (type: int) -Function 421: ExportMesh() (2 input parameters) +Function 422: ExportMesh() (2 input parameters) Name: ExportMesh Return type: bool Description: Export mesh data to file, returns true on success Param[1]: mesh (type: Mesh) Param[2]: fileName (type: const char *) -Function 422: GetMeshBoundingBox() (1 input parameters) +Function 423: GetMeshBoundingBox() (1 input parameters) Name: GetMeshBoundingBox Return type: BoundingBox Description: Compute mesh bounding box limits Param[1]: mesh (type: Mesh) -Function 423: GenMeshTangents() (1 input parameters) +Function 424: GenMeshTangents() (1 input parameters) Name: GenMeshTangents Return type: void Description: Compute mesh tangents Param[1]: mesh (type: Mesh *) -Function 424: GenMeshPoly() (2 input parameters) +Function 425: GenMeshPoly() (2 input parameters) Name: GenMeshPoly Return type: Mesh Description: Generate polygonal mesh Param[1]: sides (type: int) Param[2]: radius (type: float) -Function 425: GenMeshPlane() (4 input parameters) +Function 426: GenMeshPlane() (4 input parameters) Name: GenMeshPlane Return type: Mesh Description: Generate plane mesh (with subdivisions) @@ -3671,42 +3680,42 @@ Function 425: GenMeshPlane() (4 input parameters) Param[2]: length (type: float) Param[3]: resX (type: int) Param[4]: resZ (type: int) -Function 426: GenMeshCube() (3 input parameters) +Function 427: GenMeshCube() (3 input parameters) Name: GenMeshCube Return type: Mesh Description: Generate cuboid mesh Param[1]: width (type: float) Param[2]: height (type: float) Param[3]: length (type: float) -Function 427: GenMeshSphere() (3 input parameters) +Function 428: GenMeshSphere() (3 input parameters) Name: GenMeshSphere Return type: Mesh Description: Generate sphere mesh (standard sphere) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 428: GenMeshHemiSphere() (3 input parameters) +Function 429: GenMeshHemiSphere() (3 input parameters) Name: GenMeshHemiSphere Return type: Mesh Description: Generate half-sphere mesh (no bottom cap) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 429: GenMeshCylinder() (3 input parameters) +Function 430: GenMeshCylinder() (3 input parameters) Name: GenMeshCylinder Return type: Mesh Description: Generate cylinder mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 430: GenMeshCone() (3 input parameters) +Function 431: GenMeshCone() (3 input parameters) Name: GenMeshCone Return type: Mesh Description: Generate cone/pyramid mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 431: GenMeshTorus() (4 input parameters) +Function 432: GenMeshTorus() (4 input parameters) Name: GenMeshTorus Return type: Mesh Description: Generate torus mesh @@ -3714,7 +3723,7 @@ Function 431: GenMeshTorus() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 432: GenMeshKnot() (4 input parameters) +Function 433: GenMeshKnot() (4 input parameters) Name: GenMeshKnot Return type: Mesh Description: Generate trefoil knot mesh @@ -3722,84 +3731,84 @@ Function 432: GenMeshKnot() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 433: GenMeshHeightmap() (2 input parameters) +Function 434: GenMeshHeightmap() (2 input parameters) Name: GenMeshHeightmap Return type: Mesh Description: Generate heightmap mesh from image data Param[1]: heightmap (type: Image) Param[2]: size (type: Vector3) -Function 434: GenMeshCubicmap() (2 input parameters) +Function 435: GenMeshCubicmap() (2 input parameters) Name: GenMeshCubicmap Return type: Mesh Description: Generate cubes-based map mesh from image data Param[1]: cubicmap (type: Image) Param[2]: cubeSize (type: Vector3) -Function 435: LoadMaterials() (2 input parameters) +Function 436: LoadMaterials() (2 input parameters) Name: LoadMaterials Return type: Material * Description: Load materials from model file Param[1]: fileName (type: const char *) Param[2]: materialCount (type: int *) -Function 436: LoadMaterialDefault() (0 input parameters) +Function 437: LoadMaterialDefault() (0 input parameters) Name: LoadMaterialDefault Return type: Material Description: Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) No input parameters -Function 437: IsMaterialReady() (1 input parameters) +Function 438: IsMaterialReady() (1 input parameters) Name: IsMaterialReady Return type: bool Description: Check if a material is ready Param[1]: material (type: Material) -Function 438: UnloadMaterial() (1 input parameters) +Function 439: UnloadMaterial() (1 input parameters) Name: UnloadMaterial Return type: void Description: Unload material from GPU memory (VRAM) Param[1]: material (type: Material) -Function 439: SetMaterialTexture() (3 input parameters) +Function 440: SetMaterialTexture() (3 input parameters) Name: SetMaterialTexture Return type: void Description: Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) Param[1]: material (type: Material *) Param[2]: mapType (type: int) Param[3]: texture (type: Texture2D) -Function 440: SetModelMeshMaterial() (3 input parameters) +Function 441: SetModelMeshMaterial() (3 input parameters) Name: SetModelMeshMaterial Return type: void Description: Set material for a mesh Param[1]: model (type: Model *) Param[2]: meshId (type: int) Param[3]: materialId (type: int) -Function 441: LoadModelAnimations() (2 input parameters) +Function 442: LoadModelAnimations() (2 input parameters) Name: LoadModelAnimations Return type: ModelAnimation * Description: Load model animations from file Param[1]: fileName (type: const char *) Param[2]: animCount (type: unsigned int *) -Function 442: UpdateModelAnimation() (3 input parameters) +Function 443: UpdateModelAnimation() (3 input parameters) Name: UpdateModelAnimation Return type: void Description: Update model animation pose Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) Param[3]: frame (type: int) -Function 443: UnloadModelAnimation() (1 input parameters) +Function 444: UnloadModelAnimation() (1 input parameters) Name: UnloadModelAnimation Return type: void Description: Unload animation data Param[1]: anim (type: ModelAnimation) -Function 444: UnloadModelAnimations() (2 input parameters) +Function 445: UnloadModelAnimations() (2 input parameters) Name: UnloadModelAnimations Return type: void Description: Unload animation array data Param[1]: animations (type: ModelAnimation *) Param[2]: count (type: unsigned int) -Function 445: IsModelAnimationValid() (2 input parameters) +Function 446: IsModelAnimationValid() (2 input parameters) Name: IsModelAnimationValid Return type: bool Description: Check model animation skeleton match Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) -Function 446: CheckCollisionSpheres() (4 input parameters) +Function 447: CheckCollisionSpheres() (4 input parameters) Name: CheckCollisionSpheres Return type: bool Description: Check collision between two spheres @@ -3807,40 +3816,40 @@ Function 446: CheckCollisionSpheres() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector3) Param[4]: radius2 (type: float) -Function 447: CheckCollisionBoxes() (2 input parameters) +Function 448: CheckCollisionBoxes() (2 input parameters) Name: CheckCollisionBoxes Return type: bool Description: Check collision between two bounding boxes Param[1]: box1 (type: BoundingBox) Param[2]: box2 (type: BoundingBox) -Function 448: CheckCollisionBoxSphere() (3 input parameters) +Function 449: CheckCollisionBoxSphere() (3 input parameters) Name: CheckCollisionBoxSphere Return type: bool Description: Check collision between box and sphere Param[1]: box (type: BoundingBox) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 449: GetRayCollisionSphere() (3 input parameters) +Function 450: GetRayCollisionSphere() (3 input parameters) Name: GetRayCollisionSphere Return type: RayCollision Description: Get collision info between ray and sphere Param[1]: ray (type: Ray) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 450: GetRayCollisionBox() (2 input parameters) +Function 451: GetRayCollisionBox() (2 input parameters) Name: GetRayCollisionBox Return type: RayCollision Description: Get collision info between ray and box Param[1]: ray (type: Ray) Param[2]: box (type: BoundingBox) -Function 451: GetRayCollisionMesh() (3 input parameters) +Function 452: GetRayCollisionMesh() (3 input parameters) Name: GetRayCollisionMesh Return type: RayCollision Description: Get collision info between ray and mesh Param[1]: ray (type: Ray) Param[2]: mesh (type: Mesh) Param[3]: transform (type: Matrix) -Function 452: GetRayCollisionTriangle() (4 input parameters) +Function 453: GetRayCollisionTriangle() (4 input parameters) Name: GetRayCollisionTriangle Return type: RayCollision Description: Get collision info between ray and triangle @@ -3848,7 +3857,7 @@ Function 452: GetRayCollisionTriangle() (4 input parameters) Param[2]: p1 (type: Vector3) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) -Function 453: GetRayCollisionQuad() (5 input parameters) +Function 454: GetRayCollisionQuad() (5 input parameters) Name: GetRayCollisionQuad Return type: RayCollision Description: Get collision info between ray and quad @@ -3857,143 +3866,143 @@ Function 453: GetRayCollisionQuad() (5 input parameters) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) Param[5]: p4 (type: Vector3) -Function 454: InitAudioDevice() (0 input parameters) +Function 455: InitAudioDevice() (0 input parameters) Name: InitAudioDevice Return type: void Description: Initialize audio device and context No input parameters -Function 455: CloseAudioDevice() (0 input parameters) +Function 456: CloseAudioDevice() (0 input parameters) Name: CloseAudioDevice Return type: void Description: Close the audio device and context No input parameters -Function 456: IsAudioDeviceReady() (0 input parameters) +Function 457: IsAudioDeviceReady() (0 input parameters) Name: IsAudioDeviceReady Return type: bool Description: Check if audio device has been initialized successfully No input parameters -Function 457: SetMasterVolume() (1 input parameters) +Function 458: SetMasterVolume() (1 input parameters) Name: SetMasterVolume Return type: void Description: Set master volume (listener) Param[1]: volume (type: float) -Function 458: LoadWave() (1 input parameters) +Function 459: LoadWave() (1 input parameters) Name: LoadWave Return type: Wave Description: Load wave data from file Param[1]: fileName (type: const char *) -Function 459: LoadWaveFromMemory() (3 input parameters) +Function 460: LoadWaveFromMemory() (3 input parameters) Name: LoadWaveFromMemory Return type: Wave Description: Load wave from memory buffer, fileType refers to extension: i.e. '.wav' Param[1]: fileType (type: const char *) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 460: IsWaveReady() (1 input parameters) +Function 461: IsWaveReady() (1 input parameters) Name: IsWaveReady Return type: bool Description: Checks if wave data is ready Param[1]: wave (type: Wave) -Function 461: LoadSound() (1 input parameters) +Function 462: LoadSound() (1 input parameters) Name: LoadSound Return type: Sound Description: Load sound from file Param[1]: fileName (type: const char *) -Function 462: LoadSoundFromWave() (1 input parameters) +Function 463: LoadSoundFromWave() (1 input parameters) Name: LoadSoundFromWave Return type: Sound Description: Load sound from wave data Param[1]: wave (type: Wave) -Function 463: IsSoundReady() (1 input parameters) +Function 464: IsSoundReady() (1 input parameters) Name: IsSoundReady Return type: bool Description: Checks if a sound is ready Param[1]: sound (type: Sound) -Function 464: UpdateSound() (3 input parameters) +Function 465: UpdateSound() (3 input parameters) Name: UpdateSound Return type: void Description: Update sound buffer with new data Param[1]: sound (type: Sound) Param[2]: data (type: const void *) Param[3]: sampleCount (type: int) -Function 465: UnloadWave() (1 input parameters) +Function 466: UnloadWave() (1 input parameters) Name: UnloadWave Return type: void Description: Unload wave data Param[1]: wave (type: Wave) -Function 466: UnloadSound() (1 input parameters) +Function 467: UnloadSound() (1 input parameters) Name: UnloadSound Return type: void Description: Unload sound Param[1]: sound (type: Sound) -Function 467: ExportWave() (2 input parameters) +Function 468: ExportWave() (2 input parameters) Name: ExportWave Return type: bool Description: Export wave data to file, returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 468: ExportWaveAsCode() (2 input parameters) +Function 469: ExportWaveAsCode() (2 input parameters) Name: ExportWaveAsCode Return type: bool Description: Export wave sample data to code (.h), returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 469: PlaySound() (1 input parameters) +Function 470: PlaySound() (1 input parameters) Name: PlaySound Return type: void Description: Play a sound Param[1]: sound (type: Sound) -Function 470: StopSound() (1 input parameters) +Function 471: StopSound() (1 input parameters) Name: StopSound Return type: void Description: Stop playing a sound Param[1]: sound (type: Sound) -Function 471: PauseSound() (1 input parameters) +Function 472: PauseSound() (1 input parameters) Name: PauseSound Return type: void Description: Pause a sound Param[1]: sound (type: Sound) -Function 472: ResumeSound() (1 input parameters) +Function 473: ResumeSound() (1 input parameters) Name: ResumeSound Return type: void Description: Resume a paused sound Param[1]: sound (type: Sound) -Function 473: IsSoundPlaying() (1 input parameters) +Function 474: IsSoundPlaying() (1 input parameters) Name: IsSoundPlaying Return type: bool Description: Check if a sound is currently playing Param[1]: sound (type: Sound) -Function 474: SetSoundVolume() (2 input parameters) +Function 475: SetSoundVolume() (2 input parameters) Name: SetSoundVolume Return type: void Description: Set volume for a sound (1.0 is max level) Param[1]: sound (type: Sound) Param[2]: volume (type: float) -Function 475: SetSoundPitch() (2 input parameters) +Function 476: SetSoundPitch() (2 input parameters) Name: SetSoundPitch Return type: void Description: Set pitch for a sound (1.0 is base level) Param[1]: sound (type: Sound) Param[2]: pitch (type: float) -Function 476: SetSoundPan() (2 input parameters) +Function 477: SetSoundPan() (2 input parameters) Name: SetSoundPan Return type: void Description: Set pan for a sound (0.5 is center) Param[1]: sound (type: Sound) Param[2]: pan (type: float) -Function 477: WaveCopy() (1 input parameters) +Function 478: WaveCopy() (1 input parameters) Name: WaveCopy Return type: Wave Description: Copy a wave to a new wave Param[1]: wave (type: Wave) -Function 478: WaveCrop() (3 input parameters) +Function 479: WaveCrop() (3 input parameters) Name: WaveCrop Return type: void Description: Crop a wave to defined samples range Param[1]: wave (type: Wave *) Param[2]: initSample (type: int) Param[3]: finalSample (type: int) -Function 479: WaveFormat() (4 input parameters) +Function 480: WaveFormat() (4 input parameters) Name: WaveFormat Return type: void Description: Convert wave data to desired format @@ -4001,203 +4010,203 @@ Function 479: WaveFormat() (4 input parameters) Param[2]: sampleRate (type: int) Param[3]: sampleSize (type: int) Param[4]: channels (type: int) -Function 480: LoadWaveSamples() (1 input parameters) +Function 481: LoadWaveSamples() (1 input parameters) Name: LoadWaveSamples Return type: float * Description: Load samples data from wave as a 32bit float data array Param[1]: wave (type: Wave) -Function 481: UnloadWaveSamples() (1 input parameters) +Function 482: UnloadWaveSamples() (1 input parameters) Name: UnloadWaveSamples Return type: void Description: Unload samples data loaded with LoadWaveSamples() Param[1]: samples (type: float *) -Function 482: LoadMusicStream() (1 input parameters) +Function 483: LoadMusicStream() (1 input parameters) Name: LoadMusicStream Return type: Music Description: Load music stream from file Param[1]: fileName (type: const char *) -Function 483: LoadMusicStreamFromMemory() (3 input parameters) +Function 484: LoadMusicStreamFromMemory() (3 input parameters) Name: LoadMusicStreamFromMemory Return type: Music Description: Load music stream from data Param[1]: fileType (type: const char *) Param[2]: data (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 484: IsMusicReady() (1 input parameters) +Function 485: IsMusicReady() (1 input parameters) Name: IsMusicReady Return type: bool Description: Checks if a music stream is ready Param[1]: music (type: Music) -Function 485: UnloadMusicStream() (1 input parameters) +Function 486: UnloadMusicStream() (1 input parameters) Name: UnloadMusicStream Return type: void Description: Unload music stream Param[1]: music (type: Music) -Function 486: PlayMusicStream() (1 input parameters) +Function 487: PlayMusicStream() (1 input parameters) Name: PlayMusicStream Return type: void Description: Start music playing Param[1]: music (type: Music) -Function 487: IsMusicStreamPlaying() (1 input parameters) +Function 488: IsMusicStreamPlaying() (1 input parameters) Name: IsMusicStreamPlaying Return type: bool Description: Check if music is playing Param[1]: music (type: Music) -Function 488: UpdateMusicStream() (1 input parameters) +Function 489: UpdateMusicStream() (1 input parameters) Name: UpdateMusicStream Return type: void Description: Updates buffers for music streaming Param[1]: music (type: Music) -Function 489: StopMusicStream() (1 input parameters) +Function 490: StopMusicStream() (1 input parameters) Name: StopMusicStream Return type: void Description: Stop music playing Param[1]: music (type: Music) -Function 490: PauseMusicStream() (1 input parameters) +Function 491: PauseMusicStream() (1 input parameters) Name: PauseMusicStream Return type: void Description: Pause music playing Param[1]: music (type: Music) -Function 491: ResumeMusicStream() (1 input parameters) +Function 492: ResumeMusicStream() (1 input parameters) Name: ResumeMusicStream Return type: void Description: Resume playing paused music Param[1]: music (type: Music) -Function 492: SeekMusicStream() (2 input parameters) +Function 493: SeekMusicStream() (2 input parameters) Name: SeekMusicStream Return type: void Description: Seek music to a position (in seconds) Param[1]: music (type: Music) Param[2]: position (type: float) -Function 493: SetMusicVolume() (2 input parameters) +Function 494: SetMusicVolume() (2 input parameters) Name: SetMusicVolume Return type: void Description: Set volume for music (1.0 is max level) Param[1]: music (type: Music) Param[2]: volume (type: float) -Function 494: SetMusicPitch() (2 input parameters) +Function 495: SetMusicPitch() (2 input parameters) Name: SetMusicPitch Return type: void Description: Set pitch for a music (1.0 is base level) Param[1]: music (type: Music) Param[2]: pitch (type: float) -Function 495: SetMusicPan() (2 input parameters) +Function 496: SetMusicPan() (2 input parameters) Name: SetMusicPan Return type: void Description: Set pan for a music (0.5 is center) Param[1]: music (type: Music) Param[2]: pan (type: float) -Function 496: GetMusicTimeLength() (1 input parameters) +Function 497: GetMusicTimeLength() (1 input parameters) Name: GetMusicTimeLength Return type: float Description: Get music time length (in seconds) Param[1]: music (type: Music) -Function 497: GetMusicTimePlayed() (1 input parameters) +Function 498: GetMusicTimePlayed() (1 input parameters) Name: GetMusicTimePlayed Return type: float Description: Get current music time played (in seconds) Param[1]: music (type: Music) -Function 498: LoadAudioStream() (3 input parameters) +Function 499: LoadAudioStream() (3 input parameters) Name: LoadAudioStream Return type: AudioStream Description: Load audio stream (to stream raw audio pcm data) Param[1]: sampleRate (type: unsigned int) Param[2]: sampleSize (type: unsigned int) Param[3]: channels (type: unsigned int) -Function 499: IsAudioStreamReady() (1 input parameters) +Function 500: IsAudioStreamReady() (1 input parameters) Name: IsAudioStreamReady Return type: bool Description: Checks if an audio stream is ready Param[1]: stream (type: AudioStream) -Function 500: UnloadAudioStream() (1 input parameters) +Function 501: UnloadAudioStream() (1 input parameters) Name: UnloadAudioStream Return type: void Description: Unload audio stream and free memory Param[1]: stream (type: AudioStream) -Function 501: UpdateAudioStream() (3 input parameters) +Function 502: UpdateAudioStream() (3 input parameters) Name: UpdateAudioStream Return type: void Description: Update audio stream buffers with data Param[1]: stream (type: AudioStream) Param[2]: data (type: const void *) Param[3]: frameCount (type: int) -Function 502: IsAudioStreamProcessed() (1 input parameters) +Function 503: IsAudioStreamProcessed() (1 input parameters) Name: IsAudioStreamProcessed Return type: bool Description: Check if any audio stream buffers requires refill Param[1]: stream (type: AudioStream) -Function 503: PlayAudioStream() (1 input parameters) +Function 504: PlayAudioStream() (1 input parameters) Name: PlayAudioStream Return type: void Description: Play audio stream Param[1]: stream (type: AudioStream) -Function 504: PauseAudioStream() (1 input parameters) +Function 505: PauseAudioStream() (1 input parameters) Name: PauseAudioStream Return type: void Description: Pause audio stream Param[1]: stream (type: AudioStream) -Function 505: ResumeAudioStream() (1 input parameters) +Function 506: ResumeAudioStream() (1 input parameters) Name: ResumeAudioStream Return type: void Description: Resume audio stream Param[1]: stream (type: AudioStream) -Function 506: IsAudioStreamPlaying() (1 input parameters) +Function 507: IsAudioStreamPlaying() (1 input parameters) Name: IsAudioStreamPlaying Return type: bool Description: Check if audio stream is playing Param[1]: stream (type: AudioStream) -Function 507: StopAudioStream() (1 input parameters) +Function 508: StopAudioStream() (1 input parameters) Name: StopAudioStream Return type: void Description: Stop audio stream Param[1]: stream (type: AudioStream) -Function 508: SetAudioStreamVolume() (2 input parameters) +Function 509: SetAudioStreamVolume() (2 input parameters) Name: SetAudioStreamVolume Return type: void Description: Set volume for audio stream (1.0 is max level) Param[1]: stream (type: AudioStream) Param[2]: volume (type: float) -Function 509: SetAudioStreamPitch() (2 input parameters) +Function 510: SetAudioStreamPitch() (2 input parameters) Name: SetAudioStreamPitch Return type: void Description: Set pitch for audio stream (1.0 is base level) Param[1]: stream (type: AudioStream) Param[2]: pitch (type: float) -Function 510: SetAudioStreamPan() (2 input parameters) +Function 511: SetAudioStreamPan() (2 input parameters) Name: SetAudioStreamPan Return type: void Description: Set pan for audio stream (0.5 is centered) Param[1]: stream (type: AudioStream) Param[2]: pan (type: float) -Function 511: SetAudioStreamBufferSizeDefault() (1 input parameters) +Function 512: SetAudioStreamBufferSizeDefault() (1 input parameters) Name: SetAudioStreamBufferSizeDefault Return type: void Description: Default size for new audio streams Param[1]: size (type: int) -Function 512: SetAudioStreamCallback() (2 input parameters) +Function 513: SetAudioStreamCallback() (2 input parameters) Name: SetAudioStreamCallback Return type: void Description: Audio thread callback to request new data Param[1]: stream (type: AudioStream) Param[2]: callback (type: AudioCallback) -Function 513: AttachAudioStreamProcessor() (2 input parameters) +Function 514: AttachAudioStreamProcessor() (2 input parameters) Name: AttachAudioStreamProcessor Return type: void Description: Attach audio stream processor to stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 514: DetachAudioStreamProcessor() (2 input parameters) +Function 515: DetachAudioStreamProcessor() (2 input parameters) Name: DetachAudioStreamProcessor Return type: void Description: Detach audio stream processor from stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 515: AttachAudioMixedProcessor() (1 input parameters) +Function 516: AttachAudioMixedProcessor() (1 input parameters) Name: AttachAudioMixedProcessor Return type: void Description: Attach audio stream processor to the entire audio pipeline Param[1]: processor (type: AudioCallback) -Function 516: DetachAudioMixedProcessor() (1 input parameters) +Function 517: DetachAudioMixedProcessor() (1 input parameters) Name: DetachAudioMixedProcessor Return type: void Description: Detach audio stream processor from the entire audio pipeline diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index 1c77f0266..85b3e3e4c 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -656,7 +656,7 @@ - + @@ -1549,7 +1549,7 @@ - + @@ -1563,6 +1563,13 @@ + + + + + + + diff --git a/projects/Geany/raylib.c.tags b/projects/Geany/raylib.c.tags index d41809983..4cc6d4605 100644 --- a/projects/Geany/raylib.c.tags +++ b/projects/Geany/raylib.c.tags @@ -213,6 +213,7 @@ ImageColorReplace|void|(Image *image, Color color, Color replace);| GenImageColor|Image|(int width, int height, Color color);| GenImageGradientLinear|Image|(int width, int height, int direction, Color start, Color end);| GenImageGradientRadial|Image|(int width, int height, float density, Color inner, Color outer);| +GenImageGradientSquare|Image|(int width, int height, float density, Color inner, Color outer);| GenImageChecked|Image|(int width, int height, int checksX, int checksY, Color col1, Color col2);| GenImageWhiteNoise|Image|(int width, int height, float factor);| GenImagePerlinNoise|Image|(int width, int height, int offsetX, int offsetY, float scale);| diff --git a/projects/Notepad++/raylib_npp_parser/raylib_npp.xml b/projects/Notepad++/raylib_npp_parser/raylib_npp.xml index 890501f2c..7e173a936 100644 --- a/projects/Notepad++/raylib_npp_parser/raylib_npp.xml +++ b/projects/Notepad++/raylib_npp_parser/raylib_npp.xml @@ -1396,6 +1396,15 @@ + + + + + + + + + diff --git a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h index 3f8450130..f20f065e3 100644 --- a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h +++ b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h @@ -319,6 +319,7 @@ RLAPI bool ExportImageAsCode(Image image, const char *fileName); RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color RLAPI Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end); // Generate image: linear gradient RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient +RLAPI Image GenImageGradientSquare(int width, int height, float density, Color inner, Color outer); // Generate image: square gradient RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise RLAPI Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale); // Generate image: perlin noise diff --git a/src/raylib.h b/src/raylib.h index 02d8e4445..703e70c21 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1241,6 +1241,7 @@ RLAPI bool ExportImageAsCode(Image image, const char *fileName); RLAPI Image GenImageColor(int width, int height, Color color); // Generate image: plain color RLAPI Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end); // Generate image: linear gradient, direction in degrees [0..360], 0=Vertical gradient RLAPI Image GenImageGradientRadial(int width, int height, float density, Color inner, Color outer); // Generate image: radial gradient +RLAPI Image GenImageGradientSquare(int width, int height, float density, Color inner, Color outer); // Generate image: square gradient RLAPI Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2); // Generate image: checked RLAPI Image GenImageWhiteNoise(int width, int height, float factor); // Generate image: white noise RLAPI Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float scale); // Generate image: perlin noise diff --git a/src/rtextures.c b/src/rtextures.c index d1cebe0d4..f6a747c0f 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -764,6 +764,55 @@ Image GenImageGradientRadial(int width, int height, float density, Color inner, return image; } +// Generate image: square gradient +Image GenImageGradientSquare(int width, int height, float density, Color inner, Color outer) +{ + Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color)); + + float centerX = (float)width/2.0f; + float centerY = (float)height/2.0f; + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + // Calculate the Manhattan distance from the center + float distX = fabsf(x - centerX); + float distY = fabsf(y - centerY); + + // Normalize the distances by the dimensions of the gradient rectangle + float normalizedDistX = distX / centerX; + float normalizedDistY = distY / centerY; + + // Calculate the total normalized Manhattan distance + float manhattanDist = fmax(normalizedDistX, normalizedDistY); + + // Subtract the density from the manhattanDist, then divide by (1 - density) + // This makes the gradient start from the center when density is 0, and from the edge when density is 1 + float factor = (manhattanDist - density) / (1.0f - density); + + // Clamp the factor between 0 and 1 + factor = fminf(fmaxf(factor, 0.f), 1.f); + + // Blend the colors based on the calculated factor + pixels[y*width + x].r = (int)((float)outer.r*factor + (float)inner.r*(1.0f - factor)); + pixels[y*width + x].g = (int)((float)outer.g*factor + (float)inner.g*(1.0f - factor)); + pixels[y*width + x].b = (int)((float)outer.b*factor + (float)inner.b*(1.0f - factor)); + pixels[y*width + x].a = (int)((float)outer.a*factor + (float)inner.a*(1.0f - factor)); + } + } + + Image image = { + .data = pixels, + .width = width, + .height = height, + .format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8, + .mipmaps = 1 + }; + + return image; +} + // Generate image: checked Image GenImageChecked(int width, int height, int checksX, int checksY, Color col1, Color col2) { From 2937f2010c0cd6c297c47711fe82436068199e30 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 22 May 2023 16:06:03 +0200 Subject: [PATCH 0092/1350] Review coding conventions --- src/rtextures.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index f6a747c0f..eeeebba82 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -692,27 +692,26 @@ Image GenImageGradientLinear(int width, int height, int direction, Color start, { Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color)); - float radianDirection = (float)(90 - direction) / 180.f * 3.14159f; + float radianDirection = (float)(90 - direction)/180.f*3.14159f; float cosDir = cos(radianDirection); float sinDir = sin(radianDirection); - int i, j; - for (i = 0; i < width; i++) + for (int i = 0; i < width; i++) { - for (j = 0; j < height; j++) + for (int j = 0; j < height; j++) { // Calculate the relative position of the pixel along the gradient direction - float pos = (i * cosDir + j * sinDir) / (width * cosDir + height * sinDir); + float pos = (i*cosDir + j*sinDir)/(width*cosDir + height*sinDir); float factor = pos; - factor = (factor > 1.f) ? 1.f : factor; // Clamp to [0,1] - factor = (factor < 0.f) ? 0.f : factor; // Clamp to [0,1] + factor = (factor > 1.0f)? 1.0f : factor; // Clamp to [0,1] + factor = (factor < 0.0f)? 0.0f : factor; // Clamp to [0,1] // Generate the color for this pixel - pixels[j * width + i].r = (int)((float)end.r*factor + (float)start.r*(1.f - factor)); - pixels[j * width + i].g = (int)((float)end.g*factor + (float)start.g*(1.f - factor)); - pixels[j * width + i].b = (int)((float)end.b*factor + (float)start.b*(1.f - factor)); - pixels[j * width + i].a = (int)((float)end.a*factor + (float)start.a*(1.f - factor)); + pixels[j*width + i].r = (int)((float)end.r*factor + (float)start.r*(1.0f - factor)); + pixels[j*width + i].g = (int)((float)end.g*factor + (float)start.g*(1.0f - factor)); + pixels[j*width + i].b = (int)((float)end.b*factor + (float)start.b*(1.0f - factor)); + pixels[j*width + i].a = (int)((float)end.a*factor + (float)start.a*(1.0f - factor)); } } @@ -789,10 +788,10 @@ Image GenImageGradientSquare(int width, int height, float density, Color inner, // Subtract the density from the manhattanDist, then divide by (1 - density) // This makes the gradient start from the center when density is 0, and from the edge when density is 1 - float factor = (manhattanDist - density) / (1.0f - density); + float factor = (manhattanDist - density)/(1.0f - density); // Clamp the factor between 0 and 1 - factor = fminf(fmaxf(factor, 0.f), 1.f); + factor = fminf(fmaxf(factor, 0.0f), 1.0f); // Blend the colors based on the calculated factor pixels[y*width + x].r = (int)((float)outer.r*factor + (float)inner.r*(1.0f - factor)); From bf69b3805601627a509b92600c9b70efcddfedeb Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 22 May 2023 16:08:14 +0200 Subject: [PATCH 0093/1350] Added security check to file reading (memory allocations) --- src/utils.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/utils.c b/src/utils.c index 01ca235fa..aa2bfc40e 100644 --- a/src/utils.c +++ b/src/utils.c @@ -207,12 +207,16 @@ unsigned char *LoadFileData(const char *fileName, unsigned int *bytesRead) { data = (unsigned char *)RL_MALLOC(size*sizeof(unsigned char)); - // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements] - unsigned int count = (unsigned int)fread(data, sizeof(unsigned char), size, file); - *bytesRead = count; + if (data != NULL) + { + // NOTE: fread() returns number of read elements instead of bytes, so we read [1 byte, size elements] + unsigned int count = (unsigned int)fread(data, sizeof(unsigned char), size, file); + *bytesRead = count; - if (count != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded", fileName); - else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName); + if (count != size) TRACELOG(LOG_WARNING, "FILEIO: [%s] File partially loaded", fileName); + else TRACELOG(LOG_INFO, "FILEIO: [%s] File loaded successfully", fileName); + } + else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to allocated memory for file reading", fileName); } else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read file", fileName); @@ -344,16 +348,21 @@ char *LoadFileText(const char *fileName) if (size > 0) { text = (char *)RL_MALLOC((size + 1)*sizeof(char)); - unsigned int count = (unsigned int)fread(text, sizeof(char), size, file); + + if (text != NULL) + { + unsigned int count = (unsigned int)fread(text, sizeof(char), size, file); - // WARNING: \r\n is converted to \n on reading, so, - // read bytes count gets reduced by the number of lines - if (count < size) text = RL_REALLOC(text, count + 1); + // WARNING: \r\n is converted to \n on reading, so, + // read bytes count gets reduced by the number of lines + if (count < size) text = RL_REALLOC(text, count + 1); - // Zero-terminate the string - text[count] = '\0'; + // Zero-terminate the string + text[count] = '\0'; - TRACELOG(LOG_INFO, "FILEIO: [%s] Text file loaded successfully", fileName); + TRACELOG(LOG_INFO, "FILEIO: [%s] Text file loaded successfully", fileName); + } + else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to allocated memory for file reading", fileName); } else TRACELOG(LOG_WARNING, "FILEIO: [%s] Failed to read text file", fileName); From e465ed0850106a3574d01363874ae49ef7e478c9 Mon Sep 17 00:00:00 2001 From: Dane Madsen Date: Wed, 24 May 2023 17:22:51 +1000 Subject: [PATCH 0094/1350] Added ImageRotate (#3078) * Added ImageRotate * Quick rename of the example * Update ImageRotate by changing doubles to floats and checking code convention * Update API --- examples/Makefile | 1 + examples/textures/textures_image_rotate.c | 79 +++ examples/textures/textures_image_rotate.png | Bin 0 -> 24060 bytes parser/output/raylib_api.json | 15 + parser/output/raylib_api.lua | 9 + parser/output/raylib_api.txt | 498 +++++++++--------- parser/output/raylib_api.xml | 6 +- projects/Geany/raylib.c.tags | 1 + .../raylib_npp_parser/raylib_npp.xml | 6 + .../raylib_npp_parser/raylib_to_parse.h | 1 + src/raylib.h | 1 + src/rtextures.c | 59 +++ 12 files changed, 429 insertions(+), 247 deletions(-) create mode 100644 examples/textures/textures_image_rotate.c create mode 100644 examples/textures/textures_image_rotate.png diff --git a/examples/Makefile b/examples/Makefile index cc9ef2395..c2ea35bf4 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -453,6 +453,7 @@ TEXTURES = \ textures/textures_image_generation \ textures/textures_image_loading \ textures/textures_image_processing \ + textures/textures_image_rotate \ textures/textures_image_text \ textures/textures_to_image \ textures/textures_raw_data \ diff --git a/examples/textures/textures_image_rotate.c b/examples/textures/textures_image_rotate.c new file mode 100644 index 000000000..a590e1e0a --- /dev/null +++ b/examples/textures/textures_image_rotate.c @@ -0,0 +1,79 @@ +/******************************************************************************************* +* +* raylib [textures] example - Image Rotation +* +* Example originally created with raylib 1.0, last time updated with raylib 1.0 +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#define NUM_TEXTURES 3 + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [textures] example - texture rotation"); + + // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required) + Image image45 = LoadImage("resources/raylib_logo.png"); + Image image90 = LoadImage("resources/raylib_logo.png"); + Image imageNeg90 = LoadImage("resources/raylib_logo.png"); + + ImageRotate(&image45, 45); + ImageRotate(&image90, 90); + ImageRotate(&imageNeg90, -90); + + Texture2D textures[NUM_TEXTURES] = { 0 }; + + textures[0] = LoadTextureFromImage(image45); + textures[1] = LoadTextureFromImage(image90); + textures[2] = LoadTextureFromImage(imageNeg90); + + int currentTexture = 0; + //--------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) || IsKeyPressed(KEY_RIGHT)) + { + currentTexture = (currentTexture + 1)%NUM_TEXTURES; // Cycle between the textures + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawTexture(textures[currentTexture], screenWidth/2 - textures[currentTexture].width/2, screenHeight/2 - textures[currentTexture].height/2, WHITE); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + for (int i = 0; i < NUM_TEXTURES; i++) UnloadTexture(textures[i]); + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/examples/textures/textures_image_rotate.png b/examples/textures/textures_image_rotate.png new file mode 100644 index 0000000000000000000000000000000000000000..64bbc54e496f4587e2609a58cea3ceee637123d1 GIT binary patch literal 24060 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 literal 0 HcmV?d00001 diff --git a/parser/output/raylib_api.json b/parser/output/raylib_api.json index 4438ab0da..b42f03a83 100644 --- a/parser/output/raylib_api.json +++ b/parser/output/raylib_api.json @@ -6757,6 +6757,21 @@ } ] }, + { + "name": "ImageRotate", + "description": "Rotate image by input angle in degrees (-359 to 359) ", + "returnType": "void", + "params": [ + { + "type": "Image *", + "name": "image" + }, + { + "type": "int", + "name": "degrees" + } + ] + }, { "name": "ImageRotateCW", "description": "Rotate image clockwise 90deg", diff --git a/parser/output/raylib_api.lua b/parser/output/raylib_api.lua index 79b3a9283..ed11ac08f 100644 --- a/parser/output/raylib_api.lua +++ b/parser/output/raylib_api.lua @@ -5266,6 +5266,15 @@ return { {type = "Image *", name = "image"} } }, + { + name = "ImageRotate", + description = "Rotate image by input angle in degrees (-359 to 359) ", + returnType = "void", + params = { + {type = "Image *", name = "image"}, + {type = "int", name = "degrees"} + } + }, { name = "ImageRotateCW", description = "Rotate image clockwise 90deg", diff --git a/parser/output/raylib_api.txt b/parser/output/raylib_api.txt index c53cb77ec..0d0ccccf1 100644 --- a/parser/output/raylib_api.txt +++ b/parser/output/raylib_api.txt @@ -964,7 +964,7 @@ Callback 006: AudioCallback() (2 input parameters) Param[1]: bufferData (type: void *) Param[2]: frames (type: unsigned int) -Functions found: 517 +Functions found: 518 Function 001: InitWindow() (3 input parameters) Name: InitWindow @@ -2606,93 +2606,99 @@ Function 272: ImageFlipHorizontal() (1 input parameters) Return type: void Description: Flip image horizontally Param[1]: image (type: Image *) -Function 273: ImageRotateCW() (1 input parameters) +Function 273: ImageRotate() (2 input parameters) + Name: ImageRotate + Return type: void + Description: Rotate image by input angle in degrees (-359 to 359) + Param[1]: image (type: Image *) + Param[2]: degrees (type: int) +Function 274: ImageRotateCW() (1 input parameters) Name: ImageRotateCW Return type: void Description: Rotate image clockwise 90deg Param[1]: image (type: Image *) -Function 274: ImageRotateCCW() (1 input parameters) +Function 275: ImageRotateCCW() (1 input parameters) Name: ImageRotateCCW Return type: void Description: Rotate image counter-clockwise 90deg Param[1]: image (type: Image *) -Function 275: ImageColorTint() (2 input parameters) +Function 276: ImageColorTint() (2 input parameters) Name: ImageColorTint Return type: void Description: Modify image color: tint Param[1]: image (type: Image *) Param[2]: color (type: Color) -Function 276: ImageColorInvert() (1 input parameters) +Function 277: ImageColorInvert() (1 input parameters) Name: ImageColorInvert Return type: void Description: Modify image color: invert Param[1]: image (type: Image *) -Function 277: ImageColorGrayscale() (1 input parameters) +Function 278: ImageColorGrayscale() (1 input parameters) Name: ImageColorGrayscale Return type: void Description: Modify image color: grayscale Param[1]: image (type: Image *) -Function 278: ImageColorContrast() (2 input parameters) +Function 279: ImageColorContrast() (2 input parameters) Name: ImageColorContrast Return type: void Description: Modify image color: contrast (-100 to 100) Param[1]: image (type: Image *) Param[2]: contrast (type: float) -Function 279: ImageColorBrightness() (2 input parameters) +Function 280: ImageColorBrightness() (2 input parameters) Name: ImageColorBrightness Return type: void Description: Modify image color: brightness (-255 to 255) Param[1]: image (type: Image *) Param[2]: brightness (type: int) -Function 280: ImageColorReplace() (3 input parameters) +Function 281: ImageColorReplace() (3 input parameters) Name: ImageColorReplace Return type: void Description: Modify image color: replace color Param[1]: image (type: Image *) Param[2]: color (type: Color) Param[3]: replace (type: Color) -Function 281: LoadImageColors() (1 input parameters) +Function 282: LoadImageColors() (1 input parameters) Name: LoadImageColors Return type: Color * Description: Load color data from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) -Function 282: LoadImagePalette() (3 input parameters) +Function 283: LoadImagePalette() (3 input parameters) Name: LoadImagePalette Return type: Color * Description: Load colors palette from image as a Color array (RGBA - 32bit) Param[1]: image (type: Image) Param[2]: maxPaletteSize (type: int) Param[3]: colorCount (type: int *) -Function 283: UnloadImageColors() (1 input parameters) +Function 284: UnloadImageColors() (1 input parameters) Name: UnloadImageColors Return type: void Description: Unload color data loaded with LoadImageColors() Param[1]: colors (type: Color *) -Function 284: UnloadImagePalette() (1 input parameters) +Function 285: UnloadImagePalette() (1 input parameters) Name: UnloadImagePalette Return type: void Description: Unload colors palette loaded with LoadImagePalette() Param[1]: colors (type: Color *) -Function 285: GetImageAlphaBorder() (2 input parameters) +Function 286: GetImageAlphaBorder() (2 input parameters) Name: GetImageAlphaBorder Return type: Rectangle Description: Get image alpha border rectangle Param[1]: image (type: Image) Param[2]: threshold (type: float) -Function 286: GetImageColor() (3 input parameters) +Function 287: GetImageColor() (3 input parameters) Name: GetImageColor Return type: Color Description: Get image pixel color at (x, y) position Param[1]: image (type: Image) Param[2]: x (type: int) Param[3]: y (type: int) -Function 287: ImageClearBackground() (2 input parameters) +Function 288: ImageClearBackground() (2 input parameters) Name: ImageClearBackground Return type: void Description: Clear image background with given color Param[1]: dst (type: Image *) Param[2]: color (type: Color) -Function 288: ImageDrawPixel() (4 input parameters) +Function 289: ImageDrawPixel() (4 input parameters) Name: ImageDrawPixel Return type: void Description: Draw pixel within an image @@ -2700,14 +2706,14 @@ Function 288: ImageDrawPixel() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: color (type: Color) -Function 289: ImageDrawPixelV() (3 input parameters) +Function 290: ImageDrawPixelV() (3 input parameters) Name: ImageDrawPixelV Return type: void Description: Draw pixel within an image (Vector version) Param[1]: dst (type: Image *) Param[2]: position (type: Vector2) Param[3]: color (type: Color) -Function 290: ImageDrawLine() (6 input parameters) +Function 291: ImageDrawLine() (6 input parameters) Name: ImageDrawLine Return type: void Description: Draw line within an image @@ -2717,7 +2723,7 @@ Function 290: ImageDrawLine() (6 input parameters) Param[4]: endPosX (type: int) Param[5]: endPosY (type: int) Param[6]: color (type: Color) -Function 291: ImageDrawLineV() (4 input parameters) +Function 292: ImageDrawLineV() (4 input parameters) Name: ImageDrawLineV Return type: void Description: Draw line within an image (Vector version) @@ -2725,7 +2731,7 @@ Function 291: ImageDrawLineV() (4 input parameters) Param[2]: start (type: Vector2) Param[3]: end (type: Vector2) Param[4]: color (type: Color) -Function 292: ImageDrawCircle() (5 input parameters) +Function 293: ImageDrawCircle() (5 input parameters) Name: ImageDrawCircle Return type: void Description: Draw a filled circle within an image @@ -2734,7 +2740,7 @@ Function 292: ImageDrawCircle() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 293: ImageDrawCircleV() (4 input parameters) +Function 294: ImageDrawCircleV() (4 input parameters) Name: ImageDrawCircleV Return type: void Description: Draw a filled circle within an image (Vector version) @@ -2742,7 +2748,7 @@ Function 293: ImageDrawCircleV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 294: ImageDrawCircleLines() (5 input parameters) +Function 295: ImageDrawCircleLines() (5 input parameters) Name: ImageDrawCircleLines Return type: void Description: Draw circle outline within an image @@ -2751,7 +2757,7 @@ Function 294: ImageDrawCircleLines() (5 input parameters) Param[3]: centerY (type: int) Param[4]: radius (type: int) Param[5]: color (type: Color) -Function 295: ImageDrawCircleLinesV() (4 input parameters) +Function 296: ImageDrawCircleLinesV() (4 input parameters) Name: ImageDrawCircleLinesV Return type: void Description: Draw circle outline within an image (Vector version) @@ -2759,7 +2765,7 @@ Function 295: ImageDrawCircleLinesV() (4 input parameters) Param[2]: center (type: Vector2) Param[3]: radius (type: int) Param[4]: color (type: Color) -Function 296: ImageDrawRectangle() (6 input parameters) +Function 297: ImageDrawRectangle() (6 input parameters) Name: ImageDrawRectangle Return type: void Description: Draw rectangle within an image @@ -2769,7 +2775,7 @@ Function 296: ImageDrawRectangle() (6 input parameters) Param[4]: width (type: int) Param[5]: height (type: int) Param[6]: color (type: Color) -Function 297: ImageDrawRectangleV() (4 input parameters) +Function 298: ImageDrawRectangleV() (4 input parameters) Name: ImageDrawRectangleV Return type: void Description: Draw rectangle within an image (Vector version) @@ -2777,14 +2783,14 @@ Function 297: ImageDrawRectangleV() (4 input parameters) Param[2]: position (type: Vector2) Param[3]: size (type: Vector2) Param[4]: color (type: Color) -Function 298: ImageDrawRectangleRec() (3 input parameters) +Function 299: ImageDrawRectangleRec() (3 input parameters) Name: ImageDrawRectangleRec Return type: void Description: Draw rectangle within an image Param[1]: dst (type: Image *) Param[2]: rec (type: Rectangle) Param[3]: color (type: Color) -Function 299: ImageDrawRectangleLines() (4 input parameters) +Function 300: ImageDrawRectangleLines() (4 input parameters) Name: ImageDrawRectangleLines Return type: void Description: Draw rectangle lines within an image @@ -2792,7 +2798,7 @@ Function 299: ImageDrawRectangleLines() (4 input parameters) Param[2]: rec (type: Rectangle) Param[3]: thick (type: int) Param[4]: color (type: Color) -Function 300: ImageDraw() (5 input parameters) +Function 301: ImageDraw() (5 input parameters) Name: ImageDraw Return type: void Description: Draw a source image within a destination image (tint applied to source) @@ -2801,7 +2807,7 @@ Function 300: ImageDraw() (5 input parameters) Param[3]: srcRec (type: Rectangle) Param[4]: dstRec (type: Rectangle) Param[5]: tint (type: Color) -Function 301: ImageDrawText() (6 input parameters) +Function 302: ImageDrawText() (6 input parameters) Name: ImageDrawText Return type: void Description: Draw text (using default font) within an image (destination) @@ -2811,7 +2817,7 @@ Function 301: ImageDrawText() (6 input parameters) Param[4]: posY (type: int) Param[5]: fontSize (type: int) Param[6]: color (type: Color) -Function 302: ImageDrawTextEx() (7 input parameters) +Function 303: ImageDrawTextEx() (7 input parameters) Name: ImageDrawTextEx Return type: void Description: Draw text (custom sprite font) within an image (destination) @@ -2822,79 +2828,79 @@ Function 302: ImageDrawTextEx() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 303: LoadTexture() (1 input parameters) +Function 304: LoadTexture() (1 input parameters) Name: LoadTexture Return type: Texture2D Description: Load texture from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 304: LoadTextureFromImage() (1 input parameters) +Function 305: LoadTextureFromImage() (1 input parameters) Name: LoadTextureFromImage Return type: Texture2D Description: Load texture from image data Param[1]: image (type: Image) -Function 305: LoadTextureCubemap() (2 input parameters) +Function 306: LoadTextureCubemap() (2 input parameters) Name: LoadTextureCubemap Return type: TextureCubemap Description: Load cubemap from image, multiple image cubemap layouts supported Param[1]: image (type: Image) Param[2]: layout (type: int) -Function 306: LoadRenderTexture() (2 input parameters) +Function 307: LoadRenderTexture() (2 input parameters) Name: LoadRenderTexture Return type: RenderTexture2D Description: Load texture for rendering (framebuffer) Param[1]: width (type: int) Param[2]: height (type: int) -Function 307: IsTextureReady() (1 input parameters) +Function 308: IsTextureReady() (1 input parameters) Name: IsTextureReady Return type: bool Description: Check if a texture is ready Param[1]: texture (type: Texture2D) -Function 308: UnloadTexture() (1 input parameters) +Function 309: UnloadTexture() (1 input parameters) Name: UnloadTexture Return type: void Description: Unload texture from GPU memory (VRAM) Param[1]: texture (type: Texture2D) -Function 309: IsRenderTextureReady() (1 input parameters) +Function 310: IsRenderTextureReady() (1 input parameters) Name: IsRenderTextureReady Return type: bool Description: Check if a render texture is ready Param[1]: target (type: RenderTexture2D) -Function 310: UnloadRenderTexture() (1 input parameters) +Function 311: UnloadRenderTexture() (1 input parameters) Name: UnloadRenderTexture Return type: void Description: Unload render texture from GPU memory (VRAM) Param[1]: target (type: RenderTexture2D) -Function 311: UpdateTexture() (2 input parameters) +Function 312: UpdateTexture() (2 input parameters) Name: UpdateTexture Return type: void Description: Update GPU texture with new data Param[1]: texture (type: Texture2D) Param[2]: pixels (type: const void *) -Function 312: UpdateTextureRec() (3 input parameters) +Function 313: UpdateTextureRec() (3 input parameters) Name: UpdateTextureRec Return type: void Description: Update GPU texture rectangle with new data Param[1]: texture (type: Texture2D) Param[2]: rec (type: Rectangle) Param[3]: pixels (type: const void *) -Function 313: GenTextureMipmaps() (1 input parameters) +Function 314: GenTextureMipmaps() (1 input parameters) Name: GenTextureMipmaps Return type: void Description: Generate GPU mipmaps for a texture Param[1]: texture (type: Texture2D *) -Function 314: SetTextureFilter() (2 input parameters) +Function 315: SetTextureFilter() (2 input parameters) Name: SetTextureFilter Return type: void Description: Set texture scaling filter mode Param[1]: texture (type: Texture2D) Param[2]: filter (type: int) -Function 315: SetTextureWrap() (2 input parameters) +Function 316: SetTextureWrap() (2 input parameters) Name: SetTextureWrap Return type: void Description: Set texture wrapping mode Param[1]: texture (type: Texture2D) Param[2]: wrap (type: int) -Function 316: DrawTexture() (4 input parameters) +Function 317: DrawTexture() (4 input parameters) Name: DrawTexture Return type: void Description: Draw a Texture2D @@ -2902,14 +2908,14 @@ Function 316: DrawTexture() (4 input parameters) Param[2]: posX (type: int) Param[3]: posY (type: int) Param[4]: tint (type: Color) -Function 317: DrawTextureV() (3 input parameters) +Function 318: DrawTextureV() (3 input parameters) Name: DrawTextureV Return type: void Description: Draw a Texture2D with position defined as Vector2 Param[1]: texture (type: Texture2D) Param[2]: position (type: Vector2) Param[3]: tint (type: Color) -Function 318: DrawTextureEx() (5 input parameters) +Function 319: DrawTextureEx() (5 input parameters) Name: DrawTextureEx Return type: void Description: Draw a Texture2D with extended parameters @@ -2918,7 +2924,7 @@ Function 318: DrawTextureEx() (5 input parameters) Param[3]: rotation (type: float) Param[4]: scale (type: float) Param[5]: tint (type: Color) -Function 319: DrawTextureRec() (4 input parameters) +Function 320: DrawTextureRec() (4 input parameters) Name: DrawTextureRec Return type: void Description: Draw a part of a texture defined by a rectangle @@ -2926,7 +2932,7 @@ Function 319: DrawTextureRec() (4 input parameters) Param[2]: source (type: Rectangle) Param[3]: position (type: Vector2) Param[4]: tint (type: Color) -Function 320: DrawTexturePro() (6 input parameters) +Function 321: DrawTexturePro() (6 input parameters) Name: DrawTexturePro Return type: void Description: Draw a part of a texture defined by a rectangle with 'pro' parameters @@ -2936,7 +2942,7 @@ Function 320: DrawTexturePro() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 321: DrawTextureNPatch() (6 input parameters) +Function 322: DrawTextureNPatch() (6 input parameters) Name: DrawTextureNPatch Return type: void Description: Draws a texture (or part of it) that stretches or shrinks nicely @@ -2946,106 +2952,106 @@ Function 321: DrawTextureNPatch() (6 input parameters) Param[4]: origin (type: Vector2) Param[5]: rotation (type: float) Param[6]: tint (type: Color) -Function 322: Fade() (2 input parameters) +Function 323: Fade() (2 input parameters) Name: Fade Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 323: ColorToInt() (1 input parameters) +Function 324: ColorToInt() (1 input parameters) Name: ColorToInt Return type: int Description: Get hexadecimal value for a Color Param[1]: color (type: Color) -Function 324: ColorNormalize() (1 input parameters) +Function 325: ColorNormalize() (1 input parameters) Name: ColorNormalize Return type: Vector4 Description: Get Color normalized as float [0..1] Param[1]: color (type: Color) -Function 325: ColorFromNormalized() (1 input parameters) +Function 326: ColorFromNormalized() (1 input parameters) Name: ColorFromNormalized Return type: Color Description: Get Color from normalized values [0..1] Param[1]: normalized (type: Vector4) -Function 326: ColorToHSV() (1 input parameters) +Function 327: ColorToHSV() (1 input parameters) Name: ColorToHSV Return type: Vector3 Description: Get HSV values for a Color, hue [0..360], saturation/value [0..1] Param[1]: color (type: Color) -Function 327: ColorFromHSV() (3 input parameters) +Function 328: ColorFromHSV() (3 input parameters) Name: ColorFromHSV Return type: Color Description: Get a Color from HSV values, hue [0..360], saturation/value [0..1] Param[1]: hue (type: float) Param[2]: saturation (type: float) Param[3]: value (type: float) -Function 328: ColorTint() (2 input parameters) +Function 329: ColorTint() (2 input parameters) Name: ColorTint Return type: Color Description: Get color multiplied with another color Param[1]: color (type: Color) Param[2]: tint (type: Color) -Function 329: ColorBrightness() (2 input parameters) +Function 330: ColorBrightness() (2 input parameters) Name: ColorBrightness Return type: Color Description: Get color with brightness correction, brightness factor goes from -1.0f to 1.0f Param[1]: color (type: Color) Param[2]: factor (type: float) -Function 330: ColorContrast() (2 input parameters) +Function 331: ColorContrast() (2 input parameters) Name: ColorContrast Return type: Color Description: Get color with contrast correction, contrast values between -1.0f and 1.0f Param[1]: color (type: Color) Param[2]: contrast (type: float) -Function 331: ColorAlpha() (2 input parameters) +Function 332: ColorAlpha() (2 input parameters) Name: ColorAlpha Return type: Color Description: Get color with alpha applied, alpha goes from 0.0f to 1.0f Param[1]: color (type: Color) Param[2]: alpha (type: float) -Function 332: ColorAlphaBlend() (3 input parameters) +Function 333: ColorAlphaBlend() (3 input parameters) Name: ColorAlphaBlend Return type: Color Description: Get src alpha-blended into dst color with tint Param[1]: dst (type: Color) Param[2]: src (type: Color) Param[3]: tint (type: Color) -Function 333: GetColor() (1 input parameters) +Function 334: GetColor() (1 input parameters) Name: GetColor Return type: Color Description: Get Color structure from hexadecimal value Param[1]: hexValue (type: unsigned int) -Function 334: GetPixelColor() (2 input parameters) +Function 335: GetPixelColor() (2 input parameters) Name: GetPixelColor Return type: Color Description: Get Color from a source pixel pointer of certain format Param[1]: srcPtr (type: void *) Param[2]: format (type: int) -Function 335: SetPixelColor() (3 input parameters) +Function 336: SetPixelColor() (3 input parameters) Name: SetPixelColor Return type: void Description: Set color formatted into destination pixel pointer Param[1]: dstPtr (type: void *) Param[2]: color (type: Color) Param[3]: format (type: int) -Function 336: GetPixelDataSize() (3 input parameters) +Function 337: GetPixelDataSize() (3 input parameters) Name: GetPixelDataSize Return type: int Description: Get pixel data size in bytes for certain format Param[1]: width (type: int) Param[2]: height (type: int) Param[3]: format (type: int) -Function 337: GetFontDefault() (0 input parameters) +Function 338: GetFontDefault() (0 input parameters) Name: GetFontDefault Return type: Font Description: Get the default Font No input parameters -Function 338: LoadFont() (1 input parameters) +Function 339: LoadFont() (1 input parameters) Name: LoadFont Return type: Font Description: Load font from file into GPU memory (VRAM) Param[1]: fileName (type: const char *) -Function 339: LoadFontEx() (4 input parameters) +Function 340: LoadFontEx() (4 input parameters) Name: LoadFontEx Return type: Font Description: Load font from file with extended parameters, use NULL for fontChars and 0 for glyphCount to load the default character set @@ -3053,14 +3059,14 @@ Function 339: LoadFontEx() (4 input parameters) Param[2]: fontSize (type: int) Param[3]: fontChars (type: int *) Param[4]: glyphCount (type: int) -Function 340: LoadFontFromImage() (3 input parameters) +Function 341: LoadFontFromImage() (3 input parameters) Name: LoadFontFromImage Return type: Font Description: Load font from Image (XNA style) Param[1]: image (type: Image) Param[2]: key (type: Color) Param[3]: firstChar (type: int) -Function 341: LoadFontFromMemory() (6 input parameters) +Function 342: LoadFontFromMemory() (6 input parameters) Name: LoadFontFromMemory Return type: Font Description: Load font from memory buffer, fileType refers to extension: i.e. '.ttf' @@ -3070,12 +3076,12 @@ Function 341: LoadFontFromMemory() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: fontChars (type: int *) Param[6]: glyphCount (type: int) -Function 342: IsFontReady() (1 input parameters) +Function 343: IsFontReady() (1 input parameters) Name: IsFontReady Return type: bool Description: Check if a font is ready Param[1]: font (type: Font) -Function 343: LoadFontData() (6 input parameters) +Function 344: LoadFontData() (6 input parameters) Name: LoadFontData Return type: GlyphInfo * Description: Load font data for further use @@ -3085,7 +3091,7 @@ Function 343: LoadFontData() (6 input parameters) Param[4]: fontChars (type: int *) Param[5]: glyphCount (type: int) Param[6]: type (type: int) -Function 344: GenImageFontAtlas() (6 input parameters) +Function 345: GenImageFontAtlas() (6 input parameters) Name: GenImageFontAtlas Return type: Image Description: Generate image font atlas using chars info @@ -3095,30 +3101,30 @@ Function 344: GenImageFontAtlas() (6 input parameters) Param[4]: fontSize (type: int) Param[5]: padding (type: int) Param[6]: packMethod (type: int) -Function 345: UnloadFontData() (2 input parameters) +Function 346: UnloadFontData() (2 input parameters) Name: UnloadFontData Return type: void Description: Unload font chars info data (RAM) Param[1]: chars (type: GlyphInfo *) Param[2]: glyphCount (type: int) -Function 346: UnloadFont() (1 input parameters) +Function 347: UnloadFont() (1 input parameters) Name: UnloadFont Return type: void Description: Unload font from GPU memory (VRAM) Param[1]: font (type: Font) -Function 347: ExportFontAsCode() (2 input parameters) +Function 348: ExportFontAsCode() (2 input parameters) Name: ExportFontAsCode Return type: bool Description: Export font as code file, returns true on success Param[1]: font (type: Font) Param[2]: fileName (type: const char *) -Function 348: DrawFPS() (2 input parameters) +Function 349: DrawFPS() (2 input parameters) Name: DrawFPS Return type: void Description: Draw current FPS Param[1]: posX (type: int) Param[2]: posY (type: int) -Function 349: DrawText() (5 input parameters) +Function 350: DrawText() (5 input parameters) Name: DrawText Return type: void Description: Draw text (using default font) @@ -3127,7 +3133,7 @@ Function 349: DrawText() (5 input parameters) Param[3]: posY (type: int) Param[4]: fontSize (type: int) Param[5]: color (type: Color) -Function 350: DrawTextEx() (6 input parameters) +Function 351: DrawTextEx() (6 input parameters) Name: DrawTextEx Return type: void Description: Draw text using font and additional parameters @@ -3137,7 +3143,7 @@ Function 350: DrawTextEx() (6 input parameters) Param[4]: fontSize (type: float) Param[5]: spacing (type: float) Param[6]: tint (type: Color) -Function 351: DrawTextPro() (8 input parameters) +Function 352: DrawTextPro() (8 input parameters) Name: DrawTextPro Return type: void Description: Draw text using Font and pro parameters (rotation) @@ -3149,7 +3155,7 @@ Function 351: DrawTextPro() (8 input parameters) Param[6]: fontSize (type: float) Param[7]: spacing (type: float) Param[8]: tint (type: Color) -Function 352: DrawTextCodepoint() (5 input parameters) +Function 353: DrawTextCodepoint() (5 input parameters) Name: DrawTextCodepoint Return type: void Description: Draw one character (codepoint) @@ -3158,7 +3164,7 @@ Function 352: DrawTextCodepoint() (5 input parameters) Param[3]: position (type: Vector2) Param[4]: fontSize (type: float) Param[5]: tint (type: Color) -Function 353: DrawTextCodepoints() (7 input parameters) +Function 354: DrawTextCodepoints() (7 input parameters) Name: DrawTextCodepoints Return type: void Description: Draw multiple character (codepoint) @@ -3169,13 +3175,13 @@ Function 353: DrawTextCodepoints() (7 input parameters) Param[5]: fontSize (type: float) Param[6]: spacing (type: float) Param[7]: tint (type: Color) -Function 354: MeasureText() (2 input parameters) +Function 355: MeasureText() (2 input parameters) Name: MeasureText Return type: int Description: Measure string width for default font Param[1]: text (type: const char *) Param[2]: fontSize (type: int) -Function 355: MeasureTextEx() (4 input parameters) +Function 356: MeasureTextEx() (4 input parameters) Name: MeasureTextEx Return type: Vector2 Description: Measure string size for Font @@ -3183,180 +3189,180 @@ Function 355: MeasureTextEx() (4 input parameters) Param[2]: text (type: const char *) Param[3]: fontSize (type: float) Param[4]: spacing (type: float) -Function 356: GetGlyphIndex() (2 input parameters) +Function 357: GetGlyphIndex() (2 input parameters) Name: GetGlyphIndex Return type: int Description: Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 357: GetGlyphInfo() (2 input parameters) +Function 358: GetGlyphInfo() (2 input parameters) Name: GetGlyphInfo Return type: GlyphInfo Description: Get glyph font info data for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 358: GetGlyphAtlasRec() (2 input parameters) +Function 359: GetGlyphAtlasRec() (2 input parameters) Name: GetGlyphAtlasRec Return type: Rectangle Description: Get glyph rectangle in font atlas for a codepoint (unicode character), fallback to '?' if not found Param[1]: font (type: Font) Param[2]: codepoint (type: int) -Function 359: LoadUTF8() (2 input parameters) +Function 360: LoadUTF8() (2 input parameters) Name: LoadUTF8 Return type: char * Description: Load UTF-8 text encoded from codepoints array Param[1]: codepoints (type: const int *) Param[2]: length (type: int) -Function 360: UnloadUTF8() (1 input parameters) +Function 361: UnloadUTF8() (1 input parameters) Name: UnloadUTF8 Return type: void Description: Unload UTF-8 text encoded from codepoints array Param[1]: text (type: char *) -Function 361: LoadCodepoints() (2 input parameters) +Function 362: LoadCodepoints() (2 input parameters) Name: LoadCodepoints Return type: int * Description: Load all codepoints from a UTF-8 text string, codepoints count returned by parameter Param[1]: text (type: const char *) Param[2]: count (type: int *) -Function 362: UnloadCodepoints() (1 input parameters) +Function 363: UnloadCodepoints() (1 input parameters) Name: UnloadCodepoints Return type: void Description: Unload codepoints data from memory Param[1]: codepoints (type: int *) -Function 363: GetCodepointCount() (1 input parameters) +Function 364: GetCodepointCount() (1 input parameters) Name: GetCodepointCount Return type: int Description: Get total number of codepoints in a UTF-8 encoded string Param[1]: text (type: const char *) -Function 364: GetCodepoint() (2 input parameters) +Function 365: GetCodepoint() (2 input parameters) Name: GetCodepoint Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 365: GetCodepointNext() (2 input parameters) +Function 366: GetCodepointNext() (2 input parameters) Name: GetCodepointNext Return type: int Description: Get next codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 366: GetCodepointPrevious() (2 input parameters) +Function 367: GetCodepointPrevious() (2 input parameters) Name: GetCodepointPrevious Return type: int Description: Get previous codepoint in a UTF-8 encoded string, 0x3f('?') is returned on failure Param[1]: text (type: const char *) Param[2]: codepointSize (type: int *) -Function 367: CodepointToUTF8() (2 input parameters) +Function 368: CodepointToUTF8() (2 input parameters) Name: CodepointToUTF8 Return type: const char * Description: Encode one codepoint into UTF-8 byte array (array length returned as parameter) Param[1]: codepoint (type: int) Param[2]: utf8Size (type: int *) -Function 368: TextCopy() (2 input parameters) +Function 369: TextCopy() (2 input parameters) Name: TextCopy Return type: int Description: Copy one string to another, returns bytes copied Param[1]: dst (type: char *) Param[2]: src (type: const char *) -Function 369: TextIsEqual() (2 input parameters) +Function 370: TextIsEqual() (2 input parameters) Name: TextIsEqual Return type: bool Description: Check if two text string are equal Param[1]: text1 (type: const char *) Param[2]: text2 (type: const char *) -Function 370: TextLength() (1 input parameters) +Function 371: TextLength() (1 input parameters) Name: TextLength Return type: unsigned int Description: Get text length, checks for '\0' ending Param[1]: text (type: const char *) -Function 371: TextFormat() (2 input parameters) +Function 372: TextFormat() (2 input parameters) Name: TextFormat Return type: const char * Description: Text formatting with variables (sprintf() style) Param[1]: text (type: const char *) Param[2]: args (type: ...) -Function 372: TextSubtext() (3 input parameters) +Function 373: TextSubtext() (3 input parameters) Name: TextSubtext Return type: const char * Description: Get a piece of a text string Param[1]: text (type: const char *) Param[2]: position (type: int) Param[3]: length (type: int) -Function 373: TextReplace() (3 input parameters) +Function 374: TextReplace() (3 input parameters) Name: TextReplace Return type: char * Description: Replace text string (WARNING: memory must be freed!) Param[1]: text (type: char *) Param[2]: replace (type: const char *) Param[3]: by (type: const char *) -Function 374: TextInsert() (3 input parameters) +Function 375: TextInsert() (3 input parameters) Name: TextInsert Return type: char * Description: Insert text in a position (WARNING: memory must be freed!) Param[1]: text (type: const char *) Param[2]: insert (type: const char *) Param[3]: position (type: int) -Function 375: TextJoin() (3 input parameters) +Function 376: TextJoin() (3 input parameters) Name: TextJoin Return type: const char * Description: Join text strings with delimiter Param[1]: textList (type: const char **) Param[2]: count (type: int) Param[3]: delimiter (type: const char *) -Function 376: TextSplit() (3 input parameters) +Function 377: TextSplit() (3 input parameters) Name: TextSplit Return type: const char ** Description: Split text into multiple strings Param[1]: text (type: const char *) Param[2]: delimiter (type: char) Param[3]: count (type: int *) -Function 377: TextAppend() (3 input parameters) +Function 378: TextAppend() (3 input parameters) Name: TextAppend Return type: void Description: Append text at specific position and move cursor! Param[1]: text (type: char *) Param[2]: append (type: const char *) Param[3]: position (type: int *) -Function 378: TextFindIndex() (2 input parameters) +Function 379: TextFindIndex() (2 input parameters) Name: TextFindIndex Return type: int Description: Find first text occurrence within a string Param[1]: text (type: const char *) Param[2]: find (type: const char *) -Function 379: TextToUpper() (1 input parameters) +Function 380: TextToUpper() (1 input parameters) Name: TextToUpper Return type: const char * Description: Get upper case version of provided string Param[1]: text (type: const char *) -Function 380: TextToLower() (1 input parameters) +Function 381: TextToLower() (1 input parameters) Name: TextToLower Return type: const char * Description: Get lower case version of provided string Param[1]: text (type: const char *) -Function 381: TextToPascal() (1 input parameters) +Function 382: TextToPascal() (1 input parameters) Name: TextToPascal Return type: const char * Description: Get Pascal case notation version of provided string Param[1]: text (type: const char *) -Function 382: TextToInteger() (1 input parameters) +Function 383: TextToInteger() (1 input parameters) Name: TextToInteger Return type: int Description: Get integer value from text (negative values not supported) Param[1]: text (type: const char *) -Function 383: DrawLine3D() (3 input parameters) +Function 384: DrawLine3D() (3 input parameters) Name: DrawLine3D Return type: void Description: Draw a line in 3D world space Param[1]: startPos (type: Vector3) Param[2]: endPos (type: Vector3) Param[3]: color (type: Color) -Function 384: DrawPoint3D() (2 input parameters) +Function 385: DrawPoint3D() (2 input parameters) Name: DrawPoint3D Return type: void Description: Draw a point in 3D space, actually a small line Param[1]: position (type: Vector3) Param[2]: color (type: Color) -Function 385: DrawCircle3D() (5 input parameters) +Function 386: DrawCircle3D() (5 input parameters) Name: DrawCircle3D Return type: void Description: Draw a circle in 3D world space @@ -3365,7 +3371,7 @@ Function 385: DrawCircle3D() (5 input parameters) Param[3]: rotationAxis (type: Vector3) Param[4]: rotationAngle (type: float) Param[5]: color (type: Color) -Function 386: DrawTriangle3D() (4 input parameters) +Function 387: DrawTriangle3D() (4 input parameters) Name: DrawTriangle3D Return type: void Description: Draw a color-filled triangle (vertex in counter-clockwise order!) @@ -3373,14 +3379,14 @@ Function 386: DrawTriangle3D() (4 input parameters) Param[2]: v2 (type: Vector3) Param[3]: v3 (type: Vector3) Param[4]: color (type: Color) -Function 387: DrawTriangleStrip3D() (3 input parameters) +Function 388: DrawTriangleStrip3D() (3 input parameters) Name: DrawTriangleStrip3D Return type: void Description: Draw a triangle strip defined by points Param[1]: points (type: Vector3 *) Param[2]: pointCount (type: int) Param[3]: color (type: Color) -Function 388: DrawCube() (5 input parameters) +Function 389: DrawCube() (5 input parameters) Name: DrawCube Return type: void Description: Draw cube @@ -3389,14 +3395,14 @@ Function 388: DrawCube() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 389: DrawCubeV() (3 input parameters) +Function 390: DrawCubeV() (3 input parameters) Name: DrawCubeV Return type: void Description: Draw cube (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 390: DrawCubeWires() (5 input parameters) +Function 391: DrawCubeWires() (5 input parameters) Name: DrawCubeWires Return type: void Description: Draw cube wires @@ -3405,21 +3411,21 @@ Function 390: DrawCubeWires() (5 input parameters) Param[3]: height (type: float) Param[4]: length (type: float) Param[5]: color (type: Color) -Function 391: DrawCubeWiresV() (3 input parameters) +Function 392: DrawCubeWiresV() (3 input parameters) Name: DrawCubeWiresV Return type: void Description: Draw cube wires (Vector version) Param[1]: position (type: Vector3) Param[2]: size (type: Vector3) Param[3]: color (type: Color) -Function 392: DrawSphere() (3 input parameters) +Function 393: DrawSphere() (3 input parameters) Name: DrawSphere Return type: void Description: Draw sphere Param[1]: centerPos (type: Vector3) Param[2]: radius (type: float) Param[3]: color (type: Color) -Function 393: DrawSphereEx() (5 input parameters) +Function 394: DrawSphereEx() (5 input parameters) Name: DrawSphereEx Return type: void Description: Draw sphere with extended parameters @@ -3428,7 +3434,7 @@ Function 393: DrawSphereEx() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 394: DrawSphereWires() (5 input parameters) +Function 395: DrawSphereWires() (5 input parameters) Name: DrawSphereWires Return type: void Description: Draw sphere wires @@ -3437,7 +3443,7 @@ Function 394: DrawSphereWires() (5 input parameters) Param[3]: rings (type: int) Param[4]: slices (type: int) Param[5]: color (type: Color) -Function 395: DrawCylinder() (6 input parameters) +Function 396: DrawCylinder() (6 input parameters) Name: DrawCylinder Return type: void Description: Draw a cylinder/cone @@ -3447,7 +3453,7 @@ Function 395: DrawCylinder() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 396: DrawCylinderEx() (6 input parameters) +Function 397: DrawCylinderEx() (6 input parameters) Name: DrawCylinderEx Return type: void Description: Draw a cylinder with base at startPos and top at endPos @@ -3457,7 +3463,7 @@ Function 396: DrawCylinderEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 397: DrawCylinderWires() (6 input parameters) +Function 398: DrawCylinderWires() (6 input parameters) Name: DrawCylinderWires Return type: void Description: Draw a cylinder/cone wires @@ -3467,7 +3473,7 @@ Function 397: DrawCylinderWires() (6 input parameters) Param[4]: height (type: float) Param[5]: slices (type: int) Param[6]: color (type: Color) -Function 398: DrawCylinderWiresEx() (6 input parameters) +Function 399: DrawCylinderWiresEx() (6 input parameters) Name: DrawCylinderWiresEx Return type: void Description: Draw a cylinder wires with base at startPos and top at endPos @@ -3477,7 +3483,7 @@ Function 398: DrawCylinderWiresEx() (6 input parameters) Param[4]: endRadius (type: float) Param[5]: sides (type: int) Param[6]: color (type: Color) -Function 399: DrawCapsule() (6 input parameters) +Function 400: DrawCapsule() (6 input parameters) Name: DrawCapsule Return type: void Description: Draw a capsule with the center of its sphere caps at startPos and endPos @@ -3487,7 +3493,7 @@ Function 399: DrawCapsule() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 400: DrawCapsuleWires() (6 input parameters) +Function 401: DrawCapsuleWires() (6 input parameters) Name: DrawCapsuleWires Return type: void Description: Draw capsule wireframe with the center of its sphere caps at startPos and endPos @@ -3497,51 +3503,51 @@ Function 400: DrawCapsuleWires() (6 input parameters) Param[4]: slices (type: int) Param[5]: rings (type: int) Param[6]: color (type: Color) -Function 401: DrawPlane() (3 input parameters) +Function 402: DrawPlane() (3 input parameters) Name: DrawPlane Return type: void Description: Draw a plane XZ Param[1]: centerPos (type: Vector3) Param[2]: size (type: Vector2) Param[3]: color (type: Color) -Function 402: DrawRay() (2 input parameters) +Function 403: DrawRay() (2 input parameters) Name: DrawRay Return type: void Description: Draw a ray line Param[1]: ray (type: Ray) Param[2]: color (type: Color) -Function 403: DrawGrid() (2 input parameters) +Function 404: DrawGrid() (2 input parameters) Name: DrawGrid Return type: void Description: Draw a grid (centered at (0, 0, 0)) Param[1]: slices (type: int) Param[2]: spacing (type: float) -Function 404: LoadModel() (1 input parameters) +Function 405: LoadModel() (1 input parameters) Name: LoadModel Return type: Model Description: Load model from files (meshes and materials) Param[1]: fileName (type: const char *) -Function 405: LoadModelFromMesh() (1 input parameters) +Function 406: LoadModelFromMesh() (1 input parameters) Name: LoadModelFromMesh Return type: Model Description: Load model from generated mesh (default material) Param[1]: mesh (type: Mesh) -Function 406: IsModelReady() (1 input parameters) +Function 407: IsModelReady() (1 input parameters) Name: IsModelReady Return type: bool Description: Check if a model is ready Param[1]: model (type: Model) -Function 407: UnloadModel() (1 input parameters) +Function 408: UnloadModel() (1 input parameters) Name: UnloadModel Return type: void Description: Unload model (including meshes) from memory (RAM and/or VRAM) Param[1]: model (type: Model) -Function 408: GetModelBoundingBox() (1 input parameters) +Function 409: GetModelBoundingBox() (1 input parameters) Name: GetModelBoundingBox Return type: BoundingBox Description: Compute model bounding box limits (considers all meshes) Param[1]: model (type: Model) -Function 409: DrawModel() (4 input parameters) +Function 410: DrawModel() (4 input parameters) Name: DrawModel Return type: void Description: Draw a model (with texture if set) @@ -3549,7 +3555,7 @@ Function 409: DrawModel() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 410: DrawModelEx() (6 input parameters) +Function 411: DrawModelEx() (6 input parameters) Name: DrawModelEx Return type: void Description: Draw a model with extended parameters @@ -3559,7 +3565,7 @@ Function 410: DrawModelEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 411: DrawModelWires() (4 input parameters) +Function 412: DrawModelWires() (4 input parameters) Name: DrawModelWires Return type: void Description: Draw a model wires (with texture if set) @@ -3567,7 +3573,7 @@ Function 411: DrawModelWires() (4 input parameters) Param[2]: position (type: Vector3) Param[3]: scale (type: float) Param[4]: tint (type: Color) -Function 412: DrawModelWiresEx() (6 input parameters) +Function 413: DrawModelWiresEx() (6 input parameters) Name: DrawModelWiresEx Return type: void Description: Draw a model wires (with texture if set) with extended parameters @@ -3577,13 +3583,13 @@ Function 412: DrawModelWiresEx() (6 input parameters) Param[4]: rotationAngle (type: float) Param[5]: scale (type: Vector3) Param[6]: tint (type: Color) -Function 413: DrawBoundingBox() (2 input parameters) +Function 414: DrawBoundingBox() (2 input parameters) Name: DrawBoundingBox Return type: void Description: Draw bounding box (wires) Param[1]: box (type: BoundingBox) Param[2]: color (type: Color) -Function 414: DrawBillboard() (5 input parameters) +Function 415: DrawBillboard() (5 input parameters) Name: DrawBillboard Return type: void Description: Draw a billboard texture @@ -3592,7 +3598,7 @@ Function 414: DrawBillboard() (5 input parameters) Param[3]: position (type: Vector3) Param[4]: size (type: float) Param[5]: tint (type: Color) -Function 415: DrawBillboardRec() (6 input parameters) +Function 416: DrawBillboardRec() (6 input parameters) Name: DrawBillboardRec Return type: void Description: Draw a billboard texture defined by source @@ -3602,7 +3608,7 @@ Function 415: DrawBillboardRec() (6 input parameters) Param[4]: position (type: Vector3) Param[5]: size (type: Vector2) Param[6]: tint (type: Color) -Function 416: DrawBillboardPro() (9 input parameters) +Function 417: DrawBillboardPro() (9 input parameters) Name: DrawBillboardPro Return type: void Description: Draw a billboard texture defined by source and rotation @@ -3615,13 +3621,13 @@ Function 416: DrawBillboardPro() (9 input parameters) Param[7]: origin (type: Vector2) Param[8]: rotation (type: float) Param[9]: tint (type: Color) -Function 417: UploadMesh() (2 input parameters) +Function 418: UploadMesh() (2 input parameters) Name: UploadMesh Return type: void Description: Upload mesh vertex data in GPU and provide VAO/VBO ids Param[1]: mesh (type: Mesh *) Param[2]: dynamic (type: bool) -Function 418: UpdateMeshBuffer() (5 input parameters) +Function 419: UpdateMeshBuffer() (5 input parameters) Name: UpdateMeshBuffer Return type: void Description: Update mesh vertex data in GPU for a specific buffer index @@ -3630,19 +3636,19 @@ Function 418: UpdateMeshBuffer() (5 input parameters) Param[3]: data (type: const void *) Param[4]: dataSize (type: int) Param[5]: offset (type: int) -Function 419: UnloadMesh() (1 input parameters) +Function 420: UnloadMesh() (1 input parameters) Name: UnloadMesh Return type: void Description: Unload mesh data from CPU and GPU Param[1]: mesh (type: Mesh) -Function 420: DrawMesh() (3 input parameters) +Function 421: DrawMesh() (3 input parameters) Name: DrawMesh Return type: void Description: Draw a 3d mesh with material and transform Param[1]: mesh (type: Mesh) Param[2]: material (type: Material) Param[3]: transform (type: Matrix) -Function 421: DrawMeshInstanced() (4 input parameters) +Function 422: DrawMeshInstanced() (4 input parameters) Name: DrawMeshInstanced Return type: void Description: Draw multiple mesh instances with material and different transforms @@ -3650,29 +3656,29 @@ Function 421: DrawMeshInstanced() (4 input parameters) Param[2]: material (type: Material) Param[3]: transforms (type: const Matrix *) Param[4]: instances (type: int) -Function 422: ExportMesh() (2 input parameters) +Function 423: ExportMesh() (2 input parameters) Name: ExportMesh Return type: bool Description: Export mesh data to file, returns true on success Param[1]: mesh (type: Mesh) Param[2]: fileName (type: const char *) -Function 423: GetMeshBoundingBox() (1 input parameters) +Function 424: GetMeshBoundingBox() (1 input parameters) Name: GetMeshBoundingBox Return type: BoundingBox Description: Compute mesh bounding box limits Param[1]: mesh (type: Mesh) -Function 424: GenMeshTangents() (1 input parameters) +Function 425: GenMeshTangents() (1 input parameters) Name: GenMeshTangents Return type: void Description: Compute mesh tangents Param[1]: mesh (type: Mesh *) -Function 425: GenMeshPoly() (2 input parameters) +Function 426: GenMeshPoly() (2 input parameters) Name: GenMeshPoly Return type: Mesh Description: Generate polygonal mesh Param[1]: sides (type: int) Param[2]: radius (type: float) -Function 426: GenMeshPlane() (4 input parameters) +Function 427: GenMeshPlane() (4 input parameters) Name: GenMeshPlane Return type: Mesh Description: Generate plane mesh (with subdivisions) @@ -3680,42 +3686,42 @@ Function 426: GenMeshPlane() (4 input parameters) Param[2]: length (type: float) Param[3]: resX (type: int) Param[4]: resZ (type: int) -Function 427: GenMeshCube() (3 input parameters) +Function 428: GenMeshCube() (3 input parameters) Name: GenMeshCube Return type: Mesh Description: Generate cuboid mesh Param[1]: width (type: float) Param[2]: height (type: float) Param[3]: length (type: float) -Function 428: GenMeshSphere() (3 input parameters) +Function 429: GenMeshSphere() (3 input parameters) Name: GenMeshSphere Return type: Mesh Description: Generate sphere mesh (standard sphere) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 429: GenMeshHemiSphere() (3 input parameters) +Function 430: GenMeshHemiSphere() (3 input parameters) Name: GenMeshHemiSphere Return type: Mesh Description: Generate half-sphere mesh (no bottom cap) Param[1]: radius (type: float) Param[2]: rings (type: int) Param[3]: slices (type: int) -Function 430: GenMeshCylinder() (3 input parameters) +Function 431: GenMeshCylinder() (3 input parameters) Name: GenMeshCylinder Return type: Mesh Description: Generate cylinder mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 431: GenMeshCone() (3 input parameters) +Function 432: GenMeshCone() (3 input parameters) Name: GenMeshCone Return type: Mesh Description: Generate cone/pyramid mesh Param[1]: radius (type: float) Param[2]: height (type: float) Param[3]: slices (type: int) -Function 432: GenMeshTorus() (4 input parameters) +Function 433: GenMeshTorus() (4 input parameters) Name: GenMeshTorus Return type: Mesh Description: Generate torus mesh @@ -3723,7 +3729,7 @@ Function 432: GenMeshTorus() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 433: GenMeshKnot() (4 input parameters) +Function 434: GenMeshKnot() (4 input parameters) Name: GenMeshKnot Return type: Mesh Description: Generate trefoil knot mesh @@ -3731,84 +3737,84 @@ Function 433: GenMeshKnot() (4 input parameters) Param[2]: size (type: float) Param[3]: radSeg (type: int) Param[4]: sides (type: int) -Function 434: GenMeshHeightmap() (2 input parameters) +Function 435: GenMeshHeightmap() (2 input parameters) Name: GenMeshHeightmap Return type: Mesh Description: Generate heightmap mesh from image data Param[1]: heightmap (type: Image) Param[2]: size (type: Vector3) -Function 435: GenMeshCubicmap() (2 input parameters) +Function 436: GenMeshCubicmap() (2 input parameters) Name: GenMeshCubicmap Return type: Mesh Description: Generate cubes-based map mesh from image data Param[1]: cubicmap (type: Image) Param[2]: cubeSize (type: Vector3) -Function 436: LoadMaterials() (2 input parameters) +Function 437: LoadMaterials() (2 input parameters) Name: LoadMaterials Return type: Material * Description: Load materials from model file Param[1]: fileName (type: const char *) Param[2]: materialCount (type: int *) -Function 437: LoadMaterialDefault() (0 input parameters) +Function 438: LoadMaterialDefault() (0 input parameters) Name: LoadMaterialDefault Return type: Material Description: Load default material (Supports: DIFFUSE, SPECULAR, NORMAL maps) No input parameters -Function 438: IsMaterialReady() (1 input parameters) +Function 439: IsMaterialReady() (1 input parameters) Name: IsMaterialReady Return type: bool Description: Check if a material is ready Param[1]: material (type: Material) -Function 439: UnloadMaterial() (1 input parameters) +Function 440: UnloadMaterial() (1 input parameters) Name: UnloadMaterial Return type: void Description: Unload material from GPU memory (VRAM) Param[1]: material (type: Material) -Function 440: SetMaterialTexture() (3 input parameters) +Function 441: SetMaterialTexture() (3 input parameters) Name: SetMaterialTexture Return type: void Description: Set texture for a material map type (MATERIAL_MAP_DIFFUSE, MATERIAL_MAP_SPECULAR...) Param[1]: material (type: Material *) Param[2]: mapType (type: int) Param[3]: texture (type: Texture2D) -Function 441: SetModelMeshMaterial() (3 input parameters) +Function 442: SetModelMeshMaterial() (3 input parameters) Name: SetModelMeshMaterial Return type: void Description: Set material for a mesh Param[1]: model (type: Model *) Param[2]: meshId (type: int) Param[3]: materialId (type: int) -Function 442: LoadModelAnimations() (2 input parameters) +Function 443: LoadModelAnimations() (2 input parameters) Name: LoadModelAnimations Return type: ModelAnimation * Description: Load model animations from file Param[1]: fileName (type: const char *) Param[2]: animCount (type: unsigned int *) -Function 443: UpdateModelAnimation() (3 input parameters) +Function 444: UpdateModelAnimation() (3 input parameters) Name: UpdateModelAnimation Return type: void Description: Update model animation pose Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) Param[3]: frame (type: int) -Function 444: UnloadModelAnimation() (1 input parameters) +Function 445: UnloadModelAnimation() (1 input parameters) Name: UnloadModelAnimation Return type: void Description: Unload animation data Param[1]: anim (type: ModelAnimation) -Function 445: UnloadModelAnimations() (2 input parameters) +Function 446: UnloadModelAnimations() (2 input parameters) Name: UnloadModelAnimations Return type: void Description: Unload animation array data Param[1]: animations (type: ModelAnimation *) Param[2]: count (type: unsigned int) -Function 446: IsModelAnimationValid() (2 input parameters) +Function 447: IsModelAnimationValid() (2 input parameters) Name: IsModelAnimationValid Return type: bool Description: Check model animation skeleton match Param[1]: model (type: Model) Param[2]: anim (type: ModelAnimation) -Function 447: CheckCollisionSpheres() (4 input parameters) +Function 448: CheckCollisionSpheres() (4 input parameters) Name: CheckCollisionSpheres Return type: bool Description: Check collision between two spheres @@ -3816,40 +3822,40 @@ Function 447: CheckCollisionSpheres() (4 input parameters) Param[2]: radius1 (type: float) Param[3]: center2 (type: Vector3) Param[4]: radius2 (type: float) -Function 448: CheckCollisionBoxes() (2 input parameters) +Function 449: CheckCollisionBoxes() (2 input parameters) Name: CheckCollisionBoxes Return type: bool Description: Check collision between two bounding boxes Param[1]: box1 (type: BoundingBox) Param[2]: box2 (type: BoundingBox) -Function 449: CheckCollisionBoxSphere() (3 input parameters) +Function 450: CheckCollisionBoxSphere() (3 input parameters) Name: CheckCollisionBoxSphere Return type: bool Description: Check collision between box and sphere Param[1]: box (type: BoundingBox) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 450: GetRayCollisionSphere() (3 input parameters) +Function 451: GetRayCollisionSphere() (3 input parameters) Name: GetRayCollisionSphere Return type: RayCollision Description: Get collision info between ray and sphere Param[1]: ray (type: Ray) Param[2]: center (type: Vector3) Param[3]: radius (type: float) -Function 451: GetRayCollisionBox() (2 input parameters) +Function 452: GetRayCollisionBox() (2 input parameters) Name: GetRayCollisionBox Return type: RayCollision Description: Get collision info between ray and box Param[1]: ray (type: Ray) Param[2]: box (type: BoundingBox) -Function 452: GetRayCollisionMesh() (3 input parameters) +Function 453: GetRayCollisionMesh() (3 input parameters) Name: GetRayCollisionMesh Return type: RayCollision Description: Get collision info between ray and mesh Param[1]: ray (type: Ray) Param[2]: mesh (type: Mesh) Param[3]: transform (type: Matrix) -Function 453: GetRayCollisionTriangle() (4 input parameters) +Function 454: GetRayCollisionTriangle() (4 input parameters) Name: GetRayCollisionTriangle Return type: RayCollision Description: Get collision info between ray and triangle @@ -3857,7 +3863,7 @@ Function 453: GetRayCollisionTriangle() (4 input parameters) Param[2]: p1 (type: Vector3) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) -Function 454: GetRayCollisionQuad() (5 input parameters) +Function 455: GetRayCollisionQuad() (5 input parameters) Name: GetRayCollisionQuad Return type: RayCollision Description: Get collision info between ray and quad @@ -3866,143 +3872,143 @@ Function 454: GetRayCollisionQuad() (5 input parameters) Param[3]: p2 (type: Vector3) Param[4]: p3 (type: Vector3) Param[5]: p4 (type: Vector3) -Function 455: InitAudioDevice() (0 input parameters) +Function 456: InitAudioDevice() (0 input parameters) Name: InitAudioDevice Return type: void Description: Initialize audio device and context No input parameters -Function 456: CloseAudioDevice() (0 input parameters) +Function 457: CloseAudioDevice() (0 input parameters) Name: CloseAudioDevice Return type: void Description: Close the audio device and context No input parameters -Function 457: IsAudioDeviceReady() (0 input parameters) +Function 458: IsAudioDeviceReady() (0 input parameters) Name: IsAudioDeviceReady Return type: bool Description: Check if audio device has been initialized successfully No input parameters -Function 458: SetMasterVolume() (1 input parameters) +Function 459: SetMasterVolume() (1 input parameters) Name: SetMasterVolume Return type: void Description: Set master volume (listener) Param[1]: volume (type: float) -Function 459: LoadWave() (1 input parameters) +Function 460: LoadWave() (1 input parameters) Name: LoadWave Return type: Wave Description: Load wave data from file Param[1]: fileName (type: const char *) -Function 460: LoadWaveFromMemory() (3 input parameters) +Function 461: LoadWaveFromMemory() (3 input parameters) Name: LoadWaveFromMemory Return type: Wave Description: Load wave from memory buffer, fileType refers to extension: i.e. '.wav' Param[1]: fileType (type: const char *) Param[2]: fileData (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 461: IsWaveReady() (1 input parameters) +Function 462: IsWaveReady() (1 input parameters) Name: IsWaveReady Return type: bool Description: Checks if wave data is ready Param[1]: wave (type: Wave) -Function 462: LoadSound() (1 input parameters) +Function 463: LoadSound() (1 input parameters) Name: LoadSound Return type: Sound Description: Load sound from file Param[1]: fileName (type: const char *) -Function 463: LoadSoundFromWave() (1 input parameters) +Function 464: LoadSoundFromWave() (1 input parameters) Name: LoadSoundFromWave Return type: Sound Description: Load sound from wave data Param[1]: wave (type: Wave) -Function 464: IsSoundReady() (1 input parameters) +Function 465: IsSoundReady() (1 input parameters) Name: IsSoundReady Return type: bool Description: Checks if a sound is ready Param[1]: sound (type: Sound) -Function 465: UpdateSound() (3 input parameters) +Function 466: UpdateSound() (3 input parameters) Name: UpdateSound Return type: void Description: Update sound buffer with new data Param[1]: sound (type: Sound) Param[2]: data (type: const void *) Param[3]: sampleCount (type: int) -Function 466: UnloadWave() (1 input parameters) +Function 467: UnloadWave() (1 input parameters) Name: UnloadWave Return type: void Description: Unload wave data Param[1]: wave (type: Wave) -Function 467: UnloadSound() (1 input parameters) +Function 468: UnloadSound() (1 input parameters) Name: UnloadSound Return type: void Description: Unload sound Param[1]: sound (type: Sound) -Function 468: ExportWave() (2 input parameters) +Function 469: ExportWave() (2 input parameters) Name: ExportWave Return type: bool Description: Export wave data to file, returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 469: ExportWaveAsCode() (2 input parameters) +Function 470: ExportWaveAsCode() (2 input parameters) Name: ExportWaveAsCode Return type: bool Description: Export wave sample data to code (.h), returns true on success Param[1]: wave (type: Wave) Param[2]: fileName (type: const char *) -Function 470: PlaySound() (1 input parameters) +Function 471: PlaySound() (1 input parameters) Name: PlaySound Return type: void Description: Play a sound Param[1]: sound (type: Sound) -Function 471: StopSound() (1 input parameters) +Function 472: StopSound() (1 input parameters) Name: StopSound Return type: void Description: Stop playing a sound Param[1]: sound (type: Sound) -Function 472: PauseSound() (1 input parameters) +Function 473: PauseSound() (1 input parameters) Name: PauseSound Return type: void Description: Pause a sound Param[1]: sound (type: Sound) -Function 473: ResumeSound() (1 input parameters) +Function 474: ResumeSound() (1 input parameters) Name: ResumeSound Return type: void Description: Resume a paused sound Param[1]: sound (type: Sound) -Function 474: IsSoundPlaying() (1 input parameters) +Function 475: IsSoundPlaying() (1 input parameters) Name: IsSoundPlaying Return type: bool Description: Check if a sound is currently playing Param[1]: sound (type: Sound) -Function 475: SetSoundVolume() (2 input parameters) +Function 476: SetSoundVolume() (2 input parameters) Name: SetSoundVolume Return type: void Description: Set volume for a sound (1.0 is max level) Param[1]: sound (type: Sound) Param[2]: volume (type: float) -Function 476: SetSoundPitch() (2 input parameters) +Function 477: SetSoundPitch() (2 input parameters) Name: SetSoundPitch Return type: void Description: Set pitch for a sound (1.0 is base level) Param[1]: sound (type: Sound) Param[2]: pitch (type: float) -Function 477: SetSoundPan() (2 input parameters) +Function 478: SetSoundPan() (2 input parameters) Name: SetSoundPan Return type: void Description: Set pan for a sound (0.5 is center) Param[1]: sound (type: Sound) Param[2]: pan (type: float) -Function 478: WaveCopy() (1 input parameters) +Function 479: WaveCopy() (1 input parameters) Name: WaveCopy Return type: Wave Description: Copy a wave to a new wave Param[1]: wave (type: Wave) -Function 479: WaveCrop() (3 input parameters) +Function 480: WaveCrop() (3 input parameters) Name: WaveCrop Return type: void Description: Crop a wave to defined samples range Param[1]: wave (type: Wave *) Param[2]: initSample (type: int) Param[3]: finalSample (type: int) -Function 480: WaveFormat() (4 input parameters) +Function 481: WaveFormat() (4 input parameters) Name: WaveFormat Return type: void Description: Convert wave data to desired format @@ -4010,203 +4016,203 @@ Function 480: WaveFormat() (4 input parameters) Param[2]: sampleRate (type: int) Param[3]: sampleSize (type: int) Param[4]: channels (type: int) -Function 481: LoadWaveSamples() (1 input parameters) +Function 482: LoadWaveSamples() (1 input parameters) Name: LoadWaveSamples Return type: float * Description: Load samples data from wave as a 32bit float data array Param[1]: wave (type: Wave) -Function 482: UnloadWaveSamples() (1 input parameters) +Function 483: UnloadWaveSamples() (1 input parameters) Name: UnloadWaveSamples Return type: void Description: Unload samples data loaded with LoadWaveSamples() Param[1]: samples (type: float *) -Function 483: LoadMusicStream() (1 input parameters) +Function 484: LoadMusicStream() (1 input parameters) Name: LoadMusicStream Return type: Music Description: Load music stream from file Param[1]: fileName (type: const char *) -Function 484: LoadMusicStreamFromMemory() (3 input parameters) +Function 485: LoadMusicStreamFromMemory() (3 input parameters) Name: LoadMusicStreamFromMemory Return type: Music Description: Load music stream from data Param[1]: fileType (type: const char *) Param[2]: data (type: const unsigned char *) Param[3]: dataSize (type: int) -Function 485: IsMusicReady() (1 input parameters) +Function 486: IsMusicReady() (1 input parameters) Name: IsMusicReady Return type: bool Description: Checks if a music stream is ready Param[1]: music (type: Music) -Function 486: UnloadMusicStream() (1 input parameters) +Function 487: UnloadMusicStream() (1 input parameters) Name: UnloadMusicStream Return type: void Description: Unload music stream Param[1]: music (type: Music) -Function 487: PlayMusicStream() (1 input parameters) +Function 488: PlayMusicStream() (1 input parameters) Name: PlayMusicStream Return type: void Description: Start music playing Param[1]: music (type: Music) -Function 488: IsMusicStreamPlaying() (1 input parameters) +Function 489: IsMusicStreamPlaying() (1 input parameters) Name: IsMusicStreamPlaying Return type: bool Description: Check if music is playing Param[1]: music (type: Music) -Function 489: UpdateMusicStream() (1 input parameters) +Function 490: UpdateMusicStream() (1 input parameters) Name: UpdateMusicStream Return type: void Description: Updates buffers for music streaming Param[1]: music (type: Music) -Function 490: StopMusicStream() (1 input parameters) +Function 491: StopMusicStream() (1 input parameters) Name: StopMusicStream Return type: void Description: Stop music playing Param[1]: music (type: Music) -Function 491: PauseMusicStream() (1 input parameters) +Function 492: PauseMusicStream() (1 input parameters) Name: PauseMusicStream Return type: void Description: Pause music playing Param[1]: music (type: Music) -Function 492: ResumeMusicStream() (1 input parameters) +Function 493: ResumeMusicStream() (1 input parameters) Name: ResumeMusicStream Return type: void Description: Resume playing paused music Param[1]: music (type: Music) -Function 493: SeekMusicStream() (2 input parameters) +Function 494: SeekMusicStream() (2 input parameters) Name: SeekMusicStream Return type: void Description: Seek music to a position (in seconds) Param[1]: music (type: Music) Param[2]: position (type: float) -Function 494: SetMusicVolume() (2 input parameters) +Function 495: SetMusicVolume() (2 input parameters) Name: SetMusicVolume Return type: void Description: Set volume for music (1.0 is max level) Param[1]: music (type: Music) Param[2]: volume (type: float) -Function 495: SetMusicPitch() (2 input parameters) +Function 496: SetMusicPitch() (2 input parameters) Name: SetMusicPitch Return type: void Description: Set pitch for a music (1.0 is base level) Param[1]: music (type: Music) Param[2]: pitch (type: float) -Function 496: SetMusicPan() (2 input parameters) +Function 497: SetMusicPan() (2 input parameters) Name: SetMusicPan Return type: void Description: Set pan for a music (0.5 is center) Param[1]: music (type: Music) Param[2]: pan (type: float) -Function 497: GetMusicTimeLength() (1 input parameters) +Function 498: GetMusicTimeLength() (1 input parameters) Name: GetMusicTimeLength Return type: float Description: Get music time length (in seconds) Param[1]: music (type: Music) -Function 498: GetMusicTimePlayed() (1 input parameters) +Function 499: GetMusicTimePlayed() (1 input parameters) Name: GetMusicTimePlayed Return type: float Description: Get current music time played (in seconds) Param[1]: music (type: Music) -Function 499: LoadAudioStream() (3 input parameters) +Function 500: LoadAudioStream() (3 input parameters) Name: LoadAudioStream Return type: AudioStream Description: Load audio stream (to stream raw audio pcm data) Param[1]: sampleRate (type: unsigned int) Param[2]: sampleSize (type: unsigned int) Param[3]: channels (type: unsigned int) -Function 500: IsAudioStreamReady() (1 input parameters) +Function 501: IsAudioStreamReady() (1 input parameters) Name: IsAudioStreamReady Return type: bool Description: Checks if an audio stream is ready Param[1]: stream (type: AudioStream) -Function 501: UnloadAudioStream() (1 input parameters) +Function 502: UnloadAudioStream() (1 input parameters) Name: UnloadAudioStream Return type: void Description: Unload audio stream and free memory Param[1]: stream (type: AudioStream) -Function 502: UpdateAudioStream() (3 input parameters) +Function 503: UpdateAudioStream() (3 input parameters) Name: UpdateAudioStream Return type: void Description: Update audio stream buffers with data Param[1]: stream (type: AudioStream) Param[2]: data (type: const void *) Param[3]: frameCount (type: int) -Function 503: IsAudioStreamProcessed() (1 input parameters) +Function 504: IsAudioStreamProcessed() (1 input parameters) Name: IsAudioStreamProcessed Return type: bool Description: Check if any audio stream buffers requires refill Param[1]: stream (type: AudioStream) -Function 504: PlayAudioStream() (1 input parameters) +Function 505: PlayAudioStream() (1 input parameters) Name: PlayAudioStream Return type: void Description: Play audio stream Param[1]: stream (type: AudioStream) -Function 505: PauseAudioStream() (1 input parameters) +Function 506: PauseAudioStream() (1 input parameters) Name: PauseAudioStream Return type: void Description: Pause audio stream Param[1]: stream (type: AudioStream) -Function 506: ResumeAudioStream() (1 input parameters) +Function 507: ResumeAudioStream() (1 input parameters) Name: ResumeAudioStream Return type: void Description: Resume audio stream Param[1]: stream (type: AudioStream) -Function 507: IsAudioStreamPlaying() (1 input parameters) +Function 508: IsAudioStreamPlaying() (1 input parameters) Name: IsAudioStreamPlaying Return type: bool Description: Check if audio stream is playing Param[1]: stream (type: AudioStream) -Function 508: StopAudioStream() (1 input parameters) +Function 509: StopAudioStream() (1 input parameters) Name: StopAudioStream Return type: void Description: Stop audio stream Param[1]: stream (type: AudioStream) -Function 509: SetAudioStreamVolume() (2 input parameters) +Function 510: SetAudioStreamVolume() (2 input parameters) Name: SetAudioStreamVolume Return type: void Description: Set volume for audio stream (1.0 is max level) Param[1]: stream (type: AudioStream) Param[2]: volume (type: float) -Function 510: SetAudioStreamPitch() (2 input parameters) +Function 511: SetAudioStreamPitch() (2 input parameters) Name: SetAudioStreamPitch Return type: void Description: Set pitch for audio stream (1.0 is base level) Param[1]: stream (type: AudioStream) Param[2]: pitch (type: float) -Function 511: SetAudioStreamPan() (2 input parameters) +Function 512: SetAudioStreamPan() (2 input parameters) Name: SetAudioStreamPan Return type: void Description: Set pan for audio stream (0.5 is centered) Param[1]: stream (type: AudioStream) Param[2]: pan (type: float) -Function 512: SetAudioStreamBufferSizeDefault() (1 input parameters) +Function 513: SetAudioStreamBufferSizeDefault() (1 input parameters) Name: SetAudioStreamBufferSizeDefault Return type: void Description: Default size for new audio streams Param[1]: size (type: int) -Function 513: SetAudioStreamCallback() (2 input parameters) +Function 514: SetAudioStreamCallback() (2 input parameters) Name: SetAudioStreamCallback Return type: void Description: Audio thread callback to request new data Param[1]: stream (type: AudioStream) Param[2]: callback (type: AudioCallback) -Function 514: AttachAudioStreamProcessor() (2 input parameters) +Function 515: AttachAudioStreamProcessor() (2 input parameters) Name: AttachAudioStreamProcessor Return type: void Description: Attach audio stream processor to stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 515: DetachAudioStreamProcessor() (2 input parameters) +Function 516: DetachAudioStreamProcessor() (2 input parameters) Name: DetachAudioStreamProcessor Return type: void Description: Detach audio stream processor from stream Param[1]: stream (type: AudioStream) Param[2]: processor (type: AudioCallback) -Function 516: AttachAudioMixedProcessor() (1 input parameters) +Function 517: AttachAudioMixedProcessor() (1 input parameters) Name: AttachAudioMixedProcessor Return type: void Description: Attach audio stream processor to the entire audio pipeline Param[1]: processor (type: AudioCallback) -Function 517: DetachAudioMixedProcessor() (1 input parameters) +Function 518: DetachAudioMixedProcessor() (1 input parameters) Name: DetachAudioMixedProcessor Return type: void Description: Detach audio stream processor from the entire audio pipeline diff --git a/parser/output/raylib_api.xml b/parser/output/raylib_api.xml index 85b3e3e4c..f450069cc 100644 --- a/parser/output/raylib_api.xml +++ b/parser/output/raylib_api.xml @@ -656,7 +656,7 @@ - + @@ -1685,6 +1685,10 @@ + + + + diff --git a/projects/Geany/raylib.c.tags b/projects/Geany/raylib.c.tags index 4cc6d4605..79a192a14 100644 --- a/projects/Geany/raylib.c.tags +++ b/projects/Geany/raylib.c.tags @@ -202,6 +202,7 @@ ImageDrawText|void|(Image *dst, Vector2 position, const char *text, int fontSize ImageDrawTextEx|void|(Image *dst, Vector2 position, Font font, const char *text, float fontSize, float spacing, Color color);| ImageFlipVertical|void|(Image *image);| ImageFlipHorizontal|void|(Image *image);| +ImageRotate|void|(Image *image, int degrees);| ImageRotateCW|void|(Image *image);| ImageRotateCCW|void|(Image *image);| ImageColorTint|void|(Image *image, Color color);| diff --git a/projects/Notepad++/raylib_npp_parser/raylib_npp.xml b/projects/Notepad++/raylib_npp_parser/raylib_npp.xml index 7e173a936..883183cef 100644 --- a/projects/Notepad++/raylib_npp_parser/raylib_npp.xml +++ b/projects/Notepad++/raylib_npp_parser/raylib_npp.xml @@ -1570,6 +1570,12 @@ + + + + + + diff --git a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h index f20f065e3..c32cdb26e 100644 --- a/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h +++ b/projects/Notepad++/raylib_npp_parser/raylib_to_parse.h @@ -346,6 +346,7 @@ RLAPI void ImageMipmaps(Image *image); RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) RLAPI void ImageFlipVertical(Image *image); // Flip image vertically RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally +RLAPI void ImageRotate(Image *image, int degrees); // Rotate image by input angle in degrees (-359 to 359) RLAPI void ImageRotateCW(Image *image); // Rotate image clockwise 90deg RLAPI void ImageRotateCCW(Image *image); // Rotate image counter-clockwise 90deg RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint diff --git a/src/raylib.h b/src/raylib.h index 703e70c21..b2b825831 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1268,6 +1268,7 @@ RLAPI void ImageMipmaps(Image *image); RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) RLAPI void ImageFlipVertical(Image *image); // Flip image vertically RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally +RLAPI void ImageRotate(Image *image, int degrees); // Rotate image by input angle in degrees (-359 to 359) RLAPI void ImageRotateCW(Image *image); // Rotate image clockwise 90deg RLAPI void ImageRotateCCW(Image *image); // Rotate image counter-clockwise 90deg RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint diff --git a/src/rtextures.c b/src/rtextures.c index eeeebba82..b5e2a7f7a 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -2118,6 +2118,65 @@ void ImageFlipHorizontal(Image *image) } } +// Rotate image in degrees +void ImageRotate(Image *image, int degrees) +{ + // Security check to avoid program crash + if ((image->data == NULL) || (image->width == 0) || (image->height == 0)) return; + + if (image->mipmaps > 1) TRACELOG(LOG_WARNING, "Image manipulation only applied to base mipmap level"); + if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats"); + else + { + float rad = degrees * PI / 180.0f; + float sinRadius = sin(rad); + float cosRadius = cos(rad); + + int width = abs(image->width * cosRadius) + abs(image->height * sinRadius); + int height = abs(image->height * cosRadius) + abs(image->width * sinRadius); + + int bytesPerPixel = GetPixelDataSize(1, 1, image->format); + unsigned char *rotatedData = (unsigned char *)RL_CALLOC(width * height, bytesPerPixel); + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + float oldX = ((x - width / 2.0f) * cosRadius + (y - height / 2.0f) * sinRadius) + image->width / 2.0f; + float oldY = ((y - height / 2.0f) * cosRadius - (x - width / 2.0f) * sinRadius) + image->height / 2.0f; + + if (oldX >= 0 && oldX < image->width && oldY >= 0 && oldY < image->height) + { + int x1 = (int)floor(oldX); + int y1 = (int)floor(oldY); + int x2 = MIN(x1 + 1, image->width - 1); + int y2 = MIN(y1 + 1, image->height - 1); + + float px = oldX - x1; + float py = oldY - y1; + + for (int i = 0; i < bytesPerPixel; i++) + { + float f1 = ((unsigned char *)image->data)[(y1 * image->width + x1) * bytesPerPixel + i]; + float f2 = ((unsigned char *)image->data)[(y1 * image->width + x2) * bytesPerPixel + i]; + float f3 = ((unsigned char *)image->data)[(y2 * image->width + x1) * bytesPerPixel + i]; + float f4 = ((unsigned char *)image->data)[(y2 * image->width + x2) * bytesPerPixel + i]; + + float val = f1 * (1 - px) * (1 - py) + f2 * px * (1 - py) + f3 * (1 - px) * py + f4 * px * py; + + rotatedData[(y * width + x) * bytesPerPixel + i] = (unsigned char)val; + } + } + } + } + + RL_FREE(image->data); + image->data = rotatedData; + image->width = width; + image->height = height; + } +} + // Rotate image clockwise 90deg void ImageRotateCW(Image *image) { From 144ae120ab0a0c2ebecd83a97c312740b5b46ce4 Mon Sep 17 00:00:00 2001 From: LuraMoth <85266594+Luramoth@users.noreply.github.com> Date: Thu, 25 May 2023 00:15:53 -0700 Subject: [PATCH 0095/1350] Add new file formats to FAQ (#3079) I noticed some file formats from the new release of raylib were missing so I decided to help out and update it! --- FAQ.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FAQ.md b/FAQ.md index b933bf615..2020162c7 100644 --- a/FAQ.md +++ b/FAQ.md @@ -119,8 +119,8 @@ raylib can load data from multiple standard file formats: - Image/Textures: PNG, BMP, TGA, JPG, GIF, QOI, PSD, DDS, HDR, KTX, ASTC, PKM, PVR - Fonts: FNT (sprite font), TTF, OTF - - Models/Meshes: OBJ, IQM, GLTF, VOX - - Audio: WAV, OGG, MP3, FLAC, XM, MOD + - Models/Meshes: OBJ, IQM, GLTF, VOX, M3D + - Audio: WAV, OGG, MP3, FLAC, XM, MOD, QOA ### Does raylib support the Vulkan API? From 5ef50ae139305c487e4cff0f49f3ced88345e445 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 26 May 2023 14:01:19 +0200 Subject: [PATCH 0096/1350] REVIEWED: `ImageRotate()` formatting --- src/rtextures.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index b5e2a7f7a..a1987ef51 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -2128,27 +2128,27 @@ void ImageRotate(Image *image, int degrees) if (image->format >= PIXELFORMAT_COMPRESSED_DXT1_RGB) TRACELOG(LOG_WARNING, "Image manipulation not supported for compressed formats"); else { - float rad = degrees * PI / 180.0f; + float rad = degrees*PI/180.0f; float sinRadius = sin(rad); float cosRadius = cos(rad); - int width = abs(image->width * cosRadius) + abs(image->height * sinRadius); - int height = abs(image->height * cosRadius) + abs(image->width * sinRadius); + int width = fabsf(image->width*cosRadius) + fabsf(image->height*sinRadius); + int height = fabsf(image->height*cosRadius) + fabsf(image->width*sinRadius); int bytesPerPixel = GetPixelDataSize(1, 1, image->format); - unsigned char *rotatedData = (unsigned char *)RL_CALLOC(width * height, bytesPerPixel); + unsigned char *rotatedData = (unsigned char *)RL_CALLOC(width*height, bytesPerPixel); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { - float oldX = ((x - width / 2.0f) * cosRadius + (y - height / 2.0f) * sinRadius) + image->width / 2.0f; - float oldY = ((y - height / 2.0f) * cosRadius - (x - width / 2.0f) * sinRadius) + image->height / 2.0f; + float oldX = ((x - width/2.0f)*cosRadius + (y - height/2.0f)*sinRadius) + image->width/2.0f; + float oldY = ((y - height/2.0f)*cosRadius - (x - width/2.0f)*sinRadius) + image->height/2.0f; - if (oldX >= 0 && oldX < image->width && oldY >= 0 && oldY < image->height) + if ((oldX >= 0) && (oldX < image->width) && (oldY >= 0) && (oldY < image->height)) { - int x1 = (int)floor(oldX); - int y1 = (int)floor(oldY); + int x1 = (int)floorf(oldX); + int y1 = (int)floorf(oldY); int x2 = MIN(x1 + 1, image->width - 1); int y2 = MIN(y1 + 1, image->height - 1); @@ -2157,14 +2157,14 @@ void ImageRotate(Image *image, int degrees) for (int i = 0; i < bytesPerPixel; i++) { - float f1 = ((unsigned char *)image->data)[(y1 * image->width + x1) * bytesPerPixel + i]; - float f2 = ((unsigned char *)image->data)[(y1 * image->width + x2) * bytesPerPixel + i]; - float f3 = ((unsigned char *)image->data)[(y2 * image->width + x1) * bytesPerPixel + i]; - float f4 = ((unsigned char *)image->data)[(y2 * image->width + x2) * bytesPerPixel + i]; + float f1 = ((unsigned char *)image->data)[(y1*image->width + x1)*bytesPerPixel + i]; + float f2 = ((unsigned char *)image->data)[(y1*image->width + x2)*bytesPerPixel + i]; + float f3 = ((unsigned char *)image->data)[(y2*image->width + x1)*bytesPerPixel + i]; + float f4 = ((unsigned char *)image->data)[(y2*image->width + x2)*bytesPerPixel + i]; - float val = f1 * (1 - px) * (1 - py) + f2 * px * (1 - py) + f3 * (1 - px) * py + f4 * px * py; + float val = f1*(1 - px)*(1 - py) + f2*px*(1 - py) + f3*(1 - px)*py + f4*px*py; - rotatedData[(y * width + x) * bytesPerPixel + i] = (unsigned char)val; + rotatedData[(y*width + x)*bytesPerPixel + i] = (unsigned char)val; } } } From 20860e2ba05ee818d57b1fd2ec4a7f094a0f3783 Mon Sep 17 00:00:00 2001 From: Jason Liang Date: Sat, 27 May 2023 00:27:44 -0700 Subject: [PATCH 0097/1350] Fix a link in the FAQ (#3082) --- FAQ.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FAQ.md b/FAQ.md index 2020162c7..274236d47 100644 --- a/FAQ.md +++ b/FAQ.md @@ -14,7 +14,7 @@ - [What does raylib provide that other engines or libraries don't?](#what-does-raylib-provide-that-other-engines-or-libraries-dont) - [How does raylib compare to Unity/Unreal/Godot?](#how-does-raylib-compare-to-unityunrealgodot) - [What development tools are required for raylib?](#what-development-tools-are-required-for-raylib) -- [Which are raylib external dependencies?](#which-are-raylib-external-dependencies) +- [What are raylib's external dependencies?](#what-are-raylibs-external-dependencies) - [Can I use raylib with other technologies or libraries?](#can-i-use-raylib-with-other-technologies-or-libraries) - [What file formats are supported by raylib?](#what-file-formats-are-supported-by-raylib) - [Does raylib support the Vulkan API?](#does-raylib-support-the-vulkan-api) From aad51d47048f9eb5c13bbbd65dc5cd4d37aa68e5 Mon Sep 17 00:00:00 2001 From: Nikita K Date: Sat, 27 May 2023 14:11:33 +0300 Subject: [PATCH 0098/1350] BINDINGS.md: Janet bindings supported version update (#3083) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index a10e5860f..a60979fc4 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -31,6 +31,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | h-raylib | **4.5** | [Haskell](https://haskell.org/) | Apache-2.0 | https://github.com/Anut-py/h-raylib | | raylib-hx | 4.2 | [Haxe](https://haxe.org/) | Zlib | https://github.com/foreignsasquatch/raylib-hx | | hb-raylib | 3.5 | [Harbour](https://harbour.github.io) | MIT | https://github.com/MarcosLeonardoMendezGerencir/hb-raylib | +| jaylib | 4.5 | [Janet](https://janet-lang.org/) | MIT | https://github.com/janet-lang/jaylib | | jaylib | 4.2 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | GPLv3+CE | https://github.com/electronstudio/jaylib/ | | raylib-j | 4.0 | [Java](https://en.wikipedia.org/wiki/Java_(programming_language)) | Zlib | https://github.com/CreedVI/Raylib-J | | raylib.jl | 4.2 | [Julia](https://julialang.org/) | Zlib | https://github.com/irishgreencitrus/raylib.jl | @@ -133,7 +134,6 @@ These are older raylib bindings that are more than 2 versions old or have not be | raylib-jai | 3.1-dev | [Jai](https://github.com/BSVino/JaiPrimer/blob/master/JaiPrimer.md) | https://github.com/kujukuju/raylib-jai | | ray.zig | 2.5 | [Zig](https://ziglang.org/) | https://github.com/BitPuffin/zig-raylib-experiments | | raylib-Ada | 3.0 | [Ada](https://www.adacore.com/about-ada) | https://github.com/mimo/raylib-Ada | -| jaylib | 3.0 | [Janet](https://janet-lang.org/) | https://github.com/janet-lang/jaylib | | raykit | ? | [Kit](https://www.kitlang.org/) | https://github.com/Gamerfiend/raykit | | ray.mod | 3.0 | [BlitzMax](https://blitzmax.org/) | https://github.com/bmx-ng/ray.mod | | raylib-mosaic | 3.0 | [Mosaic](https://github.com/sal55/langs/tree/master/Mosaic) | https://github.com/pluckyporcupine/raylib-mosaic | From 4a371a51975cf7e06bbf1d94284493f77a820113 Mon Sep 17 00:00:00 2001 From: RayIT Date: Sun, 28 May 2023 11:33:14 +0200 Subject: [PATCH 0099/1350] Fixed compile on OpenBSD (#3085) --- cmake/LibraryConfigurations.cmake | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index ffb1a047d..a54261d27 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -25,6 +25,18 @@ if (${PLATFORM} MATCHES "Desktop") add_definitions(-D_CRT_SECURE_NO_WARNINGS) find_package(OpenGL QUIET) set(LIBS_PRIVATE ${OPENGL_LIBRARIES} winmm) + elseif (UNIX) + find_library(pthread NAMES pthread) + find_package(OpenGL QUIET) + if ("${OPENGL_LIBRARIES}" STREQUAL "") + set(OPENGL_LIBRARIES "GL") + endif () + + if ("${CMAKE_SYSTEM_NAME}" MATCHES "(Net|Open)BSD") + find_library(OSS_LIBRARY ossaudio) + endif () + + set(LIBS_PRIVATE m pthread ${OPENGL_LIBRARIES} ${OSS_LIBRARY}) else () find_library(pthread NAMES pthread) find_package(OpenGL QUIET) From 15cbf313bb3f490a64c25b0d54a5635742b6de53 Mon Sep 17 00:00:00 2001 From: RayIT Date: Sun, 28 May 2023 14:49:33 +0200 Subject: [PATCH 0100/1350] Enhanced cmake part for OpenBSD (#3086) * Fixed compile on OpenBSD * Changed to not use seperate UNIX for cmake --- cmake/LibraryConfigurations.cmake | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index a54261d27..e9eee630a 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -43,14 +43,15 @@ if (${PLATFORM} MATCHES "Desktop") if ("${OPENGL_LIBRARIES}" STREQUAL "") set(OPENGL_LIBRARIES "GL") endif () + + set(LIBS_PRIVATE m atomic pthread ${OPENGL_LIBRARIES} ${OSS_LIBRARY}) if ("${CMAKE_SYSTEM_NAME}" MATCHES "(Net|Open)BSD") find_library(OSS_LIBRARY ossaudio) + set(LIBS_PRIVATE m pthread ${OPENGL_LIBRARIES} ${OSS_LIBRARY}) endif () - set(LIBS_PRIVATE m atomic pthread ${OPENGL_LIBRARIES} ${OSS_LIBRARY}) - - if (USE_AUDIO) + if (NOT "${CMAKE_SYSTEM_NAME}" MATCHES "(Net|Open)BSD" AND USE_AUDIO) set(LIBS_PRIVATE ${LIBS_PRIVATE} dl) endif () endif () From 924bb7226ba1aa5228479fab5f7e9b0f8b0837d1 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 30 May 2023 21:12:03 +0200 Subject: [PATCH 0101/1350] UPDATED: `sdefl` and `sinfl` DEFLATE compression libraries --- src/external/sdefl.h | 65 +++++++++------ src/external/sinfl.h | 187 ++++++++++++++++++++++++------------------- 2 files changed, 144 insertions(+), 108 deletions(-) diff --git a/src/external/sdefl.h b/src/external/sdefl.h index 5db76763a..56539a56e 100644 --- a/src/external/sdefl.h +++ b/src/external/sdefl.h @@ -38,10 +38,10 @@ this file implementation in *one* C or C++ file to prevent collisions. | zlib 1.2.11 -1 | 72 MB/s | 307 MB/s | 42298774 | 42.30 | | zlib 1.2.11 -6 | 24 MB/s | 313 MB/s | 36548921 | 36.55 | | zlib 1.2.11 -9 | 20 MB/s | 314 MB/s | 36475792 | 36.48 | -| sdefl 1.0 -0 | 127 MB/s | 371 MB/s | 40004116 | 39.88 | -| sdefl 1.0 -1 | 111 MB/s | 398 MB/s | 38940674 | 38.82 | -| sdefl 1.0 -5 | 45 MB/s | 420 MB/s | 36577183 | 36.46 | -| sdefl 1.0 -7 | 38 MB/s | 423 MB/s | 36523781 | 36.41 | +| sdefl 1.0 -0 | 127 MB/s | 355 MB/s | 40004116 | 39.88 | +| sdefl 1.0 -1 | 111 MB/s | 413 MB/s | 38940674 | 38.82 | +| sdefl 1.0 -5 | 45 MB/s | 436 MB/s | 36577183 | 36.46 | +| sdefl 1.0 -7 | 38 MB/s | 432 MB/s | 36523781 | 36.41 | | libdeflate 1.3 -1 | 147 MB/s | 667 MB/s | 39597378 | 39.60 | | libdeflate 1.3 -6 | 69 MB/s | 689 MB/s | 36648318 | 36.65 | | libdeflate 1.3 -9 | 13 MB/s | 672 MB/s | 35197141 | 35.20 | @@ -50,20 +50,20 @@ this file implementation in *one* C or C++ file to prevent collisions. ### Compression Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia): -| File | Original | `sdefl 0` | `sdefl 5` | `sdefl 7` | -| :------ | ---------: | -----------------: | ---------: | ----------: | -| dickens | 10.192.446 | 4,260,187| 3,845,261| 3,833,657 | -| mozilla | 51.220.480 | 20,774,706 | 19,607,009 | 19,565,867 | -| mr | 9.970.564 | 3,860,531 | 3,673,460 | 3,665,627 | -| nci | 33.553.445 | 4,030,283 | 3,094,526 | 3,006,075 | -| ooffice | 6.152.192 | 3,320,063 | 3,186,373 | 3,183,815 | -| osdb | 10.085.684 | 3,919,646 | 3,649,510 | 3,649,477 | -| reymont | 6.627.202 | 2,263,378 | 1,857,588 | 1,827,237 | -| samba | 21.606.400 | 6,121,797 | 5,462,670 | 5,450,762 | -| sao | 7.251.944 | 5,612,421 | 5,485,380 | 5,481,765 | -| webster | 41.458.703 | 13,972,648 | 12,059,432 | 11,991,421 | -| xml | 5.345.280 | 886,620| 674,009 | 662,141 | -| x-ray | 8.474.240 | 6,304,655 | 6,244,779 | 6,244,779 | +| File | Original | `sdefl 0` | `sdefl 5` | `sdefl 7` | +| --------| -----------| -------------| ---------- | ------------| +| dickens | 10.192.446 | 4,260,187 | 3,845,261 | 3,833,657 | +| mozilla | 51.220.480 | 20,774,706 | 19,607,009 | 19,565,867 | +| mr | 9.970.564 | 3,860,531 | 3,673,460 | 3,665,627 | +| nci | 33.553.445 | 4,030,283 | 3,094,526 | 3,006,075 | +| ooffice | 6.152.192 | 3,320,063 | 3,186,373 | 3,183,815 | +| osdb | 10.085.684 | 3,919,646 | 3,649,510 | 3,649,477 | +| reymont | 6.627.202 | 2,263,378 | 1,857,588 | 1,827,237 | +| samba | 21.606.400 | 6,121,797 | 5,462,670 | 5,450,762 | +| sao | 7.251.944 | 5,612,421 | 5,485,380 | 5,481,765 | +| webster | 41.458.703 | 13,972,648 | 12,059,432 | 11,991,421 | +| xml | 5.345.280 | 886,620 | 674,009 | 662,141 | +| x-ray | 8.474.240 | 6,304,655 | 6,244,779 | 6,244,779 | ## License ``` @@ -462,8 +462,12 @@ sdefl_match_codes(struct sdefl_match_codes *cod, int dist, int len) { 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28 }; + assert(len <= 258); + assert(dist <= 32768); cod->ls = lslot[len]; cod->lc = 257 + cod->ls; + assert(cod->lc <= 285); + cod->dx = sdefl_ilog2(sdefl_npow2(dist) >> 2); cod->dc = cod->dx ? ((cod->dx + 1) << 1) + (dist > dxmax[cod->dx]) : dist-1; } @@ -501,7 +505,9 @@ sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, sdefl_precode(&symcnt, freqs, items, s->cod.len.lit, s->cod.len.off); sdefl_huff(lens, codes, freqs, SDEFL_PRE_MAX, SDEFL_PRE_CODES); for (item_cnt = SDEFL_PRE_MAX; item_cnt > 4; item_cnt--) { - if (lens[perm[item_cnt - 1]]) break; + if (lens[perm[item_cnt - 1]]){ + break; + } } /* block header */ sdefl_put(dst, s, is_last ? 0x01 : 0x00, 1); /* block */ @@ -509,8 +515,9 @@ sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, sdefl_put(dst, s, symcnt.lit - 257, 5); sdefl_put(dst, s, symcnt.off - 1, 5); sdefl_put(dst, s, item_cnt - 4, 4); - for (i = 0; i < item_cnt; ++i) + for (i = 0; i < item_cnt; ++i) { sdefl_put(dst, s, lens[perm[i]], 3); + } for (i = 0; i < symcnt.items; ++i) { unsigned sym = items[i] & 0x1F; sdefl_put(dst, s, (int)codes[sym], lens[sym]); @@ -521,12 +528,14 @@ sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, } /* block sequences */ for (i = 0; i < s->seq_cnt; ++i) { - if (s->seq[i].off >= 0) + if (s->seq[i].off >= 0) { for (j = 0; j < s->seq[i].len; ++j) { int c = in[s->seq[i].off + j]; sdefl_put(dst, s, (int)s->cod.word.lit[c], s->cod.len.lit[c]); } - else sdefl_match(dst, s, -s->seq[i].off, s->seq[i].len); + } else { + sdefl_match(dst, s, -s->seq[i].off, s->seq[i].len); + } } sdefl_put(dst, s, (int)(s)->cod.word.lit[SDEFL_EOB], (s)->cod.len.lit[SDEFL_EOB]); memset(&s->freq, 0, sizeof(s->freq)); @@ -579,12 +588,13 @@ sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in, for (n = 0; n < SDEFL_HASH_SIZ; ++n) { s->tbl[n] = SDEFL_NIL; } - do {int blk_end = i + SDEFL_BLK_MAX < in_len ? i + SDEFL_BLK_MAX : in_len; + do {int blk_end = ((i + SDEFL_BLK_MAX) < in_len) ? (i + SDEFL_BLK_MAX) : in_len; while (i < blk_end) { struct sdefl_match m = {0}; - int max_match = ((in_len-i)>SDEFL_MAX_MATCH) ? SDEFL_MAX_MATCH:(in_len-i); + int left = blk_end - i; + int max_match = (left >= SDEFL_MAX_MATCH) ? SDEFL_MAX_MATCH : left; int nice_match = pref[lvl] < max_match ? pref[lvl] : max_match; - int run = 1, inc = 1, run_inc; + int run = 1, inc = 1, run_inc = 0; if (max_match > SDEFL_MIN_MATCH) { sdefl_fnd(&m, s, max_chain, max_match, in, i); } @@ -615,9 +625,11 @@ sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in, unsigned h = sdefl_hash32(&in[i]); s->prv[i&SDEFL_WIN_MSK] = s->tbl[h]; s->tbl[h] = i, i += inc; + assert(i <= blk_end); } } else { i += run_inc; + assert(i <= blk_end); } } if (litlen) { @@ -627,8 +639,9 @@ sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in, sdefl_flush(&q, s, blk_end == in_len, in); } while (i < in_len); - if (s->bitcnt) + if (s->bitcnt > 0) sdefl_put(&q, s, 0x00, 8 - s->bitcnt); + return (int)(q - out); } extern int diff --git a/src/external/sinfl.h b/src/external/sinfl.h index 09f50d2bc..915da9d23 100644 --- a/src/external/sinfl.h +++ b/src/external/sinfl.h @@ -10,7 +10,7 @@ as needed to keep the implementation as concise as possible. - Dual license with either MIT or public domain - Small implementation - Deflate: 525 LoC - - Inflate: 320 LoC + - Inflate: 500 LoC - Webassembly: - Deflate ~3.7 KB (~2.2KB compressed) - Inflate ~3.6 KB (~2.2KB compressed) @@ -39,10 +39,10 @@ this file implementation in *one* C or C++ file to prevent collisions. | zlib 1.2.11 -1 | 72 MB/s | 307 MB/s | 42298774 | 42.30 | | zlib 1.2.11 -6 | 24 MB/s | 313 MB/s | 36548921 | 36.55 | | zlib 1.2.11 -9 | 20 MB/s | 314 MB/s | 36475792 | 36.48 | -| sdefl 1.0 -0 | 127 MB/s | 371 MB/s | 40004116 | 39.88 | -| sdefl 1.0 -1 | 111 MB/s | 398 MB/s | 38940674 | 38.82 | -| sdefl 1.0 -5 | 45 MB/s | 420 MB/s | 36577183 | 36.46 | -| sdefl 1.0 -7 | 38 MB/s | 423 MB/s | 36523781 | 36.41 | +| sdefl 1.0 -0 | 127 MB/s | 355 MB/s | 40004116 | 39.88 | +| sdefl 1.0 -1 | 111 MB/s | 413 MB/s | 38940674 | 38.82 | +| sdefl 1.0 -5 | 45 MB/s | 436 MB/s | 36577183 | 36.46 | +| sdefl 1.0 -7 | 38 MB/s | 432 MB/s | 36523781 | 36.41 | | libdeflate 1.3 -1 | 147 MB/s | 667 MB/s | 39597378 | 39.60 | | libdeflate 1.3 -6 | 69 MB/s | 689 MB/s | 36648318 | 36.65 | | libdeflate 1.3 -9 | 13 MB/s | 672 MB/s | 35197141 | 35.20 | @@ -51,20 +51,20 @@ this file implementation in *one* C or C++ file to prevent collisions. ### Compression Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index.php?page=silesia): -| File | Original | `sdefl 0` | `sdefl 5` | `sdefl 7` | -| :------ | ---------: | -----------------: | ---------: | ----------: | -| dickens | 10.192.446 | 4,260,187| 3,845,261| 3,833,657 | -| mozilla | 51.220.480 | 20,774,706 | 19,607,009 | 19,565,867 | -| mr | 9.970.564 | 3,860,531 | 3,673,460 | 3,665,627 | -| nci | 33.553.445 | 4,030,283 | 3,094,526 | 3,006,075 | -| ooffice | 6.152.192 | 3,320,063 | 3,186,373 | 3,183,815 | -| osdb | 10.085.684 | 3,919,646 | 3,649,510 | 3,649,477 | -| reymont | 6.627.202 | 2,263,378 | 1,857,588 | 1,827,237 | -| samba | 21.606.400 | 6,121,797 | 5,462,670 | 5,450,762 | -| sao | 7.251.944 | 5,612,421 | 5,485,380 | 5,481,765 | -| webster | 41.458.703 | 13,972,648 | 12,059,432 | 11,991,421 | -| xml | 5.345.280 | 886,620| 674,009 | 662,141 | -| x-ray | 8.474.240 | 6,304,655 | 6,244,779 | 6,244,779 | +| File | Original | `sdefl 0` | `sdefl 5` | `sdefl 7` | +| --------| -----------| -------------| ---------- | ------------| +| dickens | 10.192.446 | 4,260,187 | 3,845,261 | 3,833,657 | +| mozilla | 51.220.480 | 20,774,706 | 19,607,009 | 19,565,867 | +| mr | 9.970.564 | 3,860,531 | 3,673,460 | 3,665,627 | +| nci | 33.553.445 | 4,030,283 | 3,094,526 | 3,006,075 | +| ooffice | 6.152.192 | 3,320,063 | 3,186,373 | 3,183,815 | +| osdb | 10.085.684 | 3,919,646 | 3,649,510 | 3,649,477 | +| reymont | 6.627.202 | 2,263,378 | 1,857,588 | 1,827,237 | +| samba | 21.606.400 | 6,121,797 | 5,462,670 | 5,450,762 | +| sao | 7.251.944 | 5,612,421 | 5,485,380 | 5,481,765 | +| webster | 41.458.703 | 13,972,648 | 12,059,432 | 11,991,421 | +| xml | 5.345.280 | 886,620 | 674,009 | 662,141 | +| x-ray | 8.474.240 | 6,304,655 | 6,244,779 | 6,244,779 | ## License ``` @@ -151,7 +151,7 @@ extern int zsinflate(void *out, int cap, const void *in, int size); #endif #ifndef SINFL_NO_SIMD -#if __x86_64__ || defined(_WIN32) || defined(_WIN64) +#if defined(__x86_64__) || defined(_WIN32) || defined(_WIN64) #include #define sinfl_char16 __m128i #define sinfl_char16_ld(p) _mm_loadu_si128((const __m128i *)(void*)(p)) @@ -183,6 +183,18 @@ sinfl_read64(const void *p) { memcpy(&n, p, 8); return n; } +static void +sinfl_copy64(unsigned char **dst, unsigned char **src) { + unsigned long long n; + memcpy(&n, *src, 8); + memcpy(*dst, &n, 8); + *dst += 8, *src += 8; +} +static unsigned char* +sinfl_write64(unsigned char *dst, unsigned long long w) { + memcpy(dst, &w, 8); + return dst + 8; +} #ifndef SINFL_NO_SIMD static unsigned char* sinfl_write128(unsigned char *dst, sinfl_char16 w) { @@ -195,25 +207,12 @@ sinfl_copy128(unsigned char **dst, unsigned char **src) { sinfl_char16_str(*dst, n); *dst += 16, *src += 16; } -#else -static unsigned char* -sinfl_write64(unsigned char *dst, unsigned long long w) { - memcpy(dst, &w, 8); - return dst + 8; -} -static void -sinfl_copy64(unsigned char **dst, unsigned char **src) { - unsigned long long n; - memcpy(&n, *src, 8); - memcpy(*dst, &n, 8); - *dst += 8, *src += 8; -} #endif static void sinfl_refill(struct sinfl *s) { s->bitbuf |= sinfl_read64(s->bitptr) << s->bitcnt; s->bitptr += (63 - s->bitcnt) >> 3; - s->bitcnt |= 56; /* bitcount is in range [56,63] */ + s->bitcnt |= 56; /* bitcount in range [56,63] */ } static int sinfl_peek(struct sinfl *s, int cnt) { @@ -222,7 +221,7 @@ sinfl_peek(struct sinfl *s, int cnt) { return s->bitbuf & ((1ull << cnt) - 1); } static void -sinfl_consume(struct sinfl *s, int cnt) { +sinfl_eat(struct sinfl *s, int cnt) { assert(cnt <= s->bitcnt); s->bitbuf >>= cnt; s->bitcnt -= cnt; @@ -230,7 +229,7 @@ sinfl_consume(struct sinfl *s, int cnt) { static int sinfl__get(struct sinfl *s, int cnt) { int res = sinfl_peek(s, cnt); - sinfl_consume(s, cnt); + sinfl_eat(s, cnt); return res; } static int @@ -285,7 +284,7 @@ sinfl_build_subtbl(struct sinfl_gen *gen, unsigned *tbl, int tbl_bits, while (1) { unsigned entry; int bit, stride, i; - /* start new subtable */ + /* start new sub-table */ if ((gen->word & ((1 << tbl_bits)-1)) != sub_prefix) { int used = 0; sub_prefix = gen->word & ((1 << tbl_bits)-1); @@ -299,7 +298,7 @@ sinfl_build_subtbl(struct sinfl_gen *gen, unsigned *tbl, int tbl_bits, tbl_end = sub_start + (1 << sub_bits); tbl[sub_prefix] = (sub_start << 16) | 0x10 | (sub_bits & 0xf); } - /* fill subtable */ + /* fill sub-table */ entry = (*gen->sorted << 16) | ((gen->len - tbl_bits) & 0xf); gen->sorted++; i = sub_start + (gen->word >> tbl_bits); @@ -353,18 +352,17 @@ sinfl_build(unsigned *tbl, unsigned char *lens, int tbl_bits, int maxlen, } static int sinfl_decode(struct sinfl *s, const unsigned *tbl, int bit_len) { - sinfl_refill(s); - {int idx = sinfl_peek(s, bit_len); + int idx = sinfl_peek(s, bit_len); unsigned key = tbl[idx]; if (key & 0x10) { /* sub-table lookup */ int len = key & 0x0f; - sinfl_consume(s, bit_len); + sinfl_eat(s, bit_len); idx = sinfl_peek(s, len); key = tbl[((key >> 16) & 0xffff) + (unsigned)idx]; } - sinfl_consume(s, key & 0x0f); - return (key >> 16) & 0x0fff;} + sinfl_eat(s, key & 0x0f); + return (key >> 16) & 0x0fff; } static int sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) { @@ -402,11 +400,11 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) } break; case stored: { /* uncompressed block */ - int len; + int len, nlen; sinfl_refill(&s); sinfl__get(&s,s.bitcnt & 7); len = sinfl__get(&s,16); - //int nlen = sinfl__get(&s,16); // @raysan5: Unused variable? + nlen = sinfl__get(&s,16); in -= 2; s.bitcnt = 0; if (len > (e-in) || !len) @@ -430,40 +428,62 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) state = blk; } break; case dyn: { - /* dynamic huffman codes */ - int n, i; - unsigned hlens[SINFL_PRE_TBL_SIZE]; - unsigned char nlens[19] = {0}, lens[288+32]; + /* dynamic huffman codes */ + int n, i; + unsigned hlens[SINFL_PRE_TBL_SIZE]; + unsigned char nlens[19] = {0}, lens[288+32]; + sinfl_refill(&s); + {int nlit = 257 + sinfl__get(&s,5); + int ndist = 1 + sinfl__get(&s,5); + int nlen = 4 + sinfl__get(&s,4); + for (n = 0; n < nlen; n++) + nlens[order[n]] = (unsigned char)sinfl_get(&s,3); + sinfl_build(hlens, nlens, 7, 7, 19); + + /* decode code lengths */ + for (n = 0; n < nlit + ndist;) { sinfl_refill(&s); - {int nlit = 257 + sinfl__get(&s,5); - int ndist = 1 + sinfl__get(&s,5); - int nlen = 4 + sinfl__get(&s,4); - for (n = 0; n < nlen; n++) - nlens[order[n]] = (unsigned char)sinfl_get(&s,3); - sinfl_build(hlens, nlens, 7, 7, 19); - - /* decode code lengths */ - for (n = 0; n < nlit + ndist;) { - int sym = sinfl_decode(&s, hlens, 7); - switch (sym) {default: lens[n++] = (unsigned char)sym; break; - case 16: for (i=3+sinfl_get(&s,2);i;i--,n++) lens[n]=lens[n-1]; break; - case 17: for (i=3+sinfl_get(&s,3);i;i--,n++) lens[n]=0; break; - case 18: for (i=11+sinfl_get(&s,7);i;i--,n++) lens[n]=0; break;} - } - /* build lit/dist tables */ - sinfl_build(s.lits, lens, 10, 15, nlit); - sinfl_build(s.dsts, lens + nlit, 8, 15, ndist); - state = blk;} + int sym = sinfl_decode(&s, hlens, 7); + switch (sym) {default: lens[n++] = (unsigned char)sym; break; + case 16: for (i=3+sinfl_get(&s,2);i;i--,n++) lens[n]=lens[n-1]; break; + case 17: for (i=3+sinfl_get(&s,3);i;i--,n++) lens[n]=0; break; + case 18: for (i=11+sinfl_get(&s,7);i;i--,n++) lens[n]=0; break;} + } + /* build lit/dist tables */ + sinfl_build(s.lits, lens, 10, 15, nlit); + sinfl_build(s.dsts, lens + nlit, 8, 15, ndist); + state = blk;} } break; case blk: { /* decompress block */ - int sym = sinfl_decode(&s, s.lits, 10); - if (sym < 256) { - /* literal */ - *out++ = (unsigned char)sym; - } else if (sym > 256) {sym -= 257; /* match symbol */ + while (1) { sinfl_refill(&s); + int sym = sinfl_decode(&s, s.lits, 10); + if (sym < 256) { + /* literal */ + if (sinfl_unlikely(out >= oe)) { + return (int)(out-o); + } + *out++ = (unsigned char)sym; + sym = sinfl_decode(&s, s.lits, 10); + if (sym < 256) { + *out++ = (unsigned char)sym; + continue; + } + } + if (sinfl_unlikely(sym == 256)) { + /* end of block */ + if (last) return (int)(out-o); + state = hdr; + break; + } + /* match */ + if (sym >= 286) { + /* length codes 286 and 287 must not appear in compressed data */ + return (int)(out-o); + } + sym -= 257; {int len = sinfl__get(&s, lbits[sym]) + lbase[sym]; int dsym = sinfl_decode(&s, s.dsts, 8); int offs = sinfl__get(&s, dbits[dsym]) + dbase[dsym]; @@ -476,11 +496,17 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) #ifndef SINFL_NO_SIMD if (sinfl_likely(oe - out >= 16 * 3)) { if (offs >= 16) { - /* copy match */ + /* simd copy match */ sinfl_copy128(&dst, &src); sinfl_copy128(&dst, &src); do sinfl_copy128(&dst, &src); while (dst < out); + } else if (offs >= 8) { + /* word copy match */ + sinfl_copy64(&dst, &src); + sinfl_copy64(&dst, &src); + do sinfl_copy64(&dst, &src); + while (dst < out); } else if (offs == 1) { /* rle match copying */ sinfl_char16 w = sinfl_char16_char(src[0]); @@ -489,6 +515,7 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) do dst = sinfl_write128(dst, w); while (dst < out); } else { + /* byte copy match */ *dst++ = *src++; *dst++ = *src++; do *dst++ = *src++; @@ -498,7 +525,7 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) #else if (sinfl_likely(oe - out >= 3 * 8 - 3)) { if (offs >= 8) { - /* copy match */ + /* word copy match */ sinfl_copy64(&dst, &src); sinfl_copy64(&dst, &src); do sinfl_copy64(&dst, &src); @@ -513,6 +540,7 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) do dst = sinfl_write64(dst, w); while (dst < out); } else { + /* byte copy match */ *dst++ = *src++; *dst++ = *src++; do *dst++ = *src++; @@ -524,13 +552,8 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) *dst++ = *src++; *dst++ = *src++; do *dst++ = *src++; - while (dst < out);} - } - } else { - /* end of block */ - if (last) return (int)(out-o); - state = hdr; - break; + while (dst < out); + }} } } break;} } From 45c00ab9d43982a841c549ce4c9d3bbe60a660b1 Mon Sep 17 00:00:00 2001 From: yujiri8 Date: Tue, 30 May 2023 15:33:01 -0400 Subject: [PATCH 0102/1350] build.zig: Fix cross-compiling from Linux (#3090) --- src/build.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/build.zig b/src/build.zig index 48e93c326..59d8241e7 100644 --- a/src/build.zig +++ b/src/build.zig @@ -45,6 +45,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built raylib.linkSystemLibrary("dl"); raylib.linkSystemLibrary("m"); raylib.linkSystemLibrary("X11"); + raylib.addIncludePath("/usr/include"); raylib.defineCMacro("PLATFORM_DESKTOP", null); }, From a18667c2e9a3a815ab37a915e582e8906ff9cace Mon Sep 17 00:00:00 2001 From: A Billy <47233777+TheLastBilly@users.noreply.github.com> Date: Tue, 30 May 2023 15:34:08 -0400 Subject: [PATCH 0103/1350] cross compilation for PLATFORM_DRM (#3091) * added cross compilation options for DRM * fixed identation --- src/Makefile | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/Makefile b/src/Makefile index 5d7086d8a..c6cd30e81 100644 --- a/src/Makefile +++ b/src/Makefile @@ -95,12 +95,11 @@ USE_EXTERNAL_GLFW ?= FALSE USE_WAYLAND_DISPLAY ?= FALSE # Use cross-compiler for PLATFORM_RPI -ifeq ($(PLATFORM),PLATFORM_RPI) - USE_RPI_CROSS_COMPILER ?= FALSE - ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) - RPI_TOOLCHAIN ?= C:/SysGCC/Raspberry - RPI_TOOLCHAIN_SYSROOT ?= $(RPI_TOOLCHAIN)/arm-linux-gnueabihf/sysroot - endif +USE_RPI_CROSS_COMPILER ?= FALSE +ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) + RPI_TOOLCHAIN ?= C:/SysGCC/Raspberry + RPI_TOOLCHAIN_NAME ?= arm-linux-gnueabihf + RPI_TOOLCHAIN_SYSROOT ?= $(RPI_TOOLCHAIN)/$(RPI_TOOLCHAIN_NAME)/sysroot endif # Determine if the file has root access (only required to install raylib) @@ -273,8 +272,16 @@ ifeq ($(PLATFORM),PLATFORM_RPI) ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) # Define RPI cross-compiler #CC = armv6j-hardfloat-linux-gnueabi-gcc - CC = $(RPI_TOOLCHAIN)/bin/arm-linux-gnueabihf-gcc - AR = $(RPI_TOOLCHAIN)/bin/arm-linux-gnueabihf-ar + CC = $(RPI_TOOLCHAIN)/bin/$(RPI_TOOLCHAIN_NAME)-gcc + AR = $(RPI_TOOLCHAIN)/bin/$(RPI_TOOLCHAIN_NAME)-ar + endif +endif +ifeq ($(PLATFORM),PLATFORM_DRM) + ifeq ($(USE_RPI_CROSS_COMPILER),TRUE) + # Define RPI cross-compiler + #CC = armv6j-hardfloat-linux-gnueabi-gcc + CC = $(RPI_TOOLCHAIN)/bin/$(RPI_TOOLCHAIN_NAME)-gcc + AR = $(RPI_TOOLCHAIN)/bin/$(RPI_TOOLCHAIN_NAME)-ar endif endif ifeq ($(PLATFORM),PLATFORM_WEB) @@ -451,6 +458,10 @@ ifeq ($(PLATFORM),PLATFORM_RPI) endif ifeq ($(PLATFORM),PLATFORM_DRM) INCLUDE_PATHS += -I/usr/include/libdrm + ifeq ($(USE_RPI_CROSSCOMPILER), TRUE) + INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/usr/include + INCLUDE_PATHS += -I$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/include + endif endif ifeq ($(PLATFORM),PLATFORM_ANDROID) NATIVE_APP_GLUE = $(ANDROID_NDK)/sources/android/native_app_glue @@ -499,6 +510,9 @@ ifeq ($(PLATFORM),PLATFORM_RPI) endif ifeq ($(PLATFORM),PLATFORM_DRM) LDFLAGS += -Wl,-soname,lib$(RAYLIB_LIB_NAME).so.$(RAYLIB_API_VERSION) + ifeq ($(USE_RPI_CROSSCOMPILER), TRUE) + INCLUDE_PATHS += -L$(RPI_TOOLCHAIN_SYSROOT)/opt/vc/lib -L$(RPI_TOOLCHAIN_SYSROOT)/usr/lib + endif endif ifeq ($(PLATFORM),PLATFORM_ANDROID) LDFLAGS += -Wl,-soname,libraylib.$(API_VERSION).so -Wl,--exclude-libs,libatomic.a From e497603678823dc93f01dd9686819a92d780ad67 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 31 May 2023 18:36:33 +0200 Subject: [PATCH 0104/1350] ADDED: Experimental support for OpenGL ES 3.0 -WIP- Just added the required flags to request the OpenGL ES 3.0 context but it has not been tested... --- src/rcore.c | 16 ++++++++++++++-- src/rlgl.h | 19 ++++++++++++++----- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 7a31b5ed6..af65cebf0 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -291,6 +291,7 @@ #if defined(PLATFORM_WEB) #define GLFW_INCLUDE_ES2 // GLFW3: Enable OpenGL ES 2.0 (translated to WebGL) + //#define GLFW_INCLUDE_ES3 // GLFW3: Enable OpenGL ES 3.0 (transalted to WebGL2?) #include "GLFW/glfw3.h" // GLFW3: Windows, OpenGL context and Input management #include // Required for: timespec, nanosleep(), select() - POSIX @@ -2451,7 +2452,7 @@ VrStereoConfig LoadVrStereoConfig(VrDeviceInfo device) { VrStereoConfig config = { 0 }; - if ((rlGetVersion() == RL_OPENGL_33) || (rlGetVersion() == RL_OPENGL_ES_20)) + if ((rlGetVersion() == RL_OPENGL_33) || (rlGetVersion() >= RL_OPENGL_ES_20)) { // Compute aspect ratio float aspect = ((float)device.hResolution*0.5f)/(float)device.vResolution; @@ -4172,6 +4173,17 @@ static bool InitGraphicsDevice(int width, int height) glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); #else glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); +#endif + } + else if (rlGetVersion() == RL_OPENGL_ES_30) // Request OpenGL ES 3.0 context + { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); +#if defined(PLATFORM_DESKTOP) + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); +#else + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); #endif } @@ -4543,7 +4555,7 @@ static bool InitGraphicsDevice(int width, int height) const EGLint framebufferAttribs[] = { - EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, // Type of context support -> Required on RPI? + EGL_RENDERABLE_TYPE, (rlGetVersion() == RL_OPENGL_ES_30)? EGL_OPENGL_ES3_BIT : EGL_OPENGL_ES2_BIT, // Type of context support #if defined(PLATFORM_DRM) EGL_SURFACE_TYPE, EGL_WINDOW_BIT, // Don't use it on Android! #endif diff --git a/src/rlgl.h b/src/rlgl.h index 85a8ec368..f0bdf3cd4 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -26,6 +26,7 @@ * #define GRAPHICS_API_OPENGL_33 * #define GRAPHICS_API_OPENGL_43 * #define GRAPHICS_API_OPENGL_ES2 +* #define GRAPHICS_API_OPENGL_ES3 * Use selected OpenGL graphics backend, should be supported by platform * Those preprocessor defines are only used on rlgl module, if OpenGL version is * required by any other module, use rlGetVersion() to check it @@ -178,6 +179,11 @@ #define GRAPHICS_API_OPENGL_33 #endif +// OpenGL ES 3.0 uses OpenGL ES 2.0 functionality (and more) +#if defined(GRAPHICS_API_OPENGL_ES3) + #define GRAPHICS_API_OPENGL_ES2 +#endif + // Support framebuffer objects by default // NOTE: Some driver implementation do not support it, despite they should #define RLGL_RENDER_TEXTURES_HINT @@ -382,7 +388,8 @@ typedef enum { RL_OPENGL_21, // OpenGL 2.1 (GLSL 120) RL_OPENGL_33, // OpenGL 3.3 (GLSL 330) RL_OPENGL_43, // OpenGL 4.3 (using GLSL 330) - RL_OPENGL_ES_20 // OpenGL ES 2.0 (GLSL 100) + RL_OPENGL_ES_20, // OpenGL ES 2.0 (GLSL 100) + RL_OPENGL_ES_30 // OpenGL ES 3.0 (GLSL 300 es) } rlGlVersion; // Trace log level @@ -2389,15 +2396,17 @@ int rlGetVersion(void) #endif #if defined(GRAPHICS_API_OPENGL_21) glVersion = RL_OPENGL_21; +#elif defined(GRAPHICS_API_OPENGL_43) + glVersion = RL_OPENGL_43; #elif defined(GRAPHICS_API_OPENGL_33) glVersion = RL_OPENGL_33; #endif -#if defined(GRAPHICS_API_OPENGL_43) - glVersion = RL_OPENGL_43; -#endif -#if defined(GRAPHICS_API_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_ES3) + glVersion = RL_OPENGL_ES_30; +#elif defined(GRAPHICS_API_OPENGL_ES2) glVersion = RL_OPENGL_ES_20; #endif + return glVersion; } From 2dec56e7b772ce4734f415e03715d0712ae68b8a Mon Sep 17 00:00:00 2001 From: Peter0x44 Date: Thu, 1 Jun 2023 08:47:52 +0100 Subject: [PATCH 0105/1350] Add error if raylib.h is included in a C++98 program (#3093) The color macros don't work properly in C++98, because they require aggregate initialzation, which is a C++11 feature. So, explicitly state how to fix this issue, instead of letting the compiler give a more vague error message like: main.cpp:8:23: error: expected '(' for function-style cast or type construction ClearBackground(BLACK); ^~~~~ /opt/homebrew/Cellar/raylib/4.5.0/include/raylib.h:179:35: note: expanded from macro 'BLACK' #define BLACK CLITERAL(Color){ 0, 0, 0, 255 } // Black NOTE: Don't use this check with MSVC because by default, it reports 199711L regardless of any C++ version passed on command line Only passing `/Zc:__cplusplus` will make MSVC set this correctly see: https://learn.microsoft.com/en-us/cpp/build/reference/zc-cplusplus --- src/raylib.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/raylib.h b/src/raylib.h index b2b825831..56acd6ad1 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -133,12 +133,20 @@ // NOTE: MSVC C++ compiler does not support compound literals (C99 feature) // Plain structures in C++ (without constructors) can be initialized with { } +// This is called aggregate initialization (C++11 feature) #if defined(__cplusplus) #define CLITERAL(type) type #else #define CLITERAL(type) (type) #endif +// Some compilers (mostly macos clang) default to C++98, +// where aggregate initialization can't be used +// So, give a more clear error stating how to fix this +#if !defined(_MSC_VER) && (defined(__cplusplus) && __cplusplus < 201103L) + #error "C++11 or later is required. Add -std=c++11" +#endif + // NOTE: We set some defines with some data types declared by raylib // Other modules (raymath, rlgl) also require some of those types, so, // to be able to use those other modules as standalone (not depending on raylib) From b1b6ae3905512cbe3b39ad37109a494c10f5ca32 Mon Sep 17 00:00:00 2001 From: Pixel Phobic <65147646+PixelPhobicGames@users.noreply.github.com> Date: Fri, 2 Jun 2023 01:29:45 -0700 Subject: [PATCH 0106/1350] Full Movement Added to Right Analog Stick (#3095) I Added Analog Stick Support to the rcamera module, However this code only allowed for 4 Directions of Movement, This Changed adds the full range of Movement to the Right Analog Stick. --- src/rcamera.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rcamera.h b/src/rcamera.h index c1b048e01..2a20d5814 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -465,10 +465,10 @@ void UpdateCamera(Camera *camera, int mode) CameraYaw(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_X) * 2)*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); CameraPitch(camera, -(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_Y) * 2)*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); - if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) <= -0.75f) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); - if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) <= -0.75f) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); - if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) >= 0.75f) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); - if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) >= 0.75f) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) <= -0.25f) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); + if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) <= -0.25f) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); + if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) >= 0.25f) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); + if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) >= 0.25f) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); } //if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, CAMERA_MOVE_SPEED); From ba802fdd5e33f0e48406841c5aa9c97fd9fc715b Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 3 Jun 2023 19:50:46 +0200 Subject: [PATCH 0107/1350] tweaks --- src/rcore.c | 2 +- src/rlgl.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index af65cebf0..ee682e8b0 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3431,7 +3431,7 @@ unsigned char *CompressData(const unsigned char *data, int dataSize, int *compDa struct sdefl sdefl = { 0 }; int bounds = sdefl_bound(dataSize); compData = (unsigned char *)RL_CALLOC(bounds, 1); - *compDataSize = sdeflate(&sdefl, compData, data, dataSize, COMPRESSION_QUALITY_DEFLATE); // Compression level 8, same as stbwi + *compDataSize = sdeflate(&sdefl, compData, data, dataSize, COMPRESSION_QUALITY_DEFLATE); // Compression level 8, same as stbiw TRACELOG(LOG_INFO, "SYSTEM: Compress data: Original size: %i -> Comp. size: %i", dataSize, *compDataSize); #endif diff --git a/src/rlgl.h b/src/rlgl.h index f0bdf3cd4..5ba2593f2 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2204,8 +2204,9 @@ void rlLoadExtensions(void *loader) #if defined(GRAPHICS_API_OPENGL_ES2) #if defined(PLATFORM_DESKTOP) + // TODO: Support OpenGL ES 3.0 if (gladLoadGLES2((GLADloadfunc)loader) == 0) TRACELOG(RL_LOG_WARNING, "GLAD: Cannot load OpenGL ES2.0 functions"); - else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL ES2.0 loaded successfully"); + else TRACELOG(RL_LOG_INFO, "GLAD: OpenGL ES 2.0 loaded successfully"); #endif // Get supported extensions list From f8b352f6d979bd7bfb960fc776bea0e9d39bf9da Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 3 Jun 2023 19:51:16 +0200 Subject: [PATCH 0108/1350] ADDED: `ExportImageToMemory()` Only PNG supported for now --- src/raylib.h | 1 + src/rtextures.c | 30 +++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index 56acd6ad1..f6a48bc73 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1243,6 +1243,7 @@ RLAPI Image LoadImageFromScreen(void); RLAPI bool IsImageReady(Image image); // Check if an image is ready RLAPI void UnloadImage(Image image); // Unload image from CPU memory (RAM) RLAPI bool ExportImage(Image image, const char *fileName); // Export image data to file, returns true on success +RLAPI unsigned char *ExportImageToMemory(Image image, const char *fileType, int *fileSize); // Export image to memory buffer RLAPI bool ExportImageAsCode(Image image, const char *fileName); // Export image as code file defining an array of bytes, returns true on success // Image generation functions diff --git a/src/rtextures.c b/src/rtextures.c index a1987ef51..7d76616c1 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -605,6 +605,34 @@ bool ExportImage(Image image, const char *fileName) return success; } +// Export image to memory buffer +unsigned char *ExportImageToMemory(Image image, const char *fileType, int *dataSize) +{ + unsigned char *fileData = NULL; + *dataSize = 0; + + if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return NULL; + +#if defined(SUPPORT_IMAGE_EXPORT) + int channels = 4; + + if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAYSCALE) channels = 1; + else if (image.format == PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA) channels = 2; + else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8) channels = 3; + else if (image.format == PIXELFORMAT_UNCOMPRESSED_R8G8B8A8) channels = 4; + +#if defined(SUPPORT_FILEFORMAT_PNG) + if ((strcmp(fileType, ".png") == 0) || (strcmp(fileType, ".PNG") == 0)) + { + fileData = stbi_write_png_to_mem((const unsigned char *)image.data, image.width*channels, image.width, image.height, channels, dataSize); + } +#endif + +#endif + + return fileData; +} + // Export image as code file (.h) defining an array of bytes bool ExportImageAsCode(Image image, const char *fileName) { @@ -1013,7 +1041,7 @@ Image ImageCopy(Image image) if (height < 1) height = 1; } - newImage.data = RL_MALLOC(size); + newImage.data = RL_CALLOC(size, 1); if (newImage.data != NULL) { From 753c0b385380adff54803fb9ff90fa5b4763327b Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Mon, 5 Jun 2023 01:13:08 +0200 Subject: [PATCH 0109/1350] Addition of support for vox files in version 200. (#3097) --- src/external/vox_loader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/external/vox_loader.h b/src/external/vox_loader.h index c1b26e6f3..6df10b518 100644 --- a/src/external/vox_loader.h +++ b/src/external/vox_loader.h @@ -555,7 +555,7 @@ int Vox_LoadFromMemory(unsigned char* pvoxData, unsigned int voxDataSize, VoxArr version = ((unsigned int*)fileDataPtr)[0]; fileDataPtr += 4; - if (version != 150) + if (version != 150 && version != 200) { return VOX_ERROR_FILE_VERSION_NOT_MATCH; //"MagicaVoxel version doesn't match" } From 6aada7d5ecc2c201665444dddec9debc2465be8f Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 9 Jun 2023 18:07:25 +0200 Subject: [PATCH 0110/1350] Updated examples to `raygui 4.0-dev` --- examples/shapes/raygui.h | 2761 ++++++++++------- examples/shapes/shapes_draw_circle_sector.c | 16 +- .../shapes/shapes_draw_rectangle_rounded.c | 28 +- examples/shapes/shapes_draw_ring.c | 24 +- 4 files changed, 1641 insertions(+), 1188 deletions(-) diff --git a/examples/shapes/raygui.h b/examples/shapes/raygui.h index ea95e5c85..63a27fa90 100644 --- a/examples/shapes/raygui.h +++ b/examples/shapes/raygui.h @@ -1,22 +1,38 @@ /******************************************************************************************* * -* raygui v3.2 - A simple and easy-to-use immediate-mode gui library +* raygui v4.0-dev - A simple and easy-to-use immediate-mode gui library * * DESCRIPTION: +* raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also +* available as a standalone library, as long as input and drawing functions are provided. * -* raygui is a tools-dev-focused immediate-mode-gui library based on raylib but also -* available as a standalone library, as long as input and drawing functions are provided. +* FEATURES: +* - Immediate-mode gui, minimal retained data +* - +25 controls provided (basic and advanced) +* - Styling system for colors, font and metrics +* - Icons supported, embeds a complete 1-bit icons pack +* - Standalone usage mode option (custom graphics backends) +* - Multiple tools provided for raygui development * -* Controls provided: +* POSSIBLE IMPROVEMENTS: +* - Redesign functions that require a value as parameter to be returned, pass by reference +* - Better standalone mode API for easy plug of custom backends +* - Externalize required inputs in some way, allow user customization * -* # Container/separators Controls +* LIMITATIONS: +* - No multi-line word-wraped text box support +* - No auto-layout mechanism provided, up to the user to define controls position and size +* - Standalone mode requires library modification and some user work to plug another backend +* +* CONTROLS PROVIDED: +* # Container/separators Controls * - WindowBox --> StatusBar, Panel * - GroupBox --> Line * - Line * - Panel --> StatusBar * - ScrollPanel --> StatusBar * -* # Basic Controls +* # Basic Controls * - Label * - Button * - LabelButton --> Label @@ -26,7 +42,6 @@ * - ComboBox * - DropdownBox * - TextBox -* - TextBoxMulti * - ValueBox --> TextBox * - Spinner --> Button, ValueBox * - Slider @@ -36,81 +51,124 @@ * - DummyRec * - Grid * -* # Advance Controls +* # Advance Controls * - ListView * - ColorPicker --> ColorPanel, ColorBarHue * - MessageBox --> Window, Label, Button * - TextInputBox --> Window, Label, TextBox, Button * -* It also provides a set of functions for styling the controls based on its properties (size, color). +* It also provides a set of functions for styling the controls based on its properties (size, color). * * * RAYGUI STYLE (guiStyle): +* raygui uses a global data array for all gui style properties (allocated on data segment by default), +* when a new style is loaded, it is loaded over the global style... but a default gui style could always be +* recovered with GuiLoadStyleDefault() function, that overwrites the current style to the default one * -* raygui uses a global data array for all gui style properties (allocated on data segment by default), -* when a new style is loaded, it is loaded over the global style... but a default gui style could always be -* recovered with GuiLoadStyleDefault() function, that overwrites the current style to the default one +* The global style array size is fixed and depends on the number of controls and properties: * -* The global style array size is fixed and depends on the number of controls and properties: +* static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)]; * -* static unsigned int guiStyle[RAYGUI_MAX_CONTROLS*(RAYGUI_MAX_PROPS_BASE + RAYGUI_MAX_PROPS_EXTENDED)]; +* guiStyle size is by default: 16*(16 + 8) = 384*4 = 1536 bytes = 1.5 KB * -* guiStyle size is by default: 16*(16 + 8) = 384*4 = 1536 bytes = 1.5 KB +* Note that the first set of BASE properties (by default guiStyle[0..15]) belong to the generic style +* used for all controls, when any of those base values is set, it is automatically populated to all +* controls, so, specific control values overwriting generic style should be set after base values. * -* Note that the first set of BASE properties (by default guiStyle[0..15]) belong to the generic style -* used for all controls, when any of those base values is set, it is automatically populated to all -* controls, so, specific control values overwriting generic style should be set after base values. +* After the first BASE set we have the EXTENDED properties (by default guiStyle[16..23]), those +* properties are actually common to all controls and can not be overwritten individually (like BASE ones) +* Some of those properties are: TEXT_SIZE, TEXT_SPACING, LINE_COLOR, BACKGROUND_COLOR * -* After the first BASE set we have the EXTENDED properties (by default guiStyle[16..23]), those -* properties are actually common to all controls and can not be overwritten individually (like BASE ones) -* Some of those properties are: TEXT_SIZE, TEXT_SPACING, LINE_COLOR, BACKGROUND_COLOR +* Custom control properties can be defined using the EXTENDED properties for each independent control. * -* Custom control properties can be defined using the EXTENDED properties for each independent control. -* -* TOOL: rGuiStyler is a visual tool to customize raygui style. +* TOOL: rGuiStyler is a visual tool to customize raygui style: github.com/raysan5/rguistyler * * * RAYGUI ICONS (guiIcons): +* raygui could use a global array containing icons data (allocated on data segment by default), +* a custom icons set could be loaded over this array using GuiLoadIcons(), but loaded icons set +* must be same RAYGUI_ICON_SIZE and no more than RAYGUI_ICON_MAX_ICONS will be loaded * -* raygui could use a global array containing icons data (allocated on data segment by default), -* a custom icons set could be loaded over this array using GuiLoadIcons(), but loaded icons set -* must be same RAYGUI_ICON_SIZE and no more than RAYGUI_ICON_MAX_ICONS will be loaded +* Every icon is codified in binary form, using 1 bit per pixel, so, every 16x16 icon +* requires 8 integers (16*16/32) to be stored in memory. * -* Every icon is codified in binary form, using 1 bit per pixel, so, every 16x16 icon -* requires 8 integers (16*16/32) to be stored in memory. +* When the icon is draw, actually one quad per pixel is drawn if the bit for that pixel is set. * -* When the icon is draw, actually one quad per pixel is drawn if the bit for that pixel is set. +* The global icons array size is fixed and depends on the number of icons and size: * -* The global icons array size is fixed and depends on the number of icons and size: +* static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS]; * -* static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS]; +* guiIcons size is by default: 256*(16*16/32) = 2048*4 = 8192 bytes = 8 KB * -* guiIcons size is by default: 256*(16*16/32) = 2048*4 = 8192 bytes = 8 KB +* TOOL: rGuiIcons is a visual tool to customize/create raygui icons: github.com/raysan5/rguiicons * -* TOOL: rGuiIcons is a visual tool to customize raygui icons. +* RAYGUI LAYOUT: +* raygui currently does not provide an auto-layout mechanism like other libraries, +* layouts must be defined manually on controls drawing, providing the right bounds Rectangle for it. * +* TOOL: rGuiLayout is a visual tool to create raygui layouts: github.com/raysan5/rguilayout * * CONFIGURATION: +* #define RAYGUI_IMPLEMENTATION +* Generates the implementation of the library into the included file. +* If not defined, the library is in header only mode and can be included in other headers +* or source files without problems. But only ONE file should hold the implementation. * -* #define RAYGUI_IMPLEMENTATION -* Generates the implementation of the library into the included file. -* If not defined, the library is in header only mode and can be included in other headers -* or source files without problems. But only ONE file should hold the implementation. +* #define RAYGUI_STANDALONE +* Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined +* internally in the library and input management and drawing functions must be provided by +* the user (check library implementation for further details). * -* #define RAYGUI_STANDALONE -* Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined -* internally in the library and input management and drawing functions must be provided by -* the user (check library implementation for further details). +* #define RAYGUI_NO_ICONS +* Avoid including embedded ricons data (256 icons, 16x16 pixels, 1-bit per pixel, 2KB) * -* #define RAYGUI_NO_ICONS -* Avoid including embedded ricons data (256 icons, 16x16 pixels, 1-bit per pixel, 2KB) -* -* #define RAYGUI_CUSTOM_ICONS -* Includes custom ricons.h header defining a set of custom icons, -* this file can be generated using rGuiIcons tool +* #define RAYGUI_CUSTOM_ICONS +* Includes custom ricons.h header defining a set of custom icons, +* this file can be generated using rGuiIcons tool * +* #define RAYGUI_DEBUG_TEXT_BOUNDS +* Draw text bounds rectangles for debug * * VERSIONS HISTORY: +* 4.0 (xx-Jun-2023) REDESIGNED: GuiScrollPanel(), get parameters by reference and return result value +* REDESIGNED: GuiToggleGroup(), get parameters by reference and return result value +* REDESIGNED: GuiComboBox(), get parameters by reference and return result value +* REDESIGNED: GuiCheckBox(), get parameters by reference and return result value +* REDESIGNED: GuiSlider(), get parameters by reference and return result value +* REDESIGNED: GuiSliderBar(), get parameters by reference and return result value +* REDESIGNED: GuiProgressBar(), get parameters by reference and return result value +* REDESIGNED: GuiListView(), get parameters by reference and return result value +* REDESIGNED: GuiColorPicker(), get parameters by reference and return result value +* REDESIGNED: GuiColorPanel(), get parameters by reference and return result value +* REDESIGNED: GuiColorBarAlpha(), get parameters by reference and return result value +* REDESIGNED: GuiColorBarHue(), get parameters by reference and return result value +* REDESIGNED: GuiGrid(), get parameters by reference and return result value +* REDESIGNED: GuiGrid(), added extra parameter +* REDESIGNED: GuiListViewEx(), change parameters order +* REDESIGNED: All controls return result as int value +* +* 3.6 (10-May-2023) ADDED: New icon: SAND_TIMER +* ADDED: GuiLoadStyleFromMemory() (binary only) +* REVIEWED: GuiScrollBar() horizontal movement key +* REVIEWED: GuiTextBox() crash on cursor movement +* REVIEWED: GuiTextBox(), additional inputs support +* REVIEWED: GuiLabelButton(), avoid text cut +* REVIEWED: GuiTextInputBox(), password input +* REVIEWED: Local GetCodepointNext(), aligned with raylib +* REDESIGNED: GuiSlider*()/GuiScrollBar() to support out-of-bounds +* +* 3.5 (20-Apr-2023) ADDED: GuiTabBar(), based on GuiToggle() +* ADDED: Helper functions to split text in separate lines +* ADDED: Multiple new icons, useful for code editing tools +* REMOVED: Unneeded icon editing functions +* REMOVED: GuiTextBoxMulti(), very limited and broken +* REMOVED: MeasureTextEx() dependency, logic directly implemented +* REMOVED: DrawTextEx() dependency, logic directly implemented +* REVIEWED: GuiScrollBar(), improve mouse-click behaviour +* REVIEWED: Library header info, more info, better organized +* REDESIGNED: GuiTextBox() to support cursor movement +* REDESIGNED: GuiDrawText() to divide drawing by lines +* * 3.2 (22-May-2022) RENAMED: Some enum values, for unification, avoiding prefixes * REMOVED: GuiScrollBar(), only internal * REDESIGNED: GuiPanel() to support text parameter @@ -120,6 +178,7 @@ * REDESIGNED: GuiColorBarAlpha() to support text parameter * REDESIGNED: GuiColorBarHue() to support text parameter * REDESIGNED: GuiTextInputBox() to support password +* * 3.1 (12-Jan-2022) REVIEWED: Default style for consistency (aligned with rGuiLayout v2.5 tool) * REVIEWED: GuiLoadStyle() to support compressed font atlas image data and unload previous textures * REVIEWED: External icons usage logic @@ -127,10 +186,12 @@ * RENAMED: Multiple controls properties definitions to prepend RAYGUI_ * RENAMED: RICON_ references to RAYGUI_ICON_ for library consistency * Projects updated and multiple tweaks +* * 3.0 (04-Nov-2021) Integrated ricons data to avoid external file * REDESIGNED: GuiTextBoxMulti() * REMOVED: GuiImageButton*() * Multiple minor tweaks and bugs corrected +* * 2.9 (17-Mar-2021) REMOVED: Tooltip API * 2.8 (03-May-2020) Centralized rectangles drawing to GuiDrawRectangle() * 2.7 (20-Feb-2020) ADDED: Possible tooltips API @@ -140,6 +201,7 @@ * Replaced property INNER_PADDING by TEXT_PADDING, renamed some properties * ADDED: 8 new custom styles ready to use * Multiple minor tweaks and bugs corrected +* * 2.5 (28-May-2019) Implemented extended GuiTextBox(), GuiValueBox(), GuiSpinner() * 2.3 (29-Apr-2019) ADDED: rIcons auxiliar library and support for it, multiple controls reviewed * Refactor all controls drawing mechanism to use control state @@ -158,9 +220,13 @@ * 0.9 (07-Mar-2016) Reviewed and tested by Albert Martos, Ian Eito, Sergio Martinez and Ramon Santamaria. * 0.8 (27-Aug-2015) Initial release. Implemented by Kevin Gato, Daniel Nicolás and Ramon Santamaria. * +* DEPENDENCIES: +* raylib 4.5 Inputs reading (keyboard/mouse), shapes drawing, font loading and text drawing +* +* By default raygui depends on raylib mostly for the inputs and the drawing functionality but that dependency can be disabled +* with the config flag RAYGUI_STANDALONE. In that case is up to the user to provide another backend to cover library needs. * * CONTRIBUTORS: -* * Ramon Santamaria: Supervision, review, redesign, update and maintenance * Vlad Adrian: Complete rewrite of GuiTextBox() to support extended features (2019) * Sergio Martinez: Review, testing (2015) and redesign of multiple controls (2018) @@ -174,7 +240,7 @@ * * LICENSE: zlib/libpng * -* Copyright (c) 2014-2022 Ramon Santamaria (@raysan5) +* Copyright (c) 2014-2023 Ramon Santamaria (@raysan5) * * This software is provided "as-is", without any express or implied warranty. In no event * will the authors be held liable for any damages arising from the use of this software. @@ -196,7 +262,10 @@ #ifndef RAYGUI_H #define RAYGUI_H -#define RAYGUI_VERSION "3.2" +#define RAYGUI_VERSION_MAJOR 4 +#define RAYGUI_VERSION_MINOR 0 +#define RAYGUI_VERSION_PATCH 0 +#define RAYGUI_VERSION "4.0-dev" #if !defined(RAYGUI_STANDALONE) #include "raylib.h" @@ -291,6 +360,15 @@ int format; // Data format (PixelFormat type) } Texture2D; + // Image, pixel data stored in CPU memory (RAM) + typedef struct Image { + void *data; // Image raw data + int width; // Image base width + int height; // Image base height + int mipmaps; // Mipmap levels, 1 by default + int format; // Data format (PixelFormat type) + } Image; + // GlyphInfo, font characters glyphs info typedef struct GlyphInfo { int value; // Character value (Unicode) @@ -304,10 +382,11 @@ // It should be redesigned to be provided by user typedef struct Font { int baseSize; // Base size (default chars height) - int glyphCount; // Number of characters - Texture2D texture; // Characters texture atlas - Rectangle *recs; // Characters rectangles in texture - GlyphInfo *chars; // Characters info data + int glyphCount; // Number of glyph characters + int glyphPadding; // Padding around the glyph characters + Texture2D texture; // Texture atlas containing the glyphs + Rectangle *recs; // Rectangles in texture for the glyphs + GlyphInfo *glyphs; // Glyphs info data } Font; #endif @@ -442,6 +521,9 @@ typedef enum { typedef enum { TEXT_INNER_PADDING = 16, // TextBox/TextBoxMulti/ValueBox/Spinner inner text padding TEXT_LINES_SPACING, // TextBoxMulti lines separation + TEXT_ALIGNMENT_VERTICAL, // TextBoxMulti vertical alignment: 0-CENTERED, 1-UP, 2-DOWN + TEXT_MULTILINE, // TextBox supports multiple lines + TEXT_WRAP_MODE // TextBox wrap mode for multiline: 0-NO_WRAP, 1-CHAR_WRAP, 2-WORD_WRAP } GuiTextBoxProperty; // Spinner @@ -484,78 +566,86 @@ extern "C" { // Prevents name mangling of functions #endif // Global gui state control functions -RAYGUIAPI void GuiEnable(void); // Enable gui controls (global state) -RAYGUIAPI void GuiDisable(void); // Disable gui controls (global state) -RAYGUIAPI void GuiLock(void); // Lock gui controls (global state) -RAYGUIAPI void GuiUnlock(void); // Unlock gui controls (global state) -RAYGUIAPI bool GuiIsLocked(void); // Check if gui is locked (global state) -RAYGUIAPI void GuiFade(float alpha); // Set gui controls alpha (global state), alpha goes from 0.0f to 1.0f -RAYGUIAPI void GuiSetState(int state); // Set gui state (global state) -RAYGUIAPI int GuiGetState(void); // Get gui state (global state) +RAYGUIAPI void GuiEnable(void); // Enable gui controls (global state) +RAYGUIAPI void GuiDisable(void); // Disable gui controls (global state) +RAYGUIAPI void GuiLock(void); // Lock gui controls (global state) +RAYGUIAPI void GuiUnlock(void); // Unlock gui controls (global state) +RAYGUIAPI bool GuiIsLocked(void); // Check if gui is locked (global state) +RAYGUIAPI void GuiFade(float alpha); // Set gui controls alpha (global state), alpha goes from 0.0f to 1.0f +RAYGUIAPI void GuiSetState(int state); // Set gui state (global state) +RAYGUIAPI int GuiGetState(void); // Get gui state (global state) // Font set/get functions -RAYGUIAPI void GuiSetFont(Font font); // Set gui custom font (global state) -RAYGUIAPI Font GuiGetFont(void); // Get gui custom font (global state) +RAYGUIAPI void GuiSetFont(Font font); // Set gui custom font (global state) +RAYGUIAPI Font GuiGetFont(void); // Get gui custom font (global state) // Style set/get functions -RAYGUIAPI void GuiSetStyle(int control, int property, int value); // Set one style property -RAYGUIAPI int GuiGetStyle(int control, int property); // Get one style property - -// Container/separator controls, useful for controls organization -RAYGUIAPI bool GuiWindowBox(Rectangle bounds, const char *title); // Window Box control, shows a window that can be closed -RAYGUIAPI void GuiGroupBox(Rectangle bounds, const char *text); // Group Box control with text name -RAYGUIAPI void GuiLine(Rectangle bounds, const char *text); // Line separator control, could contain text -RAYGUIAPI void GuiPanel(Rectangle bounds, const char *text); // Panel control, useful to group controls -RAYGUIAPI Rectangle GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll); // Scroll Panel control - -// Basic controls set -RAYGUIAPI void GuiLabel(Rectangle bounds, const char *text); // Label control, shows text -RAYGUIAPI bool GuiButton(Rectangle bounds, const char *text); // Button control, returns true when clicked -RAYGUIAPI bool GuiLabelButton(Rectangle bounds, const char *text); // Label button control, show true when clicked -RAYGUIAPI bool GuiToggle(Rectangle bounds, const char *text, bool active); // Toggle Button control, returns true when active -RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int active); // Toggle Group control, returns active toggle index -RAYGUIAPI bool GuiCheckBox(Rectangle bounds, const char *text, bool checked); // Check Box control, returns true when active -RAYGUIAPI int GuiComboBox(Rectangle bounds, const char *text, int active); // Combo Box control, returns selected item index -RAYGUIAPI bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode); // Dropdown Box control, returns selected item -RAYGUIAPI bool GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Spinner control, returns selected value -RAYGUIAPI bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Value Box control, updates input text with numbers -RAYGUIAPI bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control, updates input text -RAYGUIAPI bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control with multiple lines -RAYGUIAPI float GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue); // Slider control, returns selected value -RAYGUIAPI float GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue); // Slider Bar control, returns selected value -RAYGUIAPI float GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue); // Progress Bar control, shows current progress value -RAYGUIAPI void GuiStatusBar(Rectangle bounds, const char *text); // Status Bar control, shows info text -RAYGUIAPI void GuiDummyRec(Rectangle bounds, const char *text); // Dummy control for placeholders -RAYGUIAPI Vector2 GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs); // Grid control, returns mouse cell position - -// Advance controls set -RAYGUIAPI int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int active); // List View control, returns selected list item index -RAYGUIAPI int GuiListViewEx(Rectangle bounds, const char **text, int count, int *focus, int *scrollIndex, int active); // List View with extended parameters -RAYGUIAPI int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons); // Message Box control, displays a message -RAYGUIAPI int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, int *secretViewActive); // Text Input Box control, ask for text, supports secret -RAYGUIAPI Color GuiColorPicker(Rectangle bounds, const char *text, Color color); // Color Picker control (multiple color controls) -RAYGUIAPI Color GuiColorPanel(Rectangle bounds, const char *text, Color color); // Color Panel control -RAYGUIAPI float GuiColorBarAlpha(Rectangle bounds, const char *text, float alpha); // Color Bar Alpha control -RAYGUIAPI float GuiColorBarHue(Rectangle bounds, const char *text, float value); // Color Bar Hue control +RAYGUIAPI void GuiSetStyle(int control, int property, int value); // Set one style property +RAYGUIAPI int GuiGetStyle(int control, int property); // Get one style property // Styles loading functions RAYGUIAPI void GuiLoadStyle(const char *fileName); // Load style file over global style variable (.rgs) RAYGUIAPI void GuiLoadStyleDefault(void); // Load style default over global style +// Tooltips management functions +RAYGUIAPI void GuiEnableTooltip(void); // Enable gui tooltips (global state) +RAYGUIAPI void GuiDisableTooltip(void); // Disable gui tooltips (global state) +RAYGUIAPI void GuiSetTooltip(const char *tooltip); // Set tooltip string + // Icons functionality RAYGUIAPI const char *GuiIconText(int iconId, const char *text); // Get text with icon id prepended (if supported) +#if !defined(RAYGUI_NO_ICONS) +RAYGUIAPI void GuiSetIconScale(int scale); // Set default icon drawing size +RAYGUIAPI unsigned int *GuiGetIcons(void); // Get raygui icons data pointer +RAYGUIAPI char **GuiLoadIcons(const char *fileName, bool loadIconsName); // Load raygui icons file (.rgi) into internal icons data +RAYGUIAPI void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color); // Draw icon using pixel size at specified position +#endif + + +// Controls +//---------------------------------------------------------------------------------------------------------- +// Container/separator controls, useful for controls organization +RAYGUIAPI int GuiWindowBox(Rectangle bounds, const char *title); // Window Box control, shows a window that can be closed +RAYGUIAPI int GuiGroupBox(Rectangle bounds, const char *text); // Group Box control with text name +RAYGUIAPI int GuiLine(Rectangle bounds, const char *text); // Line separator control, could contain text +RAYGUIAPI int GuiPanel(Rectangle bounds, const char *text); // Panel control, useful to group controls +RAYGUIAPI int GuiTabBar(Rectangle bounds, const char **text, int count, int *active); // Tab Bar control, returns TAB to be closed or -1 +RAYGUIAPI int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view); // Scroll Panel control + +// Basic controls set +RAYGUIAPI int GuiLabel(Rectangle bounds, const char *text); // Label control, shows text +RAYGUIAPI int GuiButton(Rectangle bounds, const char *text); // Button control, returns true when clicked +RAYGUIAPI int GuiLabelButton(Rectangle bounds, const char *text); // Label button control, show true when clicked +RAYGUIAPI int GuiToggle(Rectangle bounds, const char *text, bool *active); // Toggle Button control, returns true when active +RAYGUIAPI int GuiToggleGroup(Rectangle bounds, const char *text, int *active); // Toggle Group control, returns active toggle index +RAYGUIAPI int GuiCheckBox(Rectangle bounds, const char *text, bool *checked); // Check Box control, returns true when active +RAYGUIAPI int GuiComboBox(Rectangle bounds, const char *text, int *active); // Combo Box control, returns selected item index + +RAYGUIAPI int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode); // Dropdown Box control, returns selected item +RAYGUIAPI int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Spinner control, returns selected value +RAYGUIAPI int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode); // Value Box control, updates input text with numbers +RAYGUIAPI int GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode); // Text Box control, updates input text + +RAYGUIAPI int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider control, returns selected value +RAYGUIAPI int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Slider Bar control, returns selected value +RAYGUIAPI int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue); // Progress Bar control, shows current progress value +RAYGUIAPI int GuiStatusBar(Rectangle bounds, const char *text); // Status Bar control, shows info text +RAYGUIAPI int GuiDummyRec(Rectangle bounds, const char *text); // Dummy control for placeholders +RAYGUIAPI int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell); // Grid control, returns mouse cell position + +// Advance controls set +RAYGUIAPI int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active); // List View control, returns selected list item index +RAYGUIAPI int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus); // List View with extended parameters +RAYGUIAPI int GuiMessageBox(Rectangle bounds, const char *title, const char *message, const char *buttons); // Message Box control, displays a message +RAYGUIAPI int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, bool *secretViewActive); // Text Input Box control, ask for text, supports secret +RAYGUIAPI int GuiColorPicker(Rectangle bounds, const char *text, Color *color); // Color Picker control (multiple color controls) +RAYGUIAPI int GuiColorPanel(Rectangle bounds, const char *text, Color *color); // Color Panel control +RAYGUIAPI int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha); // Color Bar Alpha control +RAYGUIAPI int GuiColorBarHue(Rectangle bounds, const char *text, float *value); // Color Bar Hue control +//---------------------------------------------------------------------------------------------------------- + #if !defined(RAYGUI_NO_ICONS) -RAYGUIAPI void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color); - -RAYGUIAPI unsigned int *GuiGetIcons(void); // Get full icons data pointer -RAYGUIAPI unsigned int *GuiGetIconData(int iconId); // Get icon bit data -RAYGUIAPI void GuiSetIconData(int iconId, unsigned int *data); // Set icon bit data -RAYGUIAPI void GuiSetIconScale(unsigned int scale); // Set icon scale (1 by default) - -RAYGUIAPI void GuiSetIconPixel(int iconId, int x, int y); // Set icon pixel value -RAYGUIAPI void GuiClearIconPixel(int iconId, int x, int y); // Clear icon pixel value -RAYGUIAPI bool GuiCheckIconPixel(int iconId, int x, int y); // Check icon pixel value #if !defined(RAYGUI_CUSTOM_ICONS) //---------------------------------------------------------------------------------- @@ -768,20 +858,20 @@ typedef enum { ICON_FILE_NEW = 203, ICON_FOLDER_ADD = 204, ICON_ALARM = 205, - ICON_206 = 206, - ICON_207 = 207, - ICON_208 = 208, - ICON_209 = 209, - ICON_210 = 210, - ICON_211 = 211, - ICON_212 = 212, - ICON_213 = 213, - ICON_214 = 214, - ICON_215 = 215, - ICON_216 = 216, - ICON_217 = 217, - ICON_218 = 218, - ICON_219 = 219, + ICON_CPU = 206, + ICON_ROM = 207, + ICON_STEP_OVER = 208, + ICON_STEP_INTO = 209, + ICON_STEP_OUT = 210, + ICON_RESTART = 211, + ICON_BREAKPOINT_ON = 212, + ICON_BREAKPOINT_OFF = 213, + ICON_BURGER_MENU = 214, + ICON_CASE_SENSITIVE = 215, + ICON_REG_EXP = 216, + ICON_FOLDER = 217, + ICON_FILE = 218, + ICON_SAND_TIMER = 219, ICON_220 = 220, ICON_221 = 221, ICON_222 = 222, @@ -839,7 +929,7 @@ typedef enum { #include // Required for: FILE, fopen(), fclose(), fprintf(), feof(), fscanf(), vsprintf() [GuiLoadStyle(), GuiLoadIcons()] #include // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()] -#include // Required for: strlen() [GuiTextBox(), GuiTextBoxMulti(), GuiValueBox()], memset(), memcpy() +#include // Required for: strlen() [GuiTextBox(), GuiValueBox()], memset(), memcpy() #include // Required for: va_list, va_start(), vfprintf(), va_end() [TextFormat()] #include // Required for: roundf() [GuiColorPicker()] @@ -849,6 +939,11 @@ typedef enum { #define RAYGUI_CLITERAL(name) (name) #endif +// Check if two rectangles are equal, used to validate a slider bounds as an id +#ifndef CHECK_BOUNDS_ID + #define CHECK_BOUNDS_ID(src, dst) ((src.x == dst.x) && (src.y == dst.y) && (src.width == dst.width) && (src.height == dst.height)) +#endif + #if !defined(RAYGUI_NO_ICONS) && !defined(RAYGUI_CUSTOM_ICONS) // Embedded icons, no external file provided @@ -875,264 +970,267 @@ typedef enum { // guiIcons size is by default: 256*(16*16/32) = 2048*4 = 8192 bytes = 8 KB //---------------------------------------------------------------------------------- static unsigned int guiIcons[RAYGUI_ICON_MAX_ICONS*RAYGUI_ICON_DATA_ELEMENTS] = { - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_NONE - 0x3ff80000, 0x2f082008, 0x2042207e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x00007ffe, // ICON_FOLDER_FILE_OPEN - 0x3ffe0000, 0x44226422, 0x400247e2, 0x5ffa4002, 0x57ea500a, 0x500a500a, 0x40025ffa, 0x00007ffe, // ICON_FILE_SAVE_CLASSIC - 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x41024002, 0x44424282, 0x793e4102, 0x00000100, // ICON_FOLDER_OPEN - 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x41024102, 0x44424102, 0x793e4282, 0x00000000, // ICON_FOLDER_SAVE - 0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, 0x24442284, 0x21042104, 0x20042104, 0x00003ffc, // ICON_FILE_OPEN - 0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, 0x21042104, 0x22842444, 0x20042104, 0x00003ffc, // ICON_FILE_SAVE - 0x3ff00000, 0x201c2010, 0x00042004, 0x20041004, 0x20844784, 0x00841384, 0x20042784, 0x00003ffc, // ICON_FILE_EXPORT - 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x22042204, 0x22042f84, 0x20042204, 0x00003ffc, // ICON_FILE_ADD - 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x25042884, 0x25042204, 0x20042884, 0x00003ffc, // ICON_FILE_DELETE - 0x3ff00000, 0x201c2010, 0x20042004, 0x20042ff4, 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc, // ICON_FILETYPE_TEXT - 0x3ff00000, 0x201c2010, 0x27042004, 0x244424c4, 0x26442444, 0x20642664, 0x20042004, 0x00003ffc, // ICON_FILETYPE_AUDIO - 0x3ff00000, 0x201c2010, 0x26042604, 0x20042004, 0x35442884, 0x2414222c, 0x20042004, 0x00003ffc, // ICON_FILETYPE_IMAGE - 0x3ff00000, 0x201c2010, 0x20c42004, 0x22442144, 0x22442444, 0x20c42144, 0x20042004, 0x00003ffc, // ICON_FILETYPE_PLAY - 0x3ff00000, 0x3ffc2ff0, 0x3f3c2ff4, 0x3dbc2eb4, 0x3dbc2bb4, 0x3f3c2eb4, 0x3ffc2ff4, 0x00002ff4, // ICON_FILETYPE_VIDEO - 0x3ff00000, 0x201c2010, 0x21842184, 0x21842004, 0x21842184, 0x21842184, 0x20042184, 0x00003ffc, // ICON_FILETYPE_INFO - 0x0ff00000, 0x381c0810, 0x28042804, 0x28042804, 0x28042804, 0x28042804, 0x20102ffc, 0x00003ff0, // ICON_FILE_COPY - 0x00000000, 0x701c0000, 0x079c1e14, 0x55a000f0, 0x079c00f0, 0x701c1e14, 0x00000000, 0x00000000, // ICON_FILE_CUT - 0x01c00000, 0x13e41bec, 0x3f841004, 0x204420c4, 0x20442044, 0x20442044, 0x207c2044, 0x00003fc0, // ICON_FILE_PASTE - 0x00000000, 0x3aa00fe0, 0x2abc2aa0, 0x2aa42aa4, 0x20042aa4, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_CURSOR_HAND - 0x00000000, 0x003c000c, 0x030800c8, 0x30100c10, 0x10202020, 0x04400840, 0x01800280, 0x00000000, // ICON_CURSOR_POINTER - 0x00000000, 0x00180000, 0x01f00078, 0x03e007f0, 0x07c003e0, 0x04000e40, 0x00000000, 0x00000000, // ICON_CURSOR_CLASSIC - 0x00000000, 0x04000000, 0x11000a00, 0x04400a80, 0x01100220, 0x00580088, 0x00000038, 0x00000000, // ICON_PENCIL - 0x04000000, 0x15000a00, 0x50402880, 0x14102820, 0x05040a08, 0x015c028c, 0x007c00bc, 0x00000000, // ICON_PENCIL_BIG - 0x01c00000, 0x01400140, 0x01400140, 0x0ff80140, 0x0ff80808, 0x0aa80808, 0x0aa80aa8, 0x00000ff8, // ICON_BRUSH_CLASSIC - 0x1ffc0000, 0x5ffc7ffe, 0x40004000, 0x00807f80, 0x01c001c0, 0x01c001c0, 0x01c001c0, 0x00000080, // ICON_BRUSH_PAINTER - 0x00000000, 0x00800000, 0x01c00080, 0x03e001c0, 0x07f003e0, 0x036006f0, 0x000001c0, 0x00000000, // ICON_WATER_DROP - 0x00000000, 0x3e003800, 0x1f803f80, 0x0c201e40, 0x02080c10, 0x00840104, 0x00380044, 0x00000000, // ICON_COLOR_PICKER - 0x00000000, 0x07800300, 0x1fe00fc0, 0x3f883fd0, 0x0e021f04, 0x02040402, 0x00f00108, 0x00000000, // ICON_RUBBER - 0x00c00000, 0x02800140, 0x08200440, 0x20081010, 0x2ffe3004, 0x03f807fc, 0x00e001f0, 0x00000040, // ICON_COLOR_BUCKET - 0x00000000, 0x21843ffc, 0x01800180, 0x01800180, 0x01800180, 0x01800180, 0x03c00180, 0x00000000, // ICON_TEXT_T - 0x00800000, 0x01400180, 0x06200340, 0x0c100620, 0x1ff80c10, 0x380c1808, 0x70067004, 0x0000f80f, // ICON_TEXT_A - 0x78000000, 0x50004000, 0x00004800, 0x03c003c0, 0x03c003c0, 0x00100000, 0x0002000a, 0x0000000e, // ICON_SCALE - 0x75560000, 0x5e004002, 0x54001002, 0x41001202, 0x408200fe, 0x40820082, 0x40820082, 0x00006afe, // ICON_RESIZE - 0x00000000, 0x3f003f00, 0x3f003f00, 0x3f003f00, 0x00400080, 0x001c0020, 0x001c001c, 0x00000000, // ICON_FILTER_POINT - 0x6d800000, 0x00004080, 0x40804080, 0x40800000, 0x00406d80, 0x001c0020, 0x001c001c, 0x00000000, // ICON_FILTER_BILINEAR - 0x40080000, 0x1ffe2008, 0x14081008, 0x11081208, 0x10481088, 0x10081028, 0x10047ff8, 0x00001002, // ICON_CROP - 0x00100000, 0x3ffc0010, 0x2ab03550, 0x22b02550, 0x20b02150, 0x20302050, 0x2000fff0, 0x00002000, // ICON_CROP_ALPHA - 0x40000000, 0x1ff82000, 0x04082808, 0x01082208, 0x00482088, 0x00182028, 0x35542008, 0x00000002, // ICON_SQUARE_TOGGLE - 0x00000000, 0x02800280, 0x06c006c0, 0x0ea00ee0, 0x1e901eb0, 0x3e883e98, 0x7efc7e8c, 0x00000000, // ICON_SYMMETRY - 0x01000000, 0x05600100, 0x1d480d50, 0x7d423d44, 0x3d447d42, 0x0d501d48, 0x01000560, 0x00000100, // ICON_SYMMETRY_HORIZONTAL - 0x01800000, 0x04200240, 0x10080810, 0x00001ff8, 0x00007ffe, 0x0ff01ff8, 0x03c007e0, 0x00000180, // ICON_SYMMETRY_VERTICAL - 0x00000000, 0x010800f0, 0x02040204, 0x02040204, 0x07f00308, 0x1c000e00, 0x30003800, 0x00000000, // ICON_LENS - 0x00000000, 0x061803f0, 0x08240c0c, 0x08040814, 0x0c0c0804, 0x23f01618, 0x18002400, 0x00000000, // ICON_LENS_BIG - 0x00000000, 0x00000000, 0x1c7007c0, 0x638e3398, 0x1c703398, 0x000007c0, 0x00000000, 0x00000000, // ICON_EYE_ON - 0x00000000, 0x10002000, 0x04700fc0, 0x610e3218, 0x1c703098, 0x001007a0, 0x00000008, 0x00000000, // ICON_EYE_OFF - 0x00000000, 0x00007ffc, 0x40047ffc, 0x10102008, 0x04400820, 0x02800280, 0x02800280, 0x00000100, // ICON_FILTER_TOP - 0x00000000, 0x40027ffe, 0x10082004, 0x04200810, 0x02400240, 0x02400240, 0x01400240, 0x000000c0, // ICON_FILTER - 0x00800000, 0x00800080, 0x00000080, 0x3c9e0000, 0x00000000, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_POINT - 0x00800000, 0x00800080, 0x00800080, 0x3f7e01c0, 0x008001c0, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_SMALL - 0x00800000, 0x00800080, 0x03e00080, 0x3e3e0220, 0x03e00220, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_BIG - 0x01000000, 0x04400280, 0x01000100, 0x43842008, 0x43849ab2, 0x01002008, 0x04400100, 0x01000280, // ICON_TARGET_MOVE - 0x01000000, 0x04400280, 0x01000100, 0x41042108, 0x41049ff2, 0x01002108, 0x04400100, 0x01000280, // ICON_CURSOR_MOVE - 0x781e0000, 0x500a4002, 0x04204812, 0x00000240, 0x02400000, 0x48120420, 0x4002500a, 0x0000781e, // ICON_CURSOR_SCALE - 0x00000000, 0x20003c00, 0x24002800, 0x01000200, 0x00400080, 0x00140024, 0x003c0004, 0x00000000, // ICON_CURSOR_SCALE_RIGHT - 0x00000000, 0x0004003c, 0x00240014, 0x00800040, 0x02000100, 0x28002400, 0x3c002000, 0x00000000, // ICON_CURSOR_SCALE_LEFT - 0x00000000, 0x00100020, 0x10101fc8, 0x10001020, 0x10001000, 0x10001000, 0x00001fc0, 0x00000000, // ICON_UNDO - 0x00000000, 0x08000400, 0x080813f8, 0x00080408, 0x00080008, 0x00080008, 0x000003f8, 0x00000000, // ICON_REDO - 0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, 0x20402000, 0x3f902020, 0x00400020, 0x00000000, // ICON_REREDO - 0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, 0x20202000, 0x3fc82010, 0x00200010, 0x00000000, // ICON_MUTATE - 0x00000000, 0x0ff00000, 0x10081818, 0x11801008, 0x10001180, 0x18101020, 0x00100fc8, 0x00000020, // ICON_ROTATE - 0x00000000, 0x04000200, 0x240429fc, 0x20042204, 0x20442004, 0x3f942024, 0x00400020, 0x00000000, // ICON_REPEAT - 0x00000000, 0x20001000, 0x22104c0e, 0x00801120, 0x11200040, 0x4c0e2210, 0x10002000, 0x00000000, // ICON_SHUFFLE - 0x7ffe0000, 0x50024002, 0x44024802, 0x41024202, 0x40424082, 0x40124022, 0x4002400a, 0x00007ffe, // ICON_EMPTYBOX - 0x00800000, 0x03e00080, 0x08080490, 0x3c9e0808, 0x08080808, 0x03e00490, 0x00800080, 0x00000000, // ICON_TARGET - 0x00800000, 0x00800080, 0x00800080, 0x3ffe01c0, 0x008001c0, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_SMALL_FILL - 0x00800000, 0x00800080, 0x03e00080, 0x3ffe03e0, 0x03e003e0, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_BIG_FILL - 0x01000000, 0x07c00380, 0x01000100, 0x638c2008, 0x638cfbbe, 0x01002008, 0x07c00100, 0x01000380, // ICON_TARGET_MOVE_FILL - 0x01000000, 0x07c00380, 0x01000100, 0x610c2108, 0x610cfffe, 0x01002108, 0x07c00100, 0x01000380, // ICON_CURSOR_MOVE_FILL - 0x781e0000, 0x6006700e, 0x04204812, 0x00000240, 0x02400000, 0x48120420, 0x700e6006, 0x0000781e, // ICON_CURSOR_SCALE_FILL - 0x00000000, 0x38003c00, 0x24003000, 0x01000200, 0x00400080, 0x000c0024, 0x003c001c, 0x00000000, // ICON_CURSOR_SCALE_RIGHT_FILL - 0x00000000, 0x001c003c, 0x0024000c, 0x00800040, 0x02000100, 0x30002400, 0x3c003800, 0x00000000, // ICON_CURSOR_SCALE_LEFT_FILL - 0x00000000, 0x00300020, 0x10301ff8, 0x10001020, 0x10001000, 0x10001000, 0x00001fc0, 0x00000000, // ICON_UNDO_FILL - 0x00000000, 0x0c000400, 0x0c081ff8, 0x00080408, 0x00080008, 0x00080008, 0x000003f8, 0x00000000, // ICON_REDO_FILL - 0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, 0x20402000, 0x3ff02060, 0x00400060, 0x00000000, // ICON_REREDO_FILL - 0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, 0x20202000, 0x3ff82030, 0x00200030, 0x00000000, // ICON_MUTATE_FILL - 0x00000000, 0x0ff00000, 0x10081818, 0x11801008, 0x10001180, 0x18301020, 0x00300ff8, 0x00000020, // ICON_ROTATE_FILL - 0x00000000, 0x06000200, 0x26042ffc, 0x20042204, 0x20442004, 0x3ff42064, 0x00400060, 0x00000000, // ICON_REPEAT_FILL - 0x00000000, 0x30001000, 0x32107c0e, 0x00801120, 0x11200040, 0x7c0e3210, 0x10003000, 0x00000000, // ICON_SHUFFLE_FILL - 0x00000000, 0x30043ffc, 0x24042804, 0x21042204, 0x20442084, 0x20142024, 0x3ffc200c, 0x00000000, // ICON_EMPTYBOX_SMALL - 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX - 0x00000000, 0x23c43ffc, 0x23c423c4, 0x200423c4, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_TOP - 0x00000000, 0x3e043ffc, 0x3e043e04, 0x20043e04, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_TOP_RIGHT - 0x00000000, 0x20043ffc, 0x20042004, 0x3e043e04, 0x3e043e04, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_RIGHT - 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x3e042004, 0x3e043e04, 0x3ffc3e04, 0x00000000, // ICON_BOX_BOTTOM_RIGHT - 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x23c42004, 0x23c423c4, 0x3ffc23c4, 0x00000000, // ICON_BOX_BOTTOM - 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x207c2004, 0x207c207c, 0x3ffc207c, 0x00000000, // ICON_BOX_BOTTOM_LEFT - 0x00000000, 0x20043ffc, 0x20042004, 0x207c207c, 0x207c207c, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_LEFT - 0x00000000, 0x207c3ffc, 0x207c207c, 0x2004207c, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_TOP_LEFT - 0x00000000, 0x20043ffc, 0x20042004, 0x23c423c4, 0x23c423c4, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_CENTER - 0x7ffe0000, 0x40024002, 0x47e24182, 0x4ff247e2, 0x47e24ff2, 0x418247e2, 0x40024002, 0x00007ffe, // ICON_BOX_CIRCLE_MASK - 0x7fff0000, 0x40014001, 0x40014001, 0x49555ddd, 0x4945495d, 0x400149c5, 0x40014001, 0x00007fff, // ICON_POT - 0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, 0x404e40ce, 0x48125432, 0x4006540e, 0x00007ffe, // ICON_ALPHA_MULTIPLY - 0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, 0x5c4e40ce, 0x44124432, 0x40065c0e, 0x00007ffe, // ICON_ALPHA_CLEAR - 0x7ffe0000, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x00007ffe, // ICON_DITHERING - 0x07fe0000, 0x1ffa0002, 0x7fea000a, 0x402a402a, 0x5b2a512a, 0x5128552a, 0x40205128, 0x00007fe0, // ICON_MIPMAPS - 0x00000000, 0x1ff80000, 0x12481248, 0x12481ff8, 0x1ff81248, 0x12481248, 0x00001ff8, 0x00000000, // ICON_BOX_GRID - 0x12480000, 0x7ffe1248, 0x12481248, 0x12487ffe, 0x7ffe1248, 0x12481248, 0x12487ffe, 0x00001248, // ICON_GRID - 0x00000000, 0x1c380000, 0x1c3817e8, 0x08100810, 0x08100810, 0x17e81c38, 0x00001c38, 0x00000000, // ICON_BOX_CORNERS_SMALL - 0x700e0000, 0x700e5ffa, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x5ffa700e, 0x0000700e, // ICON_BOX_CORNERS_BIG - 0x3f7e0000, 0x21422142, 0x21422142, 0x00003f7e, 0x21423f7e, 0x21422142, 0x3f7e2142, 0x00000000, // ICON_FOUR_BOXES - 0x00000000, 0x3bb80000, 0x3bb83bb8, 0x3bb80000, 0x3bb83bb8, 0x3bb80000, 0x3bb83bb8, 0x00000000, // ICON_GRID_FILL - 0x7ffe0000, 0x7ffe7ffe, 0x77fe7000, 0x77fe77fe, 0x777e7700, 0x777e777e, 0x777e777e, 0x0000777e, // ICON_BOX_MULTISIZE - 0x781e0000, 0x40024002, 0x00004002, 0x01800000, 0x00000180, 0x40020000, 0x40024002, 0x0000781e, // ICON_ZOOM_SMALL - 0x781e0000, 0x40024002, 0x00004002, 0x03c003c0, 0x03c003c0, 0x40020000, 0x40024002, 0x0000781e, // ICON_ZOOM_MEDIUM - 0x781e0000, 0x40024002, 0x07e04002, 0x07e007e0, 0x07e007e0, 0x400207e0, 0x40024002, 0x0000781e, // ICON_ZOOM_BIG - 0x781e0000, 0x5ffa4002, 0x1ff85ffa, 0x1ff81ff8, 0x1ff81ff8, 0x5ffa1ff8, 0x40025ffa, 0x0000781e, // ICON_ZOOM_ALL - 0x00000000, 0x2004381c, 0x00002004, 0x00000000, 0x00000000, 0x20040000, 0x381c2004, 0x00000000, // ICON_ZOOM_CENTER - 0x00000000, 0x1db80000, 0x10081008, 0x10080000, 0x00001008, 0x10081008, 0x00001db8, 0x00000000, // ICON_BOX_DOTS_SMALL - 0x35560000, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x35562002, 0x00000000, // ICON_BOX_DOTS_BIG - 0x7ffe0000, 0x40024002, 0x48124ff2, 0x49924812, 0x48124992, 0x4ff24812, 0x40024002, 0x00007ffe, // ICON_BOX_CONCENTRIC - 0x00000000, 0x10841ffc, 0x10841084, 0x1ffc1084, 0x10841084, 0x10841084, 0x00001ffc, 0x00000000, // ICON_BOX_GRID_BIG - 0x00000000, 0x00000000, 0x10000000, 0x04000800, 0x01040200, 0x00500088, 0x00000020, 0x00000000, // ICON_OK_TICK - 0x00000000, 0x10080000, 0x04200810, 0x01800240, 0x02400180, 0x08100420, 0x00001008, 0x00000000, // ICON_CROSS - 0x00000000, 0x02000000, 0x00800100, 0x00200040, 0x00200010, 0x00800040, 0x02000100, 0x00000000, // ICON_ARROW_LEFT - 0x00000000, 0x00400000, 0x01000080, 0x04000200, 0x04000800, 0x01000200, 0x00400080, 0x00000000, // ICON_ARROW_RIGHT - 0x00000000, 0x00000000, 0x00000000, 0x08081004, 0x02200410, 0x00800140, 0x00000000, 0x00000000, // ICON_ARROW_DOWN - 0x00000000, 0x00000000, 0x01400080, 0x04100220, 0x10040808, 0x00000000, 0x00000000, 0x00000000, // ICON_ARROW_UP - 0x00000000, 0x02000000, 0x03800300, 0x03e003c0, 0x03e003f0, 0x038003c0, 0x02000300, 0x00000000, // ICON_ARROW_LEFT_FILL - 0x00000000, 0x00400000, 0x01c000c0, 0x07c003c0, 0x07c00fc0, 0x01c003c0, 0x004000c0, 0x00000000, // ICON_ARROW_RIGHT_FILL - 0x00000000, 0x00000000, 0x00000000, 0x0ff81ffc, 0x03e007f0, 0x008001c0, 0x00000000, 0x00000000, // ICON_ARROW_DOWN_FILL - 0x00000000, 0x00000000, 0x01c00080, 0x07f003e0, 0x1ffc0ff8, 0x00000000, 0x00000000, 0x00000000, // ICON_ARROW_UP_FILL - 0x00000000, 0x18a008c0, 0x32881290, 0x24822686, 0x26862482, 0x12903288, 0x08c018a0, 0x00000000, // ICON_AUDIO - 0x00000000, 0x04800780, 0x004000c0, 0x662000f0, 0x08103c30, 0x130a0e18, 0x0000318e, 0x00000000, // ICON_FX - 0x00000000, 0x00800000, 0x08880888, 0x2aaa0a8a, 0x0a8a2aaa, 0x08880888, 0x00000080, 0x00000000, // ICON_WAVE - 0x00000000, 0x00600000, 0x01080090, 0x02040108, 0x42044204, 0x24022402, 0x00001800, 0x00000000, // ICON_WAVE_SINUS - 0x00000000, 0x07f80000, 0x04080408, 0x04080408, 0x04080408, 0x7c0e0408, 0x00000000, 0x00000000, // ICON_WAVE_SQUARE - 0x00000000, 0x00000000, 0x00a00040, 0x22084110, 0x08021404, 0x00000000, 0x00000000, 0x00000000, // ICON_WAVE_TRIANGULAR - 0x00000000, 0x00000000, 0x04200000, 0x01800240, 0x02400180, 0x00000420, 0x00000000, 0x00000000, // ICON_CROSS_SMALL - 0x00000000, 0x18380000, 0x12281428, 0x10a81128, 0x112810a8, 0x14281228, 0x00001838, 0x00000000, // ICON_PLAYER_PREVIOUS - 0x00000000, 0x18000000, 0x11801600, 0x10181060, 0x10601018, 0x16001180, 0x00001800, 0x00000000, // ICON_PLAYER_PLAY_BACK - 0x00000000, 0x00180000, 0x01880068, 0x18080608, 0x06081808, 0x00680188, 0x00000018, 0x00000000, // ICON_PLAYER_PLAY - 0x00000000, 0x1e780000, 0x12481248, 0x12481248, 0x12481248, 0x12481248, 0x00001e78, 0x00000000, // ICON_PLAYER_PAUSE - 0x00000000, 0x1ff80000, 0x10081008, 0x10081008, 0x10081008, 0x10081008, 0x00001ff8, 0x00000000, // ICON_PLAYER_STOP - 0x00000000, 0x1c180000, 0x14481428, 0x15081488, 0x14881508, 0x14281448, 0x00001c18, 0x00000000, // ICON_PLAYER_NEXT - 0x00000000, 0x03c00000, 0x08100420, 0x10081008, 0x10081008, 0x04200810, 0x000003c0, 0x00000000, // ICON_PLAYER_RECORD - 0x00000000, 0x0c3007e0, 0x13c81818, 0x14281668, 0x14281428, 0x1c381c38, 0x08102244, 0x00000000, // ICON_MAGNET - 0x07c00000, 0x08200820, 0x3ff80820, 0x23882008, 0x21082388, 0x20082108, 0x1ff02008, 0x00000000, // ICON_LOCK_CLOSE - 0x07c00000, 0x08000800, 0x3ff80800, 0x23882008, 0x21082388, 0x20082108, 0x1ff02008, 0x00000000, // ICON_LOCK_OPEN - 0x01c00000, 0x0c180770, 0x3086188c, 0x60832082, 0x60034781, 0x30062002, 0x0c18180c, 0x01c00770, // ICON_CLOCK - 0x0a200000, 0x1b201b20, 0x04200e20, 0x04200420, 0x04700420, 0x0e700e70, 0x0e700e70, 0x04200e70, // ICON_TOOLS - 0x01800000, 0x3bdc318c, 0x0ff01ff8, 0x7c3e1e78, 0x1e787c3e, 0x1ff80ff0, 0x318c3bdc, 0x00000180, // ICON_GEAR - 0x01800000, 0x3ffc318c, 0x1c381ff8, 0x781e1818, 0x1818781e, 0x1ff81c38, 0x318c3ffc, 0x00000180, // ICON_GEAR_BIG - 0x00000000, 0x08080ff8, 0x08081ffc, 0x0aa80aa8, 0x0aa80aa8, 0x0aa80aa8, 0x08080aa8, 0x00000ff8, // ICON_BIN - 0x00000000, 0x00000000, 0x20043ffc, 0x08043f84, 0x04040f84, 0x04040784, 0x000007fc, 0x00000000, // ICON_HAND_POINTER - 0x00000000, 0x24400400, 0x00001480, 0x6efe0e00, 0x00000e00, 0x24401480, 0x00000400, 0x00000000, // ICON_LASER - 0x00000000, 0x03c00000, 0x08300460, 0x11181118, 0x11181118, 0x04600830, 0x000003c0, 0x00000000, // ICON_COIN - 0x00000000, 0x10880080, 0x06c00810, 0x366c07e0, 0x07e00240, 0x00001768, 0x04200240, 0x00000000, // ICON_EXPLOSION - 0x00000000, 0x3d280000, 0x2528252c, 0x3d282528, 0x05280528, 0x05e80528, 0x00000000, 0x00000000, // ICON_1UP - 0x01800000, 0x03c003c0, 0x018003c0, 0x0ff007e0, 0x0bd00bd0, 0x0a500bd0, 0x02400240, 0x02400240, // ICON_PLAYER - 0x01800000, 0x03c003c0, 0x118013c0, 0x03c81ff8, 0x07c003c8, 0x04400440, 0x0c080478, 0x00000000, // ICON_PLAYER_JUMP - 0x3ff80000, 0x30183ff8, 0x30183018, 0x3ff83ff8, 0x03000300, 0x03c003c0, 0x03e00300, 0x000003e0, // ICON_KEY - 0x3ff80000, 0x3ff83ff8, 0x33983ff8, 0x3ff83398, 0x3ff83ff8, 0x00000540, 0x0fe00aa0, 0x00000fe0, // ICON_DEMON - 0x00000000, 0x0ff00000, 0x20041008, 0x25442004, 0x10082004, 0x06000bf0, 0x00000300, 0x00000000, // ICON_TEXT_POPUP - 0x00000000, 0x11440000, 0x07f00be8, 0x1c1c0e38, 0x1c1c0c18, 0x07f00e38, 0x11440be8, 0x00000000, // ICON_GEAR_EX - 0x00000000, 0x20080000, 0x0c601010, 0x07c00fe0, 0x07c007c0, 0x0c600fe0, 0x20081010, 0x00000000, // ICON_CRACK - 0x00000000, 0x20080000, 0x0c601010, 0x04400fe0, 0x04405554, 0x0c600fe0, 0x20081010, 0x00000000, // ICON_CRACK_POINTS - 0x00000000, 0x00800080, 0x01c001c0, 0x1ffc3ffe, 0x03e007f0, 0x07f003e0, 0x0c180770, 0x00000808, // ICON_STAR - 0x0ff00000, 0x08180810, 0x08100818, 0x0a100810, 0x08180810, 0x08100818, 0x08100810, 0x00001ff8, // ICON_DOOR - 0x0ff00000, 0x08100810, 0x08100810, 0x10100010, 0x4f902010, 0x10102010, 0x08100010, 0x00000ff0, // ICON_EXIT - 0x00040000, 0x001f000e, 0x0ef40004, 0x12f41284, 0x0ef41214, 0x10040004, 0x7ffc3004, 0x10003000, // ICON_MODE_2D - 0x78040000, 0x501f600e, 0x0ef44004, 0x12f41284, 0x0ef41284, 0x10140004, 0x7ffc300c, 0x10003000, // ICON_MODE_3D - 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe, // ICON_CUBE - 0x7fe00000, 0x5ff87ff0, 0x47fe4ffc, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe, // ICON_CUBE_FACE_TOP - 0x7fe00000, 0x50386030, 0x47fe483c, 0x443e443e, 0x443e443e, 0x241e75fe, 0x0c06140e, 0x000007fe, // ICON_CUBE_FACE_LEFT - 0x7fe00000, 0x50286030, 0x47fe4804, 0x47fe47fe, 0x47fe47fe, 0x27fe77fe, 0x0ffe17fe, 0x000007fe, // ICON_CUBE_FACE_FRONT - 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x3ff27fe2, 0x0ffe1ffa, 0x000007fe, // ICON_CUBE_FACE_BOTTOM - 0x7fe00000, 0x70286030, 0x7ffe7804, 0x7c227c02, 0x7c227c22, 0x3c127de2, 0x0c061c0a, 0x000007fe, // ICON_CUBE_FACE_RIGHT - 0x7fe00000, 0x7fe87ff0, 0x7ffe7fe4, 0x7fe27fe2, 0x7fe27fe2, 0x24127fe2, 0x0c06140a, 0x000007fe, // ICON_CUBE_FACE_BACK - 0x00000000, 0x2a0233fe, 0x22022602, 0x22022202, 0x2a022602, 0x00a033fe, 0x02080110, 0x00000000, // ICON_CAMERA - 0x00000000, 0x200c3ffc, 0x000c000c, 0x3ffc000c, 0x30003000, 0x30003000, 0x3ffc3004, 0x00000000, // ICON_SPECIAL - 0x00000000, 0x0022003e, 0x012201e2, 0x0100013e, 0x01000100, 0x79000100, 0x4f004900, 0x00007800, // ICON_LINK_NET - 0x00000000, 0x44007c00, 0x45004600, 0x00627cbe, 0x00620022, 0x45007cbe, 0x44004600, 0x00007c00, // ICON_LINK_BOXES - 0x00000000, 0x0044007c, 0x0010007c, 0x3f100010, 0x3f1021f0, 0x3f100010, 0x3f0021f0, 0x00000000, // ICON_LINK_MULTI - 0x00000000, 0x0044007c, 0x00440044, 0x0010007c, 0x00100010, 0x44107c10, 0x440047f0, 0x00007c00, // ICON_LINK - 0x00000000, 0x0044007c, 0x00440044, 0x0000007c, 0x00000010, 0x44007c10, 0x44004550, 0x00007c00, // ICON_LINK_BROKE - 0x02a00000, 0x22a43ffc, 0x20042004, 0x20042ff4, 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc, // ICON_TEXT_NOTES - 0x3ffc0000, 0x20042004, 0x245e27c4, 0x27c42444, 0x2004201e, 0x201e2004, 0x20042004, 0x00003ffc, // ICON_NOTEBOOK - 0x00000000, 0x07e00000, 0x04200420, 0x24243ffc, 0x24242424, 0x24242424, 0x3ffc2424, 0x00000000, // ICON_SUITCASE - 0x00000000, 0x0fe00000, 0x08200820, 0x40047ffc, 0x7ffc5554, 0x40045554, 0x7ffc4004, 0x00000000, // ICON_SUITCASE_ZIP - 0x00000000, 0x20043ffc, 0x3ffc2004, 0x13c81008, 0x100813c8, 0x10081008, 0x1ff81008, 0x00000000, // ICON_MAILBOX - 0x00000000, 0x40027ffe, 0x5ffa5ffa, 0x5ffa5ffa, 0x40025ffa, 0x03c07ffe, 0x1ff81ff8, 0x00000000, // ICON_MONITOR - 0x0ff00000, 0x6bfe7ffe, 0x7ffe7ffe, 0x68167ffe, 0x08106816, 0x08100810, 0x0ff00810, 0x00000000, // ICON_PRINTER - 0x3ff80000, 0xfffe2008, 0x870a8002, 0x904a888a, 0x904a904a, 0x870a888a, 0xfffe8002, 0x00000000, // ICON_PHOTO_CAMERA - 0x0fc00000, 0xfcfe0cd8, 0x8002fffe, 0x84428382, 0x84428442, 0x80028382, 0xfffe8002, 0x00000000, // ICON_PHOTO_CAMERA_FLASH - 0x00000000, 0x02400180, 0x08100420, 0x20041008, 0x23c42004, 0x22442244, 0x3ffc2244, 0x00000000, // ICON_HOUSE - 0x00000000, 0x1c700000, 0x3ff83ef8, 0x3ff83ff8, 0x0fe01ff0, 0x038007c0, 0x00000100, 0x00000000, // ICON_HEART - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x80000000, 0xe000c000, // ICON_CORNER - 0x00000000, 0x14001c00, 0x15c01400, 0x15401540, 0x155c1540, 0x15541554, 0x1ddc1554, 0x00000000, // ICON_VERTICAL_BARS - 0x00000000, 0x03000300, 0x1b001b00, 0x1b601b60, 0x1b6c1b60, 0x1b6c1b6c, 0x1b6c1b6c, 0x00000000, // ICON_VERTICAL_BARS_FILL - 0x00000000, 0x00000000, 0x403e7ffe, 0x7ffe403e, 0x7ffe0000, 0x43fe43fe, 0x00007ffe, 0x00000000, // ICON_LIFE_BARS - 0x7ffc0000, 0x43844004, 0x43844284, 0x43844004, 0x42844284, 0x42844284, 0x40044384, 0x00007ffc, // ICON_INFO - 0x40008000, 0x10002000, 0x04000800, 0x01000200, 0x00400080, 0x00100020, 0x00040008, 0x00010002, // ICON_CROSSLINE - 0x00000000, 0x1ff01ff0, 0x18301830, 0x1f001830, 0x03001f00, 0x00000300, 0x03000300, 0x00000000, // ICON_HELP - 0x3ff00000, 0x2abc3550, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x00003ffc, // ICON_FILETYPE_ALPHA - 0x3ff00000, 0x201c2010, 0x22442184, 0x28142424, 0x29942814, 0x2ff42994, 0x20042004, 0x00003ffc, // ICON_FILETYPE_HOME - 0x07fe0000, 0x04020402, 0x7fe20402, 0x44224422, 0x44224422, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_LAYERS_VISIBLE - 0x07fe0000, 0x04020402, 0x7c020402, 0x44024402, 0x44024402, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_LAYERS - 0x00000000, 0x40027ffe, 0x7ffe4002, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000, // ICON_WINDOW - 0x09100000, 0x09f00910, 0x09100910, 0x00000910, 0x24a2779e, 0x27a224a2, 0x709e20a2, 0x00000000, // ICON_HIDPI - 0x3ff00000, 0x201c2010, 0x2a842e84, 0x2e842a84, 0x2ba42004, 0x2aa42aa4, 0x20042ba4, 0x00003ffc, // ICON_FILETYPE_BINARY - 0x00000000, 0x00000000, 0x00120012, 0x4a5e4bd2, 0x485233d2, 0x00004bd2, 0x00000000, 0x00000000, // ICON_HEX - 0x01800000, 0x381c0660, 0x23c42004, 0x23c42044, 0x13c82204, 0x08101008, 0x02400420, 0x00000180, // ICON_SHIELD - 0x007e0000, 0x20023fc2, 0x40227fe2, 0x400a403a, 0x400a400a, 0x400a400a, 0x4008400e, 0x00007ff8, // ICON_FILE_NEW - 0x00000000, 0x0042007e, 0x40027fc2, 0x44024002, 0x5f024402, 0x44024402, 0x7ffe4002, 0x00000000, // ICON_FOLDER_ADD - 0x44220000, 0x12482244, 0xf3cf0000, 0x14280420, 0x48122424, 0x08100810, 0x1ff81008, 0x03c00420, // ICON_ALARM - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_206 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_207 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_208 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_209 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_210 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_211 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_212 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_213 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_214 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_215 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_216 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_217 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_218 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_219 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_220 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_221 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_222 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_223 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_224 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_225 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_226 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_227 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_228 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_229 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_230 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_231 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_232 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_233 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_234 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_235 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_236 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_237 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_238 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_239 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_240 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_241 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_242 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_243 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_244 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_245 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_246 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_247 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_248 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_249 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_250 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_251 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_252 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_253 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_254 - 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_255 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_NONE + 0x3ff80000, 0x2f082008, 0x2042207e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x00007ffe, // ICON_FOLDER_FILE_OPEN + 0x3ffe0000, 0x44226422, 0x400247e2, 0x5ffa4002, 0x57ea500a, 0x500a500a, 0x40025ffa, 0x00007ffe, // ICON_FILE_SAVE_CLASSIC + 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x41024002, 0x44424282, 0x793e4102, 0x00000100, // ICON_FOLDER_OPEN + 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x41024102, 0x44424102, 0x793e4282, 0x00000000, // ICON_FOLDER_SAVE + 0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, 0x24442284, 0x21042104, 0x20042104, 0x00003ffc, // ICON_FILE_OPEN + 0x3ff00000, 0x201c2010, 0x20042004, 0x21042004, 0x21042104, 0x22842444, 0x20042104, 0x00003ffc, // ICON_FILE_SAVE + 0x3ff00000, 0x201c2010, 0x00042004, 0x20041004, 0x20844784, 0x00841384, 0x20042784, 0x00003ffc, // ICON_FILE_EXPORT + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x22042204, 0x22042f84, 0x20042204, 0x00003ffc, // ICON_FILE_ADD + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x25042884, 0x25042204, 0x20042884, 0x00003ffc, // ICON_FILE_DELETE + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042ff4, 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc, // ICON_FILETYPE_TEXT + 0x3ff00000, 0x201c2010, 0x27042004, 0x244424c4, 0x26442444, 0x20642664, 0x20042004, 0x00003ffc, // ICON_FILETYPE_AUDIO + 0x3ff00000, 0x201c2010, 0x26042604, 0x20042004, 0x35442884, 0x2414222c, 0x20042004, 0x00003ffc, // ICON_FILETYPE_IMAGE + 0x3ff00000, 0x201c2010, 0x20c42004, 0x22442144, 0x22442444, 0x20c42144, 0x20042004, 0x00003ffc, // ICON_FILETYPE_PLAY + 0x3ff00000, 0x3ffc2ff0, 0x3f3c2ff4, 0x3dbc2eb4, 0x3dbc2bb4, 0x3f3c2eb4, 0x3ffc2ff4, 0x00002ff4, // ICON_FILETYPE_VIDEO + 0x3ff00000, 0x201c2010, 0x21842184, 0x21842004, 0x21842184, 0x21842184, 0x20042184, 0x00003ffc, // ICON_FILETYPE_INFO + 0x0ff00000, 0x381c0810, 0x28042804, 0x28042804, 0x28042804, 0x28042804, 0x20102ffc, 0x00003ff0, // ICON_FILE_COPY + 0x00000000, 0x701c0000, 0x079c1e14, 0x55a000f0, 0x079c00f0, 0x701c1e14, 0x00000000, 0x00000000, // ICON_FILE_CUT + 0x01c00000, 0x13e41bec, 0x3f841004, 0x204420c4, 0x20442044, 0x20442044, 0x207c2044, 0x00003fc0, // ICON_FILE_PASTE + 0x00000000, 0x3aa00fe0, 0x2abc2aa0, 0x2aa42aa4, 0x20042aa4, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_CURSOR_HAND + 0x00000000, 0x003c000c, 0x030800c8, 0x30100c10, 0x10202020, 0x04400840, 0x01800280, 0x00000000, // ICON_CURSOR_POINTER + 0x00000000, 0x00180000, 0x01f00078, 0x03e007f0, 0x07c003e0, 0x04000e40, 0x00000000, 0x00000000, // ICON_CURSOR_CLASSIC + 0x00000000, 0x04000000, 0x11000a00, 0x04400a80, 0x01100220, 0x00580088, 0x00000038, 0x00000000, // ICON_PENCIL + 0x04000000, 0x15000a00, 0x50402880, 0x14102820, 0x05040a08, 0x015c028c, 0x007c00bc, 0x00000000, // ICON_PENCIL_BIG + 0x01c00000, 0x01400140, 0x01400140, 0x0ff80140, 0x0ff80808, 0x0aa80808, 0x0aa80aa8, 0x00000ff8, // ICON_BRUSH_CLASSIC + 0x1ffc0000, 0x5ffc7ffe, 0x40004000, 0x00807f80, 0x01c001c0, 0x01c001c0, 0x01c001c0, 0x00000080, // ICON_BRUSH_PAINTER + 0x00000000, 0x00800000, 0x01c00080, 0x03e001c0, 0x07f003e0, 0x036006f0, 0x000001c0, 0x00000000, // ICON_WATER_DROP + 0x00000000, 0x3e003800, 0x1f803f80, 0x0c201e40, 0x02080c10, 0x00840104, 0x00380044, 0x00000000, // ICON_COLOR_PICKER + 0x00000000, 0x07800300, 0x1fe00fc0, 0x3f883fd0, 0x0e021f04, 0x02040402, 0x00f00108, 0x00000000, // ICON_RUBBER + 0x00c00000, 0x02800140, 0x08200440, 0x20081010, 0x2ffe3004, 0x03f807fc, 0x00e001f0, 0x00000040, // ICON_COLOR_BUCKET + 0x00000000, 0x21843ffc, 0x01800180, 0x01800180, 0x01800180, 0x01800180, 0x03c00180, 0x00000000, // ICON_TEXT_T + 0x00800000, 0x01400180, 0x06200340, 0x0c100620, 0x1ff80c10, 0x380c1808, 0x70067004, 0x0000f80f, // ICON_TEXT_A + 0x78000000, 0x50004000, 0x00004800, 0x03c003c0, 0x03c003c0, 0x00100000, 0x0002000a, 0x0000000e, // ICON_SCALE + 0x75560000, 0x5e004002, 0x54001002, 0x41001202, 0x408200fe, 0x40820082, 0x40820082, 0x00006afe, // ICON_RESIZE + 0x00000000, 0x3f003f00, 0x3f003f00, 0x3f003f00, 0x00400080, 0x001c0020, 0x001c001c, 0x00000000, // ICON_FILTER_POINT + 0x6d800000, 0x00004080, 0x40804080, 0x40800000, 0x00406d80, 0x001c0020, 0x001c001c, 0x00000000, // ICON_FILTER_BILINEAR + 0x40080000, 0x1ffe2008, 0x14081008, 0x11081208, 0x10481088, 0x10081028, 0x10047ff8, 0x00001002, // ICON_CROP + 0x00100000, 0x3ffc0010, 0x2ab03550, 0x22b02550, 0x20b02150, 0x20302050, 0x2000fff0, 0x00002000, // ICON_CROP_ALPHA + 0x40000000, 0x1ff82000, 0x04082808, 0x01082208, 0x00482088, 0x00182028, 0x35542008, 0x00000002, // ICON_SQUARE_TOGGLE + 0x00000000, 0x02800280, 0x06c006c0, 0x0ea00ee0, 0x1e901eb0, 0x3e883e98, 0x7efc7e8c, 0x00000000, // ICON_SYMMETRY + 0x01000000, 0x05600100, 0x1d480d50, 0x7d423d44, 0x3d447d42, 0x0d501d48, 0x01000560, 0x00000100, // ICON_SYMMETRY_HORIZONTAL + 0x01800000, 0x04200240, 0x10080810, 0x00001ff8, 0x00007ffe, 0x0ff01ff8, 0x03c007e0, 0x00000180, // ICON_SYMMETRY_VERTICAL + 0x00000000, 0x010800f0, 0x02040204, 0x02040204, 0x07f00308, 0x1c000e00, 0x30003800, 0x00000000, // ICON_LENS + 0x00000000, 0x061803f0, 0x08240c0c, 0x08040814, 0x0c0c0804, 0x23f01618, 0x18002400, 0x00000000, // ICON_LENS_BIG + 0x00000000, 0x00000000, 0x1c7007c0, 0x638e3398, 0x1c703398, 0x000007c0, 0x00000000, 0x00000000, // ICON_EYE_ON + 0x00000000, 0x10002000, 0x04700fc0, 0x610e3218, 0x1c703098, 0x001007a0, 0x00000008, 0x00000000, // ICON_EYE_OFF + 0x00000000, 0x00007ffc, 0x40047ffc, 0x10102008, 0x04400820, 0x02800280, 0x02800280, 0x00000100, // ICON_FILTER_TOP + 0x00000000, 0x40027ffe, 0x10082004, 0x04200810, 0x02400240, 0x02400240, 0x01400240, 0x000000c0, // ICON_FILTER + 0x00800000, 0x00800080, 0x00000080, 0x3c9e0000, 0x00000000, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_POINT + 0x00800000, 0x00800080, 0x00800080, 0x3f7e01c0, 0x008001c0, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_SMALL + 0x00800000, 0x00800080, 0x03e00080, 0x3e3e0220, 0x03e00220, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_BIG + 0x01000000, 0x04400280, 0x01000100, 0x43842008, 0x43849ab2, 0x01002008, 0x04400100, 0x01000280, // ICON_TARGET_MOVE + 0x01000000, 0x04400280, 0x01000100, 0x41042108, 0x41049ff2, 0x01002108, 0x04400100, 0x01000280, // ICON_CURSOR_MOVE + 0x781e0000, 0x500a4002, 0x04204812, 0x00000240, 0x02400000, 0x48120420, 0x4002500a, 0x0000781e, // ICON_CURSOR_SCALE + 0x00000000, 0x20003c00, 0x24002800, 0x01000200, 0x00400080, 0x00140024, 0x003c0004, 0x00000000, // ICON_CURSOR_SCALE_RIGHT + 0x00000000, 0x0004003c, 0x00240014, 0x00800040, 0x02000100, 0x28002400, 0x3c002000, 0x00000000, // ICON_CURSOR_SCALE_LEFT + 0x00000000, 0x00100020, 0x10101fc8, 0x10001020, 0x10001000, 0x10001000, 0x00001fc0, 0x00000000, // ICON_UNDO + 0x00000000, 0x08000400, 0x080813f8, 0x00080408, 0x00080008, 0x00080008, 0x000003f8, 0x00000000, // ICON_REDO + 0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, 0x20402000, 0x3f902020, 0x00400020, 0x00000000, // ICON_REREDO + 0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, 0x20202000, 0x3fc82010, 0x00200010, 0x00000000, // ICON_MUTATE + 0x00000000, 0x0ff00000, 0x10081818, 0x11801008, 0x10001180, 0x18101020, 0x00100fc8, 0x00000020, // ICON_ROTATE + 0x00000000, 0x04000200, 0x240429fc, 0x20042204, 0x20442004, 0x3f942024, 0x00400020, 0x00000000, // ICON_REPEAT + 0x00000000, 0x20001000, 0x22104c0e, 0x00801120, 0x11200040, 0x4c0e2210, 0x10002000, 0x00000000, // ICON_SHUFFLE + 0x7ffe0000, 0x50024002, 0x44024802, 0x41024202, 0x40424082, 0x40124022, 0x4002400a, 0x00007ffe, // ICON_EMPTYBOX + 0x00800000, 0x03e00080, 0x08080490, 0x3c9e0808, 0x08080808, 0x03e00490, 0x00800080, 0x00000000, // ICON_TARGET + 0x00800000, 0x00800080, 0x00800080, 0x3ffe01c0, 0x008001c0, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_SMALL_FILL + 0x00800000, 0x00800080, 0x03e00080, 0x3ffe03e0, 0x03e003e0, 0x00800080, 0x00800080, 0x00000000, // ICON_TARGET_BIG_FILL + 0x01000000, 0x07c00380, 0x01000100, 0x638c2008, 0x638cfbbe, 0x01002008, 0x07c00100, 0x01000380, // ICON_TARGET_MOVE_FILL + 0x01000000, 0x07c00380, 0x01000100, 0x610c2108, 0x610cfffe, 0x01002108, 0x07c00100, 0x01000380, // ICON_CURSOR_MOVE_FILL + 0x781e0000, 0x6006700e, 0x04204812, 0x00000240, 0x02400000, 0x48120420, 0x700e6006, 0x0000781e, // ICON_CURSOR_SCALE_FILL + 0x00000000, 0x38003c00, 0x24003000, 0x01000200, 0x00400080, 0x000c0024, 0x003c001c, 0x00000000, // ICON_CURSOR_SCALE_RIGHT_FILL + 0x00000000, 0x001c003c, 0x0024000c, 0x00800040, 0x02000100, 0x30002400, 0x3c003800, 0x00000000, // ICON_CURSOR_SCALE_LEFT_FILL + 0x00000000, 0x00300020, 0x10301ff8, 0x10001020, 0x10001000, 0x10001000, 0x00001fc0, 0x00000000, // ICON_UNDO_FILL + 0x00000000, 0x0c000400, 0x0c081ff8, 0x00080408, 0x00080008, 0x00080008, 0x000003f8, 0x00000000, // ICON_REDO_FILL + 0x00000000, 0x3ffc0000, 0x20042004, 0x20002000, 0x20402000, 0x3ff02060, 0x00400060, 0x00000000, // ICON_REREDO_FILL + 0x00000000, 0x3ffc0000, 0x20042004, 0x27fc2004, 0x20202000, 0x3ff82030, 0x00200030, 0x00000000, // ICON_MUTATE_FILL + 0x00000000, 0x0ff00000, 0x10081818, 0x11801008, 0x10001180, 0x18301020, 0x00300ff8, 0x00000020, // ICON_ROTATE_FILL + 0x00000000, 0x06000200, 0x26042ffc, 0x20042204, 0x20442004, 0x3ff42064, 0x00400060, 0x00000000, // ICON_REPEAT_FILL + 0x00000000, 0x30001000, 0x32107c0e, 0x00801120, 0x11200040, 0x7c0e3210, 0x10003000, 0x00000000, // ICON_SHUFFLE_FILL + 0x00000000, 0x30043ffc, 0x24042804, 0x21042204, 0x20442084, 0x20142024, 0x3ffc200c, 0x00000000, // ICON_EMPTYBOX_SMALL + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX + 0x00000000, 0x23c43ffc, 0x23c423c4, 0x200423c4, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_TOP + 0x00000000, 0x3e043ffc, 0x3e043e04, 0x20043e04, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_TOP_RIGHT + 0x00000000, 0x20043ffc, 0x20042004, 0x3e043e04, 0x3e043e04, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_RIGHT + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x3e042004, 0x3e043e04, 0x3ffc3e04, 0x00000000, // ICON_BOX_BOTTOM_RIGHT + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x23c42004, 0x23c423c4, 0x3ffc23c4, 0x00000000, // ICON_BOX_BOTTOM + 0x00000000, 0x20043ffc, 0x20042004, 0x20042004, 0x207c2004, 0x207c207c, 0x3ffc207c, 0x00000000, // ICON_BOX_BOTTOM_LEFT + 0x00000000, 0x20043ffc, 0x20042004, 0x207c207c, 0x207c207c, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_LEFT + 0x00000000, 0x207c3ffc, 0x207c207c, 0x2004207c, 0x20042004, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_TOP_LEFT + 0x00000000, 0x20043ffc, 0x20042004, 0x23c423c4, 0x23c423c4, 0x20042004, 0x3ffc2004, 0x00000000, // ICON_BOX_CENTER + 0x7ffe0000, 0x40024002, 0x47e24182, 0x4ff247e2, 0x47e24ff2, 0x418247e2, 0x40024002, 0x00007ffe, // ICON_BOX_CIRCLE_MASK + 0x7fff0000, 0x40014001, 0x40014001, 0x49555ddd, 0x4945495d, 0x400149c5, 0x40014001, 0x00007fff, // ICON_POT + 0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, 0x404e40ce, 0x48125432, 0x4006540e, 0x00007ffe, // ICON_ALPHA_MULTIPLY + 0x7ffe0000, 0x53327332, 0x44ce4cce, 0x41324332, 0x5c4e40ce, 0x44124432, 0x40065c0e, 0x00007ffe, // ICON_ALPHA_CLEAR + 0x7ffe0000, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x42fe417e, 0x00007ffe, // ICON_DITHERING + 0x07fe0000, 0x1ffa0002, 0x7fea000a, 0x402a402a, 0x5b2a512a, 0x5128552a, 0x40205128, 0x00007fe0, // ICON_MIPMAPS + 0x00000000, 0x1ff80000, 0x12481248, 0x12481ff8, 0x1ff81248, 0x12481248, 0x00001ff8, 0x00000000, // ICON_BOX_GRID + 0x12480000, 0x7ffe1248, 0x12481248, 0x12487ffe, 0x7ffe1248, 0x12481248, 0x12487ffe, 0x00001248, // ICON_GRID + 0x00000000, 0x1c380000, 0x1c3817e8, 0x08100810, 0x08100810, 0x17e81c38, 0x00001c38, 0x00000000, // ICON_BOX_CORNERS_SMALL + 0x700e0000, 0x700e5ffa, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x5ffa700e, 0x0000700e, // ICON_BOX_CORNERS_BIG + 0x3f7e0000, 0x21422142, 0x21422142, 0x00003f7e, 0x21423f7e, 0x21422142, 0x3f7e2142, 0x00000000, // ICON_FOUR_BOXES + 0x00000000, 0x3bb80000, 0x3bb83bb8, 0x3bb80000, 0x3bb83bb8, 0x3bb80000, 0x3bb83bb8, 0x00000000, // ICON_GRID_FILL + 0x7ffe0000, 0x7ffe7ffe, 0x77fe7000, 0x77fe77fe, 0x777e7700, 0x777e777e, 0x777e777e, 0x0000777e, // ICON_BOX_MULTISIZE + 0x781e0000, 0x40024002, 0x00004002, 0x01800000, 0x00000180, 0x40020000, 0x40024002, 0x0000781e, // ICON_ZOOM_SMALL + 0x781e0000, 0x40024002, 0x00004002, 0x03c003c0, 0x03c003c0, 0x40020000, 0x40024002, 0x0000781e, // ICON_ZOOM_MEDIUM + 0x781e0000, 0x40024002, 0x07e04002, 0x07e007e0, 0x07e007e0, 0x400207e0, 0x40024002, 0x0000781e, // ICON_ZOOM_BIG + 0x781e0000, 0x5ffa4002, 0x1ff85ffa, 0x1ff81ff8, 0x1ff81ff8, 0x5ffa1ff8, 0x40025ffa, 0x0000781e, // ICON_ZOOM_ALL + 0x00000000, 0x2004381c, 0x00002004, 0x00000000, 0x00000000, 0x20040000, 0x381c2004, 0x00000000, // ICON_ZOOM_CENTER + 0x00000000, 0x1db80000, 0x10081008, 0x10080000, 0x00001008, 0x10081008, 0x00001db8, 0x00000000, // ICON_BOX_DOTS_SMALL + 0x35560000, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x00002002, 0x35562002, 0x00000000, // ICON_BOX_DOTS_BIG + 0x7ffe0000, 0x40024002, 0x48124ff2, 0x49924812, 0x48124992, 0x4ff24812, 0x40024002, 0x00007ffe, // ICON_BOX_CONCENTRIC + 0x00000000, 0x10841ffc, 0x10841084, 0x1ffc1084, 0x10841084, 0x10841084, 0x00001ffc, 0x00000000, // ICON_BOX_GRID_BIG + 0x00000000, 0x00000000, 0x10000000, 0x04000800, 0x01040200, 0x00500088, 0x00000020, 0x00000000, // ICON_OK_TICK + 0x00000000, 0x10080000, 0x04200810, 0x01800240, 0x02400180, 0x08100420, 0x00001008, 0x00000000, // ICON_CROSS + 0x00000000, 0x02000000, 0x00800100, 0x00200040, 0x00200010, 0x00800040, 0x02000100, 0x00000000, // ICON_ARROW_LEFT + 0x00000000, 0x00400000, 0x01000080, 0x04000200, 0x04000800, 0x01000200, 0x00400080, 0x00000000, // ICON_ARROW_RIGHT + 0x00000000, 0x00000000, 0x00000000, 0x08081004, 0x02200410, 0x00800140, 0x00000000, 0x00000000, // ICON_ARROW_DOWN + 0x00000000, 0x00000000, 0x01400080, 0x04100220, 0x10040808, 0x00000000, 0x00000000, 0x00000000, // ICON_ARROW_UP + 0x00000000, 0x02000000, 0x03800300, 0x03e003c0, 0x03e003f0, 0x038003c0, 0x02000300, 0x00000000, // ICON_ARROW_LEFT_FILL + 0x00000000, 0x00400000, 0x01c000c0, 0x07c003c0, 0x07c00fc0, 0x01c003c0, 0x004000c0, 0x00000000, // ICON_ARROW_RIGHT_FILL + 0x00000000, 0x00000000, 0x00000000, 0x0ff81ffc, 0x03e007f0, 0x008001c0, 0x00000000, 0x00000000, // ICON_ARROW_DOWN_FILL + 0x00000000, 0x00000000, 0x01c00080, 0x07f003e0, 0x1ffc0ff8, 0x00000000, 0x00000000, 0x00000000, // ICON_ARROW_UP_FILL + 0x00000000, 0x18a008c0, 0x32881290, 0x24822686, 0x26862482, 0x12903288, 0x08c018a0, 0x00000000, // ICON_AUDIO + 0x00000000, 0x04800780, 0x004000c0, 0x662000f0, 0x08103c30, 0x130a0e18, 0x0000318e, 0x00000000, // ICON_FX + 0x00000000, 0x00800000, 0x08880888, 0x2aaa0a8a, 0x0a8a2aaa, 0x08880888, 0x00000080, 0x00000000, // ICON_WAVE + 0x00000000, 0x00600000, 0x01080090, 0x02040108, 0x42044204, 0x24022402, 0x00001800, 0x00000000, // ICON_WAVE_SINUS + 0x00000000, 0x07f80000, 0x04080408, 0x04080408, 0x04080408, 0x7c0e0408, 0x00000000, 0x00000000, // ICON_WAVE_SQUARE + 0x00000000, 0x00000000, 0x00a00040, 0x22084110, 0x08021404, 0x00000000, 0x00000000, 0x00000000, // ICON_WAVE_TRIANGULAR + 0x00000000, 0x00000000, 0x04200000, 0x01800240, 0x02400180, 0x00000420, 0x00000000, 0x00000000, // ICON_CROSS_SMALL + 0x00000000, 0x18380000, 0x12281428, 0x10a81128, 0x112810a8, 0x14281228, 0x00001838, 0x00000000, // ICON_PLAYER_PREVIOUS + 0x00000000, 0x18000000, 0x11801600, 0x10181060, 0x10601018, 0x16001180, 0x00001800, 0x00000000, // ICON_PLAYER_PLAY_BACK + 0x00000000, 0x00180000, 0x01880068, 0x18080608, 0x06081808, 0x00680188, 0x00000018, 0x00000000, // ICON_PLAYER_PLAY + 0x00000000, 0x1e780000, 0x12481248, 0x12481248, 0x12481248, 0x12481248, 0x00001e78, 0x00000000, // ICON_PLAYER_PAUSE + 0x00000000, 0x1ff80000, 0x10081008, 0x10081008, 0x10081008, 0x10081008, 0x00001ff8, 0x00000000, // ICON_PLAYER_STOP + 0x00000000, 0x1c180000, 0x14481428, 0x15081488, 0x14881508, 0x14281448, 0x00001c18, 0x00000000, // ICON_PLAYER_NEXT + 0x00000000, 0x03c00000, 0x08100420, 0x10081008, 0x10081008, 0x04200810, 0x000003c0, 0x00000000, // ICON_PLAYER_RECORD + 0x00000000, 0x0c3007e0, 0x13c81818, 0x14281668, 0x14281428, 0x1c381c38, 0x08102244, 0x00000000, // ICON_MAGNET + 0x07c00000, 0x08200820, 0x3ff80820, 0x23882008, 0x21082388, 0x20082108, 0x1ff02008, 0x00000000, // ICON_LOCK_CLOSE + 0x07c00000, 0x08000800, 0x3ff80800, 0x23882008, 0x21082388, 0x20082108, 0x1ff02008, 0x00000000, // ICON_LOCK_OPEN + 0x01c00000, 0x0c180770, 0x3086188c, 0x60832082, 0x60034781, 0x30062002, 0x0c18180c, 0x01c00770, // ICON_CLOCK + 0x0a200000, 0x1b201b20, 0x04200e20, 0x04200420, 0x04700420, 0x0e700e70, 0x0e700e70, 0x04200e70, // ICON_TOOLS + 0x01800000, 0x3bdc318c, 0x0ff01ff8, 0x7c3e1e78, 0x1e787c3e, 0x1ff80ff0, 0x318c3bdc, 0x00000180, // ICON_GEAR + 0x01800000, 0x3ffc318c, 0x1c381ff8, 0x781e1818, 0x1818781e, 0x1ff81c38, 0x318c3ffc, 0x00000180, // ICON_GEAR_BIG + 0x00000000, 0x08080ff8, 0x08081ffc, 0x0aa80aa8, 0x0aa80aa8, 0x0aa80aa8, 0x08080aa8, 0x00000ff8, // ICON_BIN + 0x00000000, 0x00000000, 0x20043ffc, 0x08043f84, 0x04040f84, 0x04040784, 0x000007fc, 0x00000000, // ICON_HAND_POINTER + 0x00000000, 0x24400400, 0x00001480, 0x6efe0e00, 0x00000e00, 0x24401480, 0x00000400, 0x00000000, // ICON_LASER + 0x00000000, 0x03c00000, 0x08300460, 0x11181118, 0x11181118, 0x04600830, 0x000003c0, 0x00000000, // ICON_COIN + 0x00000000, 0x10880080, 0x06c00810, 0x366c07e0, 0x07e00240, 0x00001768, 0x04200240, 0x00000000, // ICON_EXPLOSION + 0x00000000, 0x3d280000, 0x2528252c, 0x3d282528, 0x05280528, 0x05e80528, 0x00000000, 0x00000000, // ICON_1UP + 0x01800000, 0x03c003c0, 0x018003c0, 0x0ff007e0, 0x0bd00bd0, 0x0a500bd0, 0x02400240, 0x02400240, // ICON_PLAYER + 0x01800000, 0x03c003c0, 0x118013c0, 0x03c81ff8, 0x07c003c8, 0x04400440, 0x0c080478, 0x00000000, // ICON_PLAYER_JUMP + 0x3ff80000, 0x30183ff8, 0x30183018, 0x3ff83ff8, 0x03000300, 0x03c003c0, 0x03e00300, 0x000003e0, // ICON_KEY + 0x3ff80000, 0x3ff83ff8, 0x33983ff8, 0x3ff83398, 0x3ff83ff8, 0x00000540, 0x0fe00aa0, 0x00000fe0, // ICON_DEMON + 0x00000000, 0x0ff00000, 0x20041008, 0x25442004, 0x10082004, 0x06000bf0, 0x00000300, 0x00000000, // ICON_TEXT_POPUP + 0x00000000, 0x11440000, 0x07f00be8, 0x1c1c0e38, 0x1c1c0c18, 0x07f00e38, 0x11440be8, 0x00000000, // ICON_GEAR_EX + 0x00000000, 0x20080000, 0x0c601010, 0x07c00fe0, 0x07c007c0, 0x0c600fe0, 0x20081010, 0x00000000, // ICON_CRACK + 0x00000000, 0x20080000, 0x0c601010, 0x04400fe0, 0x04405554, 0x0c600fe0, 0x20081010, 0x00000000, // ICON_CRACK_POINTS + 0x00000000, 0x00800080, 0x01c001c0, 0x1ffc3ffe, 0x03e007f0, 0x07f003e0, 0x0c180770, 0x00000808, // ICON_STAR + 0x0ff00000, 0x08180810, 0x08100818, 0x0a100810, 0x08180810, 0x08100818, 0x08100810, 0x00001ff8, // ICON_DOOR + 0x0ff00000, 0x08100810, 0x08100810, 0x10100010, 0x4f902010, 0x10102010, 0x08100010, 0x00000ff0, // ICON_EXIT + 0x00040000, 0x001f000e, 0x0ef40004, 0x12f41284, 0x0ef41214, 0x10040004, 0x7ffc3004, 0x10003000, // ICON_MODE_2D + 0x78040000, 0x501f600e, 0x0ef44004, 0x12f41284, 0x0ef41284, 0x10140004, 0x7ffc300c, 0x10003000, // ICON_MODE_3D + 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe, // ICON_CUBE + 0x7fe00000, 0x5ff87ff0, 0x47fe4ffc, 0x44224402, 0x44224422, 0x241275e2, 0x0c06140a, 0x000007fe, // ICON_CUBE_FACE_TOP + 0x7fe00000, 0x50386030, 0x47fe483c, 0x443e443e, 0x443e443e, 0x241e75fe, 0x0c06140e, 0x000007fe, // ICON_CUBE_FACE_LEFT + 0x7fe00000, 0x50286030, 0x47fe4804, 0x47fe47fe, 0x47fe47fe, 0x27fe77fe, 0x0ffe17fe, 0x000007fe, // ICON_CUBE_FACE_FRONT + 0x7fe00000, 0x50286030, 0x47fe4804, 0x44224402, 0x44224422, 0x3ff27fe2, 0x0ffe1ffa, 0x000007fe, // ICON_CUBE_FACE_BOTTOM + 0x7fe00000, 0x70286030, 0x7ffe7804, 0x7c227c02, 0x7c227c22, 0x3c127de2, 0x0c061c0a, 0x000007fe, // ICON_CUBE_FACE_RIGHT + 0x7fe00000, 0x7fe87ff0, 0x7ffe7fe4, 0x7fe27fe2, 0x7fe27fe2, 0x24127fe2, 0x0c06140a, 0x000007fe, // ICON_CUBE_FACE_BACK + 0x00000000, 0x2a0233fe, 0x22022602, 0x22022202, 0x2a022602, 0x00a033fe, 0x02080110, 0x00000000, // ICON_CAMERA + 0x00000000, 0x200c3ffc, 0x000c000c, 0x3ffc000c, 0x30003000, 0x30003000, 0x3ffc3004, 0x00000000, // ICON_SPECIAL + 0x00000000, 0x0022003e, 0x012201e2, 0x0100013e, 0x01000100, 0x79000100, 0x4f004900, 0x00007800, // ICON_LINK_NET + 0x00000000, 0x44007c00, 0x45004600, 0x00627cbe, 0x00620022, 0x45007cbe, 0x44004600, 0x00007c00, // ICON_LINK_BOXES + 0x00000000, 0x0044007c, 0x0010007c, 0x3f100010, 0x3f1021f0, 0x3f100010, 0x3f0021f0, 0x00000000, // ICON_LINK_MULTI + 0x00000000, 0x0044007c, 0x00440044, 0x0010007c, 0x00100010, 0x44107c10, 0x440047f0, 0x00007c00, // ICON_LINK + 0x00000000, 0x0044007c, 0x00440044, 0x0000007c, 0x00000010, 0x44007c10, 0x44004550, 0x00007c00, // ICON_LINK_BROKE + 0x02a00000, 0x22a43ffc, 0x20042004, 0x20042ff4, 0x20042ff4, 0x20042ff4, 0x20042004, 0x00003ffc, // ICON_TEXT_NOTES + 0x3ffc0000, 0x20042004, 0x245e27c4, 0x27c42444, 0x2004201e, 0x201e2004, 0x20042004, 0x00003ffc, // ICON_NOTEBOOK + 0x00000000, 0x07e00000, 0x04200420, 0x24243ffc, 0x24242424, 0x24242424, 0x3ffc2424, 0x00000000, // ICON_SUITCASE + 0x00000000, 0x0fe00000, 0x08200820, 0x40047ffc, 0x7ffc5554, 0x40045554, 0x7ffc4004, 0x00000000, // ICON_SUITCASE_ZIP + 0x00000000, 0x20043ffc, 0x3ffc2004, 0x13c81008, 0x100813c8, 0x10081008, 0x1ff81008, 0x00000000, // ICON_MAILBOX + 0x00000000, 0x40027ffe, 0x5ffa5ffa, 0x5ffa5ffa, 0x40025ffa, 0x03c07ffe, 0x1ff81ff8, 0x00000000, // ICON_MONITOR + 0x0ff00000, 0x6bfe7ffe, 0x7ffe7ffe, 0x68167ffe, 0x08106816, 0x08100810, 0x0ff00810, 0x00000000, // ICON_PRINTER + 0x3ff80000, 0xfffe2008, 0x870a8002, 0x904a888a, 0x904a904a, 0x870a888a, 0xfffe8002, 0x00000000, // ICON_PHOTO_CAMERA + 0x0fc00000, 0xfcfe0cd8, 0x8002fffe, 0x84428382, 0x84428442, 0x80028382, 0xfffe8002, 0x00000000, // ICON_PHOTO_CAMERA_FLASH + 0x00000000, 0x02400180, 0x08100420, 0x20041008, 0x23c42004, 0x22442244, 0x3ffc2244, 0x00000000, // ICON_HOUSE + 0x00000000, 0x1c700000, 0x3ff83ef8, 0x3ff83ff8, 0x0fe01ff0, 0x038007c0, 0x00000100, 0x00000000, // ICON_HEART + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x80000000, 0xe000c000, // ICON_CORNER + 0x00000000, 0x14001c00, 0x15c01400, 0x15401540, 0x155c1540, 0x15541554, 0x1ddc1554, 0x00000000, // ICON_VERTICAL_BARS + 0x00000000, 0x03000300, 0x1b001b00, 0x1b601b60, 0x1b6c1b60, 0x1b6c1b6c, 0x1b6c1b6c, 0x00000000, // ICON_VERTICAL_BARS_FILL + 0x00000000, 0x00000000, 0x403e7ffe, 0x7ffe403e, 0x7ffe0000, 0x43fe43fe, 0x00007ffe, 0x00000000, // ICON_LIFE_BARS + 0x7ffc0000, 0x43844004, 0x43844284, 0x43844004, 0x42844284, 0x42844284, 0x40044384, 0x00007ffc, // ICON_INFO + 0x40008000, 0x10002000, 0x04000800, 0x01000200, 0x00400080, 0x00100020, 0x00040008, 0x00010002, // ICON_CROSSLINE + 0x00000000, 0x1ff01ff0, 0x18301830, 0x1f001830, 0x03001f00, 0x00000300, 0x03000300, 0x00000000, // ICON_HELP + 0x3ff00000, 0x2abc3550, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x2aac3554, 0x00003ffc, // ICON_FILETYPE_ALPHA + 0x3ff00000, 0x201c2010, 0x22442184, 0x28142424, 0x29942814, 0x2ff42994, 0x20042004, 0x00003ffc, // ICON_FILETYPE_HOME + 0x07fe0000, 0x04020402, 0x7fe20402, 0x44224422, 0x44224422, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_LAYERS_VISIBLE + 0x07fe0000, 0x04020402, 0x7c020402, 0x44024402, 0x44024402, 0x402047fe, 0x40204020, 0x00007fe0, // ICON_LAYERS + 0x00000000, 0x40027ffe, 0x7ffe4002, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000, // ICON_WINDOW + 0x09100000, 0x09f00910, 0x09100910, 0x00000910, 0x24a2779e, 0x27a224a2, 0x709e20a2, 0x00000000, // ICON_HIDPI + 0x3ff00000, 0x201c2010, 0x2a842e84, 0x2e842a84, 0x2ba42004, 0x2aa42aa4, 0x20042ba4, 0x00003ffc, // ICON_FILETYPE_BINARY + 0x00000000, 0x00000000, 0x00120012, 0x4a5e4bd2, 0x485233d2, 0x00004bd2, 0x00000000, 0x00000000, // ICON_HEX + 0x01800000, 0x381c0660, 0x23c42004, 0x23c42044, 0x13c82204, 0x08101008, 0x02400420, 0x00000180, // ICON_SHIELD + 0x007e0000, 0x20023fc2, 0x40227fe2, 0x400a403a, 0x400a400a, 0x400a400a, 0x4008400e, 0x00007ff8, // ICON_FILE_NEW + 0x00000000, 0x0042007e, 0x40027fc2, 0x44024002, 0x5f024402, 0x44024402, 0x7ffe4002, 0x00000000, // ICON_FOLDER_ADD + 0x44220000, 0x12482244, 0xf3cf0000, 0x14280420, 0x48122424, 0x08100810, 0x1ff81008, 0x03c00420, // ICON_ALARM + 0x0aa00000, 0x1ff80aa0, 0x1068700e, 0x1008706e, 0x1008700e, 0x1008700e, 0x0aa01ff8, 0x00000aa0, // ICON_CPU + 0x07e00000, 0x04201db8, 0x04a01c38, 0x04a01d38, 0x04a01d38, 0x04a01d38, 0x04201d38, 0x000007e0, // ICON_ROM + 0x00000000, 0x03c00000, 0x3c382ff0, 0x3c04380c, 0x01800000, 0x03c003c0, 0x00000180, 0x00000000, // ICON_STEP_OVER + 0x01800000, 0x01800180, 0x01800180, 0x03c007e0, 0x00000180, 0x01800000, 0x03c003c0, 0x00000180, // ICON_STEP_INTO + 0x01800000, 0x07e003c0, 0x01800180, 0x01800180, 0x00000180, 0x01800000, 0x03c003c0, 0x00000180, // ICON_STEP_OUT + 0x00000000, 0x0ff003c0, 0x181c1c34, 0x303c301c, 0x30003000, 0x1c301800, 0x03c00ff0, 0x00000000, // ICON_RESTART + 0x00000000, 0x00000000, 0x07e003c0, 0x0ff00ff0, 0x0ff00ff0, 0x03c007e0, 0x00000000, 0x00000000, // ICON_BREAKPOINT_ON + 0x00000000, 0x00000000, 0x042003c0, 0x08100810, 0x08100810, 0x03c00420, 0x00000000, 0x00000000, // ICON_BREAKPOINT_OFF + 0x00000000, 0x00000000, 0x1ff81ff8, 0x1ff80000, 0x00001ff8, 0x1ff81ff8, 0x00000000, 0x00000000, // ICON_BURGER_MENU + 0x00000000, 0x00000000, 0x00880070, 0x0c880088, 0x1e8810f8, 0x3e881288, 0x00000000, 0x00000000, // ICON_CASE_SENSITIVE + 0x00000000, 0x02000000, 0x07000a80, 0x07001fc0, 0x02000a80, 0x00300030, 0x00000000, 0x00000000, // ICON_REG_EXP + 0x00000000, 0x0042007e, 0x40027fc2, 0x40024002, 0x40024002, 0x40024002, 0x7ffe4002, 0x00000000, // ICON_FOLDER + 0x3ff00000, 0x201c2010, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x20042004, 0x00003ffc, // ICON_FILE + 0x1ff00000, 0x20082008, 0x17d02fe8, 0x05400ba0, 0x09200540, 0x23881010, 0x2fe827c8, 0x00001ff0, // ICON_SAND_TIMER + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_220 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_221 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_222 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_223 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_224 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_225 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_226 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_227 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_228 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_229 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_230 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_231 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_232 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_233 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_234 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_235 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_236 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_237 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_238 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_239 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_240 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_241 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_242 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_243 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_244 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_245 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_246 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_247 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_248 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_249 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_250 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_251 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_252 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_253 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_254 + 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, // ICON_255 }; +// NOTE: We keep a pointer to the icons array, useful to point to other sets if required +static unsigned int *guiIconsPtr = guiIcons; + #endif // !RAYGUI_NO_ICONS && !RAYGUI_CUSTOM_ICONS #ifndef RAYGUI_ICON_SIZE @@ -1152,13 +1250,24 @@ typedef enum { BORDER = 0, BASE, TEXT, OTHER } GuiPropertyElement; //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -static GuiState guiState = STATE_NORMAL; // Gui global state, if !STATE_NORMAL, forces defined state +static GuiState guiState = STATE_NORMAL; // Gui global state, if !STATE_NORMAL, forces defined state -static Font guiFont = { 0 }; // Gui current font (WARNING: highly coupled to raylib) -static bool guiLocked = false; // Gui lock state (no inputs processed) -static float guiAlpha = 1.0f; // Gui element transpacency on drawing +static Font guiFont = { 0 }; // Gui current font (WARNING: highly coupled to raylib) +static bool guiLocked = false; // Gui lock state (no inputs processed) +static float guiAlpha = 1.0f; // Gui element transpacency on drawing -static unsigned int guiIconScale = 1; // Gui icon default scale (if icons enabled) +static unsigned int guiIconScale = 1; // Gui icon default scale (if icons enabled) + +static bool guiTooltip = false; // Tooltip enabled/disabled +static const char *guiTooltipPtr = NULL; // Tooltip string pointer (string provided by user) + +static bool guiSliderDragging = false; // Gui slider drag state (no inputs processed except dragged slider) +static Rectangle guiSliderActive = { 0 }; // Gui slider active bounds rectangle, used as an unique identifier + +static unsigned int textBoxCursorIndex = 0; // Cursor index, shared by all GuiTextBox*() +//static int blinkCursorFrameCounter = 0; // Frame counter for cursor blinking +static int autoCursorCooldownCounter = 0; // Cooldown frame counter for automatic cursor movement on key-down +static int autoCursorDelayCounter = 0; // Delay frame counter for automatic cursor movement //---------------------------------------------------------------------------------- // Style data array for all gui style properties (allocated on data segment by default) @@ -1203,13 +1312,12 @@ static bool IsMouseButtonReleased(int button); static bool IsKeyDown(int key); static bool IsKeyPressed(int key); -static int GetCharPressed(void); // -- GuiTextBox(), GuiTextBoxMulti(), GuiValueBox() +static int GetCharPressed(void); // -- GuiTextBox(), GuiValueBox() //------------------------------------------------------------------------------- // Drawing required functions //------------------------------------------------------------------------------- static void DrawRectangle(int x, int y, int width, int height, Color color); // -- GuiDrawRectangle(), GuiDrawIcon() - static void DrawRectangleGradientEx(Rectangle rec, Color col1, Color col2, Color col3, Color col4); // -- GuiColorPicker() //------------------------------------------------------------------------------- @@ -1221,9 +1329,6 @@ static Texture2D LoadTextureFromImage(Image image); // -- GuiLoadStyle() static void SetShapesTexture(Texture2D tex, Rectangle rec); // -- GuiLoadStyle() static char *LoadFileText(const char *fileName); // -- GuiLoadStyle() static const char *GetDirectoryPath(const char *filePath); // -- GuiLoadStyle() - -static Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // -- GetTextWidth(), GuiTextBoxMulti() -static void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, float spacing, Color tint); // -- GuiDrawText() //------------------------------------------------------------------------------- // raylib functions already implemented in raygui @@ -1235,7 +1340,8 @@ static bool CheckCollisionPointRec(Vector2 point, Rectangle rec); // Check if static const char *TextFormat(const char *text, ...); // Formatting of text with variables to 'embed' static const char **TextSplit(const char *text, char delimiter, int *count); // Split text into multiple strings static int TextToInteger(const char *text); // Get integer value from text -static int GetCodepoint(const char *text, int *bytesProcessed); // Get next codepoint in a UTF-8 encoded text + +static int GetCodepointNext(const char *text, int *codepointSize); // Get next codepoint in a UTF-8 encoded text static const char *CodepointToUTF8(int codepoint, int *byteSize); // Encode codepoint into UTF-8 text (char array size returned as parameter) static void DrawRectangleGradientV(int posX, int posY, int width, int height, Color color1, Color color2); // Draw rectangle vertical gradient @@ -1246,18 +1352,22 @@ static void DrawRectangleGradientV(int posX, int posY, int width, int height, Co //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- -static int GetTextWidth(const char *text); // Gui get text width using default font +static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize); // Load style from memory (binary only) + +static int GetTextWidth(const char *text); // Gui get text width using gui font and style static Rectangle GetTextBounds(int control, Rectangle bounds); // Get text bounds considering control bounds static const char *GetTextIcon(const char *text, int *iconId); // Get text icon if provided and move text cursor static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color tint); // Gui draw text using default font static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, Color color); // Gui draw rectangle using default raygui style -static const char **GuiTextSplit(const char *text, int *count, int *textRow); // Split controls text into multiple strings +static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow); // Split controls text into multiple strings static Vector3 ConvertHSVtoRGB(Vector3 hsv); // Convert color data from HSV to RGB static Vector3 ConvertRGBtoHSV(Vector3 rgb); // Convert color data from RGB to HSV static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue); // Scroll bar control, used by GuiScrollPanel() +static void GuiTooltip(Rectangle controlRec); // Draw tooltip using control rec position + //---------------------------------------------------------------------------------- // Gui Setup Functions Definition @@ -1341,7 +1451,7 @@ int GuiGetStyle(int control, int property) //---------------------------------------------------------------------------------- // Window Box control -bool GuiWindowBox(Rectangle bounds, const char *title) +int GuiWindowBox(Rectangle bounds, const char *title) { // Window title bar height (including borders) // NOTE: This define is also used by GuiMessageBox() and GuiTextInputBox() @@ -1349,8 +1459,8 @@ bool GuiWindowBox(Rectangle bounds, const char *title) #define RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT 24 #endif + int result = 0; //GuiState state = guiState; - bool clicked = false; int statusBarHeight = RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT; @@ -1379,22 +1489,23 @@ bool GuiWindowBox(Rectangle bounds, const char *title) #if defined(RAYGUI_NO_ICONS) clicked = GuiButton(closeButtonRec, "x"); #else - clicked = GuiButton(closeButtonRec, GuiIconText(ICON_CROSS_SMALL, NULL)); + result = GuiButton(closeButtonRec, GuiIconText(ICON_CROSS_SMALL, NULL)); #endif GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlignment); //-------------------------------------------------------------------- - return clicked; + return result; // Window close button clicked: result = 1 } // Group Box control with text name -void GuiGroupBox(Rectangle bounds, const char *text) +int GuiGroupBox(Rectangle bounds, const char *text) { #if !defined(RAYGUI_GROUPBOX_LINE_THICK) #define RAYGUI_GROUPBOX_LINE_THICK 1 #endif + int result = 0; GuiState state = guiState; // Draw control @@ -1405,10 +1516,12 @@ void GuiGroupBox(Rectangle bounds, const char *text) GuiLine(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2, bounds.width, (float)GuiGetStyle(DEFAULT, TEXT_SIZE) }, text); //-------------------------------------------------------------------- + + return result; } // Line control -void GuiLine(Rectangle bounds, const char *text) +int GuiLine(Rectangle bounds, const char *text) { #if !defined(RAYGUI_LINE_ORIGIN_SIZE) #define RAYGUI_LINE_MARGIN_TEXT 12 @@ -1417,6 +1530,7 @@ void GuiLine(Rectangle bounds, const char *text) #define RAYGUI_LINE_TEXT_PADDING 4 #endif + int result = 0; GuiState state = guiState; Color color = Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED : LINE_COLOR)), guiAlpha); @@ -1427,7 +1541,7 @@ void GuiLine(Rectangle bounds, const char *text) else { Rectangle textBounds = { 0 }; - textBounds.width = (float)GetTextWidth(text); + textBounds.width = (float)GetTextWidth(text) + 2; textBounds.height = bounds.height; textBounds.x = bounds.x + RAYGUI_LINE_MARGIN_TEXT; textBounds.y = bounds.y; @@ -1438,15 +1552,18 @@ void GuiLine(Rectangle bounds, const char *text) GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x + 12 + textBounds.width + 4, bounds.y + bounds.height/2, bounds.width - textBounds.width - RAYGUI_LINE_MARGIN_TEXT - RAYGUI_LINE_TEXT_PADDING, 1 }, 0, BLANK, color); } //-------------------------------------------------------------------- + + return result; } // Panel control -void GuiPanel(Rectangle bounds, const char *text) +int GuiPanel(Rectangle bounds, const char *text) { #if !defined(RAYGUI_PANEL_BORDER_WIDTH) #define RAYGUI_PANEL_BORDER_WIDTH 1 #endif + int result = 0; GuiState state = guiState; // Text will be drawn as a header bar (if provided) @@ -1467,13 +1584,91 @@ void GuiPanel(Rectangle bounds, const char *text) GuiDrawRectangle(bounds, RAYGUI_PANEL_BORDER_WIDTH, Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BORDER_COLOR_DISABLED: LINE_COLOR)), guiAlpha), Fade(GetColor(GuiGetStyle(DEFAULT, (state == STATE_DISABLED)? BASE_COLOR_DISABLED : BACKGROUND_COLOR)), guiAlpha)); //-------------------------------------------------------------------- + + return result; +} + +// Tab Bar control +// NOTE: Using GuiToggle() for the TABS +int GuiTabBar(Rectangle bounds, const char **text, int count, int *active) +{ + #define RAYGUI_TABBAR_ITEM_WIDTH 160 + + int result = -1; + GuiState state = guiState; + + Rectangle tabBounds = { bounds.x, bounds.y, RAYGUI_TABBAR_ITEM_WIDTH, bounds.height }; + + if (*active < 0) *active = 0; + else if (*active > count - 1) *active = count - 1; + + int offsetX = 0; // Required in case tabs go out of screen + offsetX = (*active + 2)*RAYGUI_TABBAR_ITEM_WIDTH - GetScreenWidth(); + if (offsetX < 0) offsetX = 0; + + bool toggle = false; // Required for individual toggles + + // Draw control + //-------------------------------------------------------------------- + for (int i = 0; i < count; i++) + { + tabBounds.x = bounds.x + (RAYGUI_TABBAR_ITEM_WIDTH + 4)*i - offsetX; + + if (tabBounds.x < GetScreenWidth()) + { + // Draw tabs as toggle controls + int textAlignment = GuiGetStyle(TOGGLE, TEXT_ALIGNMENT); + int textPadding = GuiGetStyle(TOGGLE, TEXT_PADDING); + GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); + GuiSetStyle(TOGGLE, TEXT_PADDING, 8); + + if (i == (*active)) + { + toggle = true; + GuiToggle(tabBounds, GuiIconText(12, text[i]), &toggle); + } + else + { + toggle = false; + GuiToggle(tabBounds, GuiIconText(12, text[i]), &toggle); + if (toggle) *active = i; + } + + GuiSetStyle(TOGGLE, TEXT_PADDING, textPadding); + GuiSetStyle(TOGGLE, TEXT_ALIGNMENT, textAlignment); + + // Draw tab close button + // NOTE: Only draw close button for curren tab: if (CheckCollisionPointRec(mousePoint, tabBounds)) + int tempBorderWidth = GuiGetStyle(BUTTON, BORDER_WIDTH); + int tempTextAlignment = GuiGetStyle(BUTTON, TEXT_ALIGNMENT); + GuiSetStyle(BUTTON, BORDER_WIDTH, 1); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); +#if defined(RAYGUI_NO_ICONS) + if (GuiButton(RAYGUI_CLITERAL(Rectangle){ tabBounds.x + tabBounds.width - 14 - 5, tabBounds.y + 5, 14, 14 }, "x")) result = i; +#else + if (GuiButton(RAYGUI_CLITERAL(Rectangle){ tabBounds.x + tabBounds.width - 14 - 5, tabBounds.y + 5, 14, 14 }, GuiIconText(ICON_CROSS_SMALL, NULL))) result = i; +#endif + GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlignment); + } + } + + // Draw tab-bar bottom line + GuiDrawRectangle(RAYGUI_CLITERAL(Rectangle){ bounds.x, bounds.y + bounds.height - 1, bounds.width, 1 }, 0, BLANK, GetColor(GuiGetStyle(TOGGLE, BORDER_COLOR_NORMAL))); + //-------------------------------------------------------------------- + + return result; // Return as result the current TAB closing requested } // Scroll Panel control -Rectangle GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll) +int GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Vector2 *scroll, Rectangle *view) { + int result = 0; GuiState state = guiState; + Rectangle temp = { 0 }; + if (view == NULL) view = &temp; + Vector2 scrollPos = { 0.0f, 0.0f }; if (scroll != NULL) scrollPos = *scroll; @@ -1501,17 +1696,17 @@ Rectangle GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, Rectangle verticalScrollBar = { (float)((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)bounds.x + bounds.width - verticalScrollBarWidth - GuiGetStyle(DEFAULT, BORDER_WIDTH)), (float)bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), (float)verticalScrollBarWidth, (float)bounds.height - horizontalScrollBarWidth - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) }; // Calculate view area (area without the scrollbars) - Rectangle view = (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? + *view = (GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? RAYGUI_CLITERAL(Rectangle){ bounds.x + verticalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth } : RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.y + GuiGetStyle(DEFAULT, BORDER_WIDTH), bounds.width - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - verticalScrollBarWidth, bounds.height - 2*GuiGetStyle(DEFAULT, BORDER_WIDTH) - horizontalScrollBarWidth }; // Clip view area to the actual content size - if (view.width > content.width) view.width = content.width; - if (view.height > content.height) view.height = content.height; + if (view->width > content.width) view->width = content.width; + if (view->height > content.height) view->height = content.height; float horizontalMin = hasHorizontalScrollBar? ((GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)-verticalScrollBarWidth : 0) - (float)GuiGetStyle(DEFAULT, BORDER_WIDTH) : (((float)GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)-verticalScrollBarWidth : 0) - (float)GuiGetStyle(DEFAULT, BORDER_WIDTH); float horizontalMax = hasHorizontalScrollBar? content.width - bounds.width + (float)verticalScrollBarWidth + GuiGetStyle(DEFAULT, BORDER_WIDTH) - (((float)GuiGetStyle(LISTVIEW, SCROLLBAR_SIDE) == SCROLLBAR_LEFT_SIDE)? (float)verticalScrollBarWidth : 0) : (float)-GuiGetStyle(DEFAULT, BORDER_WIDTH); - float verticalMin = hasVerticalScrollBar? 0 : -1.0f; + float verticalMin = hasVerticalScrollBar? 0.0f : -1.0f; float verticalMax = hasVerticalScrollBar? content.height - bounds.height + (float)horizontalScrollBarWidth + (float)GuiGetStyle(DEFAULT, BORDER_WIDTH) : (float)-GuiGetStyle(DEFAULT, BORDER_WIDTH); // Update control @@ -1541,8 +1736,8 @@ Rectangle GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, #endif float wheelMove = GetMouseWheelMove(); - // Horizontal scroll (Shift + Mouse wheel) - if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_RIGHT_SHIFT))) scrollPos.x += wheelMove*20; + // Horizontal scroll ((Left Control or Left Shift) + Mouse wheel) + if (hasHorizontalScrollBar && (IsKeyDown(KEY_LEFT_CONTROL) || IsKeyDown(KEY_LEFT_SHIFT))) scrollPos.x += wheelMove*20; else scrollPos.y += wheelMove*20; // Vertical scroll } } @@ -1558,7 +1753,7 @@ Rectangle GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, //-------------------------------------------------------------------- if (text != NULL) GuiStatusBar(statusBar, text); // Draw panel header as status bar - GuiDrawRectangle(bounds, 0, BLANK, GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR))); // Draw background + GuiDrawRectangle(bounds, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, BACKGROUND_COLOR)), guiAlpha)); // Draw background // Save size of the scrollbar slider const int slider = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE); @@ -1597,34 +1792,37 @@ Rectangle GuiScrollPanel(Rectangle bounds, const char *text, Rectangle content, if (scroll != NULL) *scroll = scrollPos; - return view; + return result; } // Label control -void GuiLabel(Rectangle bounds, const char *text) +int GuiLabel(Rectangle bounds, const char *text) { + int result = 0; GuiState state = guiState; // Update control //-------------------------------------------------------------------- - // ... + //... //-------------------------------------------------------------------- // Draw control //-------------------------------------------------------------------- GuiDrawText(text, GetTextBounds(LABEL, bounds), GuiGetStyle(LABEL, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); //-------------------------------------------------------------------- + + return result; } // Button control, returns true when clicked -bool GuiButton(Rectangle bounds, const char *text) +int GuiButton(Rectangle bounds, const char *text) { + int result = 0; GuiState state = guiState; - bool pressed = false; // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -1634,7 +1832,7 @@ bool GuiButton(Rectangle bounds, const char *text) if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; else state = STATE_FOCUSED; - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true; + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) result = 1; } } //-------------------------------------------------------------------- @@ -1643,24 +1841,26 @@ bool GuiButton(Rectangle bounds, const char *text) //-------------------------------------------------------------------- GuiDrawRectangle(bounds, GuiGetStyle(BUTTON, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(BUTTON, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(BUTTON, BASE + (state*3))), guiAlpha)); GuiDrawText(text, GetTextBounds(BUTTON, bounds), GuiGetStyle(BUTTON, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(BUTTON, TEXT + (state*3))), guiAlpha)); + + if (state == STATE_FOCUSED) GuiTooltip(bounds); //------------------------------------------------------------------ - return pressed; + return result; // Button pressed: result = 1 } // Label button control -bool GuiLabelButton(Rectangle bounds, const char *text) +int GuiLabelButton(Rectangle bounds, const char *text) { GuiState state = guiState; bool pressed = false; // NOTE: We force bounds.width to be all text - float textWidth = MeasureTextEx(guiFont, text, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING)).x; - if (bounds.width < textWidth) bounds.width = textWidth; + float textWidth = (float)GetTextWidth(text); + if ((bounds.width - 2*GuiGetStyle(LABEL, BORDER_WIDTH) - 2*GuiGetStyle(LABEL, TEXT_PADDING)) < textWidth) bounds.width = textWidth + 2*GuiGetStyle(LABEL, BORDER_WIDTH) + 2*GuiGetStyle(LABEL, TEXT_PADDING) + 2; // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -1684,13 +1884,17 @@ bool GuiLabelButton(Rectangle bounds, const char *text) } // Toggle Button control, returns true when active -bool GuiToggle(Rectangle bounds, const char *text, bool active) +int GuiToggle(Rectangle bounds, const char *text, bool *active) { + int result = 0; GuiState state = guiState; + bool temp = false; + if (active == NULL) active = &temp; + // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -1701,7 +1905,7 @@ bool GuiToggle(Rectangle bounds, const char *text, bool active) else if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) { state = STATE_NORMAL; - active = !active; + *active = !(*active); } else state = STATE_FOCUSED; } @@ -1712,32 +1916,40 @@ bool GuiToggle(Rectangle bounds, const char *text, bool active) //-------------------------------------------------------------------- if (state == STATE_NORMAL) { - GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, (active? BORDER_COLOR_PRESSED : (BORDER + state*3)))), guiAlpha), Fade(GetColor(GuiGetStyle(TOGGLE, (active? BASE_COLOR_PRESSED : (BASE + state*3)))), guiAlpha)); - GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, (active? TEXT_COLOR_PRESSED : (TEXT + state*3)))), guiAlpha)); + GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, ((*active)? BORDER_COLOR_PRESSED : (BORDER + state*3)))), guiAlpha), Fade(GetColor(GuiGetStyle(TOGGLE, ((*active)? BASE_COLOR_PRESSED : (BASE + state*3)))), guiAlpha)); + GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, ((*active)? TEXT_COLOR_PRESSED : (TEXT + state*3)))), guiAlpha)); } else { GuiDrawRectangle(bounds, GuiGetStyle(TOGGLE, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TOGGLE, BORDER + state*3)), guiAlpha), Fade(GetColor(GuiGetStyle(TOGGLE, BASE + state*3)), guiAlpha)); GuiDrawText(text, GetTextBounds(TOGGLE, bounds), GuiGetStyle(TOGGLE, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TOGGLE, TEXT + state*3)), guiAlpha)); } + + if (state == STATE_FOCUSED) GuiTooltip(bounds); //-------------------------------------------------------------------- - return active; + return result; } -// Toggle Group control, returns toggled button index -int GuiToggleGroup(Rectangle bounds, const char *text, int active) +// Toggle Group control, returns toggled button codepointIndex +int GuiToggleGroup(Rectangle bounds, const char *text, int *active) { #if !defined(RAYGUI_TOGGLEGROUP_MAX_ITEMS) #define RAYGUI_TOGGLEGROUP_MAX_ITEMS 32 #endif + int result = 0; float initBoundsX = bounds.x; + int temp = 0; + if (active == NULL) active = &temp; + + bool toggle = false; // Required for individual toggles + // Get substrings items from text (items pointers) int rows[RAYGUI_TOGGLEGROUP_MAX_ITEMS] = { 0 }; int itemCount = 0; - const char **items = GuiTextSplit(text, &itemCount, rows); + const char **items = GuiTextSplit(text, ';', &itemCount, rows); int prevRow = rows[0]; @@ -1750,25 +1962,38 @@ int GuiToggleGroup(Rectangle bounds, const char *text, int active) prevRow = rows[i]; } - if (i == active) GuiToggle(bounds, items[i], true); - else if (GuiToggle(bounds, items[i], false) == true) active = i; + if (i == (*active)) + { + toggle = true; + GuiToggle(bounds, items[i], &toggle); + } + else + { + toggle = false; + GuiToggle(bounds, items[i], &toggle); + if (toggle) *active = i; + } bounds.x += (bounds.width + GuiGetStyle(TOGGLE, GROUP_PADDING)); } - return active; + return result; } // Check Box control, returns true when active -bool GuiCheckBox(Rectangle bounds, const char *text, bool checked) +int GuiCheckBox(Rectangle bounds, const char *text, bool *checked) { + int result = 0; GuiState state = guiState; + bool temp = false; + if (checked == NULL) checked = &temp; + Rectangle textBounds = { 0 }; if (text != NULL) { - textBounds.width = (float)GetTextWidth(text); + textBounds.width = (float)GetTextWidth(text) + 2; textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); textBounds.x = bounds.x + bounds.width + GuiGetStyle(CHECKBOX, TEXT_PADDING); textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; @@ -1777,7 +2002,7 @@ bool GuiCheckBox(Rectangle bounds, const char *text, bool checked) // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -1794,7 +2019,7 @@ bool GuiCheckBox(Rectangle bounds, const char *text, bool checked) if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; else state = STATE_FOCUSED; - if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) checked = !checked; + if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) *checked = !(*checked); } } //-------------------------------------------------------------------- @@ -1803,7 +2028,7 @@ bool GuiCheckBox(Rectangle bounds, const char *text, bool checked) //-------------------------------------------------------------------- GuiDrawRectangle(bounds, GuiGetStyle(CHECKBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(CHECKBOX, BORDER + (state*3))), guiAlpha), BLANK); - if (checked) + if (*checked) { Rectangle check = { bounds.x + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING), bounds.y + GuiGetStyle(CHECKBOX, BORDER_WIDTH) + GuiGetStyle(CHECKBOX, CHECK_PADDING), @@ -1815,14 +2040,18 @@ bool GuiCheckBox(Rectangle bounds, const char *text, bool checked) GuiDrawText(text, textBounds, (GuiGetStyle(CHECKBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); //-------------------------------------------------------------------- - return checked; + return result; } -// Combo Box control, returns selected item index -int GuiComboBox(Rectangle bounds, const char *text, int active) +// Combo Box control, returns selected item codepointIndex +int GuiComboBox(Rectangle bounds, const char *text, int *active) { + int result = 0; GuiState state = guiState; + int temp = 0; + if (active == NULL) active = &temp; + bounds.width -= (GuiGetStyle(COMBOBOX, COMBO_BUTTON_WIDTH) + GuiGetStyle(COMBOBOX, COMBO_BUTTON_SPACING)); Rectangle selector = { (float)bounds.x + bounds.width + GuiGetStyle(COMBOBOX, COMBO_BUTTON_SPACING), @@ -1830,14 +2059,14 @@ int GuiComboBox(Rectangle bounds, const char *text, int active) // Get substrings items from text (items pointers, lengths and count) int itemCount = 0; - const char **items = GuiTextSplit(text, &itemCount, NULL); + const char **items = GuiTextSplit(text, ';', &itemCount, NULL); - if (active < 0) active = 0; - else if (active > itemCount - 1) active = itemCount - 1; + if (*active < 0) *active = 0; + else if (*active > (itemCount - 1)) *active = itemCount - 1; // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1)) + if ((state != STATE_DISABLED) && !guiLocked && (itemCount > 1) && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -1846,8 +2075,8 @@ int GuiComboBox(Rectangle bounds, const char *text, int active) { if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { - active += 1; - if (active >= itemCount) active = 0; + *active += 1; + if (*active >= itemCount) *active = 0; // Cyclic combobox } if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) state = STATE_PRESSED; @@ -1860,7 +2089,7 @@ int GuiComboBox(Rectangle bounds, const char *text, int active) //-------------------------------------------------------------------- // Draw combo box main GuiDrawRectangle(bounds, GuiGetStyle(COMBOBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COMBOBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(COMBOBOX, BASE + (state*3))), guiAlpha)); - GuiDrawText(items[active], GetTextBounds(COMBOBOX, bounds), GuiGetStyle(COMBOBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(COMBOBOX, TEXT + (state*3))), guiAlpha)); + GuiDrawText(items[*active], GetTextBounds(COMBOBOX, bounds), GuiGetStyle(COMBOBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(COMBOBOX, TEXT + (state*3))), guiAlpha)); // Draw selector using a custom button // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values @@ -1869,37 +2098,37 @@ int GuiComboBox(Rectangle bounds, const char *text, int active) GuiSetStyle(BUTTON, BORDER_WIDTH, 1); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); - GuiButton(selector, TextFormat("%i/%i", active + 1, itemCount)); + GuiButton(selector, TextFormat("%i/%i", *active + 1, itemCount)); GuiSetStyle(BUTTON, TEXT_ALIGNMENT, tempTextAlign); GuiSetStyle(BUTTON, BORDER_WIDTH, tempBorderWidth); //-------------------------------------------------------------------- - return active; + return result; } // Dropdown Box control // NOTE: Returns mouse click -bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode) +int GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMode) { + int result = 0; GuiState state = guiState; + int itemSelected = *active; int itemFocused = -1; // Get substrings items from text (items pointers, lengths and count) int itemCount = 0; - const char **items = GuiTextSplit(text, &itemCount, NULL); + const char **items = GuiTextSplit(text, ';', &itemCount, NULL); Rectangle boundsOpen = bounds; boundsOpen.height = (itemCount + 1)*(bounds.height + GuiGetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING)); Rectangle itemBounds = bounds; - bool pressed = false; // Check mouse button pressed - // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1)) + if ((state != STATE_DISABLED) && (editMode || !guiLocked) && (itemCount > 1) && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -1910,11 +2139,11 @@ bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMo // Check if mouse has been pressed or released outside limits if (!CheckCollisionPointRec(mousePoint, boundsOpen)) { - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) pressed = true; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON) || IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) result = 1; } // Check if already selected item has been pressed again - if (CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true; + if (CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1; // Check focused and selected item for (int i = 0; i < itemCount; i++) @@ -1928,7 +2157,7 @@ bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMo if (IsMouseButtonReleased(MOUSE_LEFT_BUTTON)) { itemSelected = i; - pressed = true; // Item selected, change to editMode = false + result = 1; // Item selected } break; } @@ -1942,7 +2171,7 @@ bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMo { if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { - pressed = true; + result = 1; state = STATE_PRESSED; } else state = STATE_FOCUSED; @@ -1991,29 +2220,68 @@ bool GuiDropdownBox(Rectangle bounds, const char *text, int *active, bool editMo //-------------------------------------------------------------------- *active = itemSelected; - return pressed; + + // TODO: Use result to return more internal states: mouse-press out-of-bounds, mouse-press over selected-item... + return result; // Mouse click: result = 1 } -// Text Box control, updates input text -// NOTE 2: Returns if KEY_ENTER pressed (useful for data validation) -bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) +// Text Box control +// NOTE: Returns true on ENTER pressed (useful for data validation) +int GuiTextBox(Rectangle bounds, char *text, int bufferSize, bool editMode) { - GuiState state = guiState; - bool pressed = false; + #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN) + #define RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN 40 // Frames to wait for autocursor movement + #endif + #if !defined(RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) + #define RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY 1 // Frames delay for autocursor movement + #endif + int result = 0; + GuiState state = guiState; + + Rectangle textBounds = GetTextBounds(TEXTBOX, bounds); + int textWidth = GetTextWidth(text) - GetTextWidth(text + textBoxCursorIndex); + int textIndexOffset = 0; // Text index offset to start drawing in the box + + int alignmentVertical = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL); + int multiline = GuiGetStyle(TEXTBOX, TEXT_MULTILINE); + + // Cursor rectangle + // NOTE: Position X value should be updated Rectangle cursor = { - bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GetTextWidth(text) + 2, - bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE), - 4, + textBounds.x + textWidth + GuiGetStyle(DEFAULT, TEXT_SPACING), + textBounds.y + textBounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE), + 2, (float)GuiGetStyle(DEFAULT, TEXT_SIZE)*2 }; + switch (alignmentVertical) + { + case 0: cursor.y = textBounds.y + textBounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE); break; // CENTERED + case 1: cursor.y = textBounds.y - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; break; // UP + case 2: cursor.y = textBounds.y + textBounds.height; break; // DOWN + default: break; + } + if (cursor.height >= bounds.height) cursor.height = bounds.height - GuiGetStyle(TEXTBOX, BORDER_WIDTH)*2; if (cursor.y < (bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH))) cursor.y = bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH); + // Auto-cursor movement logic + // NOTE: Cursor moves automatically when key down after some time + if (IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_UP) || IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_BACKSPACE) || IsKeyDown(KEY_DELETE)) autoCursorCooldownCounter++; + else + { + autoCursorCooldownCounter = 0; // GLOBAL: Cursor cooldown counter + autoCursorDelayCounter = 0; // GLOBAL: Cursor delay counter + } + + // Blink-cursor frame counter + //if (!autoCursorMode) blinkCursorFrameCounter++; + //else blinkCursorFrameCounter = 0; + // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -2021,51 +2289,158 @@ bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) { state = STATE_PRESSED; - int key = GetCharPressed(); // Returns codepoint as Unicode - int keyCount = (int)strlen(text); - int byteSize = 0; - const char *textUTF8 = CodepointToUTF8(key, &byteSize); - - // Only allow keys in range [32..125] - if ((keyCount + byteSize) < textSize) + // If text does not fit in the textbox and current cursor position is out of bounds, + // we add an index offset to text for drawing only what requires depending on cursor + while (textWidth >= textBounds.width) { - float maxWidth = (bounds.width - (GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING)*2)); + int nextCodepointSize = 0; + GetCodepointNext(text + textIndexOffset, &nextCodepointSize); - if ((GetTextWidth(text) < (maxWidth - GuiGetStyle(DEFAULT, TEXT_SIZE))) && (key >= 32)) + textIndexOffset += nextCodepointSize; + + textWidth = GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex); + } + + unsigned int textLength = (unsigned int)strlen(text); // Get current text length + int codepoint = GetCharPressed(); // Get Unicode codepoint + if (multiline && IsKeyPressed(KEY_ENTER)) codepoint = (int)'\n'; + + if (textBoxCursorIndex > textLength) textBoxCursorIndex = textLength; + + // Encode codepoint as UTF-8 + int codepointSize = 0; + const char *charEncoded = CodepointToUTF8(codepoint, &codepointSize); + + // Add codepoint to text, at current cursor position + // NOTE: Make sure we do not overflow buffer size + if (((multiline && (codepoint == (int)'\n')) || (codepoint >= 32)) && ((textLength + codepointSize) < bufferSize)) + { + // Move forward data from cursor position + for (int i = (textLength + codepointSize); i > textBoxCursorIndex; i--) text[i] = text[i - codepointSize]; + + // Add new codepoint in current cursor position + for (int i = 0; i < codepointSize; i++) text[textBoxCursorIndex + i] = charEncoded[i]; + + textBoxCursorIndex += codepointSize; + textLength += codepointSize; + + // Make sure text last character is EOL + text[textLength] = '\0'; + } + + // Move cursor to start + if ((textLength > 0) && IsKeyPressed(KEY_HOME)) + { + textBoxCursorIndex = 0; + } + + // Move cursor to end + if ((textLength > textBoxCursorIndex) && IsKeyPressed(KEY_END)) + { + textBoxCursorIndex = textLength; + } + + // Delete codepoint from text, after current cursor position + if ((textLength > textBoxCursorIndex) && (IsKeyPressed(KEY_DELETE) || (IsKeyDown(KEY_DELETE) && (autoCursorCooldownCounter >= RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)))) + { + autoCursorDelayCounter++; + + if (IsKeyPressed(KEY_DELETE) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0) // Delay every movement some frames { - for (int i = 0; i < byteSize; i++) + int nextCodepointSize = 0; + GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize); + + // Move backward text from cursor position + for (int i = textBoxCursorIndex; i < textLength; i++) text[i] = text[i + nextCodepointSize]; + + textLength -= codepointSize; + + // Make sure text last character is EOL + text[textLength] = '\0'; + } + } + + // Delete codepoint from text, before current cursor position + if ((textLength > 0) && (IsKeyPressed(KEY_BACKSPACE) || (IsKeyDown(KEY_BACKSPACE) && (autoCursorCooldownCounter >= RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN)))) + { + autoCursorDelayCounter++; + + if (IsKeyPressed(KEY_BACKSPACE) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0) // Delay every movement some frames + { + int prevCodepointSize = 0; + GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize); + + // Move backward text from cursor position + for (int i = (textBoxCursorIndex - prevCodepointSize); i < textLength; i++) text[i] = text[i + prevCodepointSize]; + + // Prevent cursor index from decrementing past 0 + if (textBoxCursorIndex > 0) { - text[keyCount] = textUTF8[i]; - keyCount++; + textBoxCursorIndex -= codepointSize; + textLength -= codepointSize; } - text[keyCount] = '\0'; + // Make sure text last character is EOL + text[textLength] = '\0'; } } - // Delete text - if (keyCount > 0) + // Move cursor position with keys + //if (IsKeyDown(KEY_LEFT) && autoCursorMode) + if (IsKeyPressed(KEY_LEFT) || (IsKeyDown(KEY_LEFT) && (autoCursorCooldownCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN))) { - if (IsKeyPressed(KEY_BACKSPACE)) + autoCursorDelayCounter++; + + if (IsKeyPressed(KEY_LEFT) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0) // Delay every movement some frames { - while ((keyCount > 0) && ((text[--keyCount] & 0xc0) == 0x80)); - text[keyCount] = '\0'; + int prevCodepointSize = 0; + GetCodepointPrevious(text + textBoxCursorIndex, &prevCodepointSize); + + if (textBoxCursorIndex >= prevCodepointSize) textBoxCursorIndex -= prevCodepointSize; + } + } + else if (IsKeyPressed(KEY_RIGHT) || (IsKeyDown(KEY_RIGHT) && (autoCursorCooldownCounter > RAYGUI_TEXTBOX_AUTO_CURSOR_COOLDOWN))) + { + autoCursorDelayCounter++; + + if (IsKeyPressed(KEY_RIGHT) || (autoCursorDelayCounter%RAYGUI_TEXTBOX_AUTO_CURSOR_DELAY) == 0) // Delay every movement some frames + { + int nextCodepointSize = 0; + GetCodepointNext(text + textBoxCursorIndex, &nextCodepointSize); + + if ((textBoxCursorIndex + nextCodepointSize) <= textLength) textBoxCursorIndex += nextCodepointSize; } } - if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) pressed = true; + // TODO: Get cursor rectangle from mouse position + //cursor = GetCursorFromMousePosition(bounds, text, mouse); // Gui style considered internally, including wrapMode - // Check text alignment to position cursor properly - int textAlignment = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT); - if (textAlignment == TEXT_ALIGN_CENTER) cursor.x = bounds.x + GetTextWidth(text)/2 + bounds.width/2 + 1; - else if (textAlignment == TEXT_ALIGN_RIGHT) cursor.x = bounds.x + bounds.width - GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING); + // TODO: Get cursor rectangle from buffer index + //cursor = GetCursorFromIndex(bounds, text, index); // Gui style considered internally, including wrapMode + + // Recalculate cursor position.y depending on textBoxCursorIndex + cursor.x = bounds.x + GuiGetStyle(TEXTBOX, TEXT_PADDING) + GetTextWidth(text + textIndexOffset) - GetTextWidth(text + textBoxCursorIndex) + GuiGetStyle(DEFAULT, TEXT_SPACING); + //if (multiline) cursor.y = GetTextLines() + + // Finish text editing on ENTER (if not multiline mode) or mouse click outside bounds + if ((!multiline && IsKeyPressed(KEY_ENTER)) || + (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) + { + textBoxCursorIndex = 0; // GLOBAL: Reset the shared cursor index + result = 1; + } } else { if (CheckCollisionPointRec(mousePoint, bounds)) { state = STATE_FOCUSED; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true; + + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) + { + textBoxCursorIndex = (int)strlen(text); // GLOBAL: Place cursor index to the end of current text + result = 1; + } } } } @@ -2081,23 +2456,51 @@ bool GuiTextBox(Rectangle bounds, char *text, int textSize, bool editMode) { GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha)); } - else GuiDrawRectangle(bounds, 1, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), BLANK); + else GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), BLANK); - GuiDrawText(text, GetTextBounds(TEXTBOX, bounds), GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha)); + // Draw text considering index offset if required + // NOTE: Text index offset depends on cursor position + GuiDrawText(text + textIndexOffset, textBounds, GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha)); // Draw cursor - if (editMode) GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha)); + if (editMode) + { + //if (autoCursorMode || ((blinkCursorFrameCounter/40)%2 == 0)) + GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha)); + } + else if (state == STATE_FOCUSED) GuiTooltip(bounds); //-------------------------------------------------------------------- + return result; // Mouse button pressed: result = 1 +} + +/* +// Text Box control with multiple lines +// NOTE: It's a regular GuiTextBox() but enabling multiline support, +// unfortunately cursor placement is not working properly so the function is removed +bool GuiTextBoxMulti(Rectangle bounds, char *text, int bufferSize, bool editMode) +{ + bool pressed = false; + + GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL, 1); + GuiSetStyle(TEXTBOX, TEXT_MULTILINE, 1); + + // TODO: Implement methods to calculate cursor position properly + pressed = GuiTextBox(bounds, text, bufferSize, editMode); + + GuiSetStyle(TEXTBOX, TEXT_MULTILINE, 0); + GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL, 0); + return pressed; } +*/ // Spinner control, returns selected value -bool GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode) +int GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode) { + int result = 1; GuiState state = guiState; - bool pressed = false; int tempValue = *value; Rectangle spinner = { bounds.x + GuiGetStyle(SPINNER, SPIN_BUTTON_WIDTH) + GuiGetStyle(SPINNER, SPIN_BUTTON_SPACING), bounds.y, @@ -2108,7 +2511,7 @@ bool GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, in Rectangle textBounds = { 0 }; if (text != NULL) { - textBounds.width = (float)GetTextWidth(text); + textBounds.width = (float)GetTextWidth(text) + 2; textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); textBounds.x = bounds.x + bounds.width + GuiGetStyle(SPINNER, TEXT_PADDING); textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; @@ -2117,7 +2520,7 @@ bool GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, in // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -2146,8 +2549,7 @@ bool GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, in // Draw control //-------------------------------------------------------------------- - // TODO: Set Spinner properties for ValueBox - pressed = GuiValueBox(spinner, NULL, &tempValue, minValue, maxValue, editMode); + result = GuiValueBox(spinner, NULL, &tempValue, minValue, maxValue, editMode); // Draw value selector custom buttons // NOTE: BORDER_WIDTH and TEXT_ALIGNMENT forced values @@ -2164,19 +2566,19 @@ bool GuiSpinner(Rectangle bounds, const char *text, int *value, int minValue, in //-------------------------------------------------------------------- *value = tempValue; - return pressed; + return result; } // Value Box control, updates input text with numbers // NOTE: Requires static variables: frameCounter -bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode) +int GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, int maxValue, bool editMode) { #if !defined(RAYGUI_VALUEBOX_MAX_CHARS) #define RAYGUI_VALUEBOX_MAX_CHARS 32 #endif + int result = 0; GuiState state = guiState; - bool pressed = false; char textValue[RAYGUI_VALUEBOX_MAX_CHARS + 1] = "\0"; sprintf(textValue, "%i", *value); @@ -2184,7 +2586,7 @@ bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, i Rectangle textBounds = { 0 }; if (text != NULL) { - textBounds.width = (float)GetTextWidth(text); + textBounds.width = (float)GetTextWidth(text) + 2; textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); textBounds.x = bounds.x + bounds.width + GuiGetStyle(VALUEBOX, TEXT_PADDING); textBounds.y = bounds.y + bounds.height/2 - GuiGetStyle(DEFAULT, TEXT_SIZE)/2; @@ -2193,7 +2595,7 @@ bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, i // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -2237,7 +2639,7 @@ bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, i //if (*value > maxValue) *value = maxValue; //else if (*value < minValue) *value = minValue; - if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) pressed = true; + if (IsKeyPressed(KEY_ENTER) || (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON))) result = 1; } else { @@ -2247,7 +2649,7 @@ bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, i if (CheckCollisionPointRec(mousePoint, bounds)) { state = STATE_FOCUSED; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true; + if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) result = 1; } } } @@ -2267,7 +2669,7 @@ bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, i if (editMode) { // NOTE: ValueBox internal text is always centered - Rectangle cursor = { bounds.x + GetTextWidth(textValue)/2 + bounds.width/2 + 2, bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4, bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH) }; + Rectangle cursor = { bounds.x + GetTextWidth(textValue)/2 + bounds.width/2 + 1, bounds.y + 2*GuiGetStyle(VALUEBOX, BORDER_WIDTH), 4, bounds.height - 4*GuiGetStyle(VALUEBOX, BORDER_WIDTH) }; GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(VALUEBOX, BORDER_COLOR_PRESSED)), guiAlpha)); } @@ -2275,190 +2677,20 @@ bool GuiValueBox(Rectangle bounds, const char *text, int *value, int minValue, i GuiDrawText(text, textBounds, (GuiGetStyle(VALUEBOX, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT)? TEXT_ALIGN_LEFT : TEXT_ALIGN_RIGHT, Fade(GetColor(GuiGetStyle(LABEL, TEXT + (state*3))), guiAlpha)); //-------------------------------------------------------------------- - return pressed; -} - -// Text Box control with multiple lines -bool GuiTextBoxMulti(Rectangle bounds, char *text, int textSize, bool editMode) -{ - GuiState state = guiState; - bool pressed = false; - - Rectangle textAreaBounds = { - bounds.x + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING), - bounds.y + GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING), - bounds.width - 2*(GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING)), - bounds.height - 2*(GuiGetStyle(TEXTBOX, BORDER_WIDTH) + GuiGetStyle(TEXTBOX, TEXT_INNER_PADDING)) - }; - - // Cursor position, [x, y] values should be updated - Rectangle cursor = { 0, -1, 4, (float)GuiGetStyle(DEFAULT, TEXT_SIZE) + 2 }; - - float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/(float)guiFont.baseSize; // Character rectangle scaling factor - - // Update control - //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) - { - Vector2 mousePoint = GetMousePosition(); - - if (editMode) - { - state = STATE_PRESSED; - - // We get an Unicode codepoint - int codepoint = GetCharPressed(); - int textLength = (int)strlen(text); // Length in bytes (UTF-8 string) - - // Introduce characters - if (textLength < (textSize - 1)) - { - if (IsKeyPressed(KEY_ENTER)) - { - text[textLength] = '\n'; - textLength++; - } - else if (codepoint >= 32) - { - // Supports Unicode inputs -> Encoded to UTF-8 - int charUTF8Length = 0; - const char *charEncoded = CodepointToUTF8(codepoint, &charUTF8Length); - memcpy(text + textLength, charEncoded, charUTF8Length); - textLength += charUTF8Length; - } - } - - // Delete characters - if (textLength > 0) - { - if (IsKeyPressed(KEY_BACKSPACE)) - { - if ((unsigned char)text[textLength - 1] < 127) - { - // Remove ASCII equivalent character (1 byte) - textLength--; - text[textLength] = '\0'; - } - else - { - // Remove latest UTF-8 unicode character introduced (n bytes) - int charUTF8Length = 0; - while (((unsigned char)text[textLength - 1 - charUTF8Length] & 0b01000000) == 0) charUTF8Length++; - - textLength -= (charUTF8Length + 1); - text[textLength] = '\0'; - } - } - } - - // Exit edit mode - if (!CheckCollisionPointRec(mousePoint, bounds) && IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true; - } - else - { - if (CheckCollisionPointRec(mousePoint, bounds)) - { - state = STATE_FOCUSED; - if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) pressed = true; - } - } - } - //-------------------------------------------------------------------- - - // Draw control - //-------------------------------------------------------------------- - if (state == STATE_PRESSED) - { - GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_PRESSED)), guiAlpha)); - } - else if (state == STATE_DISABLED) - { - GuiDrawRectangle(bounds, GuiGetStyle(TEXTBOX, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), Fade(GetColor(GuiGetStyle(TEXTBOX, BASE_COLOR_DISABLED)), guiAlpha)); - } - else GuiDrawRectangle(bounds, 1, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER + (state*3))), guiAlpha), BLANK); - - int wrapMode = 1; // 0-No wrap, 1-Char wrap, 2-Word wrap - Vector2 cursorPos = { textAreaBounds.x, textAreaBounds.y }; - - //int lastSpacePos = 0; - //int lastSpaceWidth = 0; - //int lastSpaceCursorPos = 0; - - for (int i = 0, codepointLength = 0; text[i] != '\0'; i += codepointLength) - { - int codepoint = GetCodepoint(text + i, &codepointLength); - int index = GetGlyphIndex(guiFont, codepoint); // If requested codepoint is not found, we get '?' (0x3f) - Rectangle atlasRec = guiFont.recs[index]; - GlyphInfo glyphInfo = guiFont.glyphs[index]; // Glyph measures - - if ((codepointLength == 1) && (codepoint == '\n')) - { - cursorPos.y += (guiFont.baseSize*scaleFactor + GuiGetStyle(TEXTBOX, TEXT_LINES_SPACING)); // Line feed - cursorPos.x = textAreaBounds.x; // Carriage return - } - else - { - if (wrapMode == 1) - { - int glyphWidth = 0; - if (glyphInfo.advanceX != 0) glyphWidth += glyphInfo.advanceX; - else glyphWidth += (int)(atlasRec.width + glyphInfo.offsetX); - - // Jump line if the end of the text box area has been reached - if ((cursorPos.x + (glyphWidth*scaleFactor)) > (textAreaBounds.x + textAreaBounds.width)) - { - cursorPos.y += (guiFont.baseSize*scaleFactor + GuiGetStyle(TEXTBOX, TEXT_LINES_SPACING)); // Line feed - cursorPos.x = textAreaBounds.x; // Carriage return - } - } - else if (wrapMode == 2) - { - /* - if ((codepointLength == 1) && (codepoint == ' ')) - { - lastSpacePos = i; - lastSpaceWidth = 0; - lastSpaceCursorPos = cursorPos.x; - } - - // Jump line if last word reaches end of text box area - if ((lastSpaceCursorPos + lastSpaceWidth) > (textAreaBounds.x + textAreaBounds.width)) - { - cursorPos.y += 12; // Line feed - cursorPos.x = textAreaBounds.x; // Carriage return - } - */ - } - - // Draw current character glyph - DrawTextCodepoint(guiFont, codepoint, cursorPos, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), Fade(GetColor(GuiGetStyle(TEXTBOX, TEXT + (state*3))), guiAlpha)); - - int glyphWidth = 0; - if (glyphInfo.advanceX != 0) glyphWidth += glyphInfo.advanceX; - else glyphWidth += (int)(atlasRec.width + glyphInfo.offsetX); - - cursorPos.x += (glyphWidth*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); - //if (i > lastSpacePos) lastSpaceWidth += (atlasRec.width + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); - } - } - - cursor.x = cursorPos.x; - cursor.y = cursorPos.y; - - // Draw cursor position considering text glyphs - if (editMode) GuiDrawRectangle(cursor, 0, BLANK, Fade(GetColor(GuiGetStyle(TEXTBOX, BORDER_COLOR_PRESSED)), guiAlpha)); - //-------------------------------------------------------------------- - - return pressed; + return result; } // Slider control with pro parameters // NOTE: Other GuiSlider*() controls use this one -float GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue, int sliderWidth) +int GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue, int sliderWidth) { + int result = 0; GuiState state = guiState; - int sliderValue = (int)(((value - minValue)/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))); + float temp = (maxValue - minValue)/2.0f; + if (value == NULL) value = &temp; + + int sliderValue = (int)(((*value - minValue)/(maxValue - minValue))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))); Rectangle slider = { bounds.x, bounds.y + GuiGetStyle(SLIDER, BORDER_WIDTH) + GuiGetStyle(SLIDER, SLIDER_PADDING), 0, bounds.height - 2*GuiGetStyle(SLIDER, BORDER_WIDTH) - 2*GuiGetStyle(SLIDER, SLIDER_PADDING) }; @@ -2480,14 +2712,32 @@ float GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight { Vector2 mousePoint = GetMousePosition(); - if (CheckCollisionPointRec(mousePoint, bounds)) + if (guiSliderDragging) // Keep dragging outside of bounds + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) + { + // Get equivalent value and slider position from mousePoint.x + *value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue; + } + } + else + { + guiSliderDragging = false; + guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds)) { if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { state = STATE_PRESSED; + guiSliderDragging = true; + guiSliderActive = bounds; // Store bounds as an identifier when dragging starts // Get equivalent value and slider position from mousePoint.x - value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue; + *value = ((maxValue - minValue)*(mousePoint.x - (float)(bounds.x + sliderWidth/2)))/(float)(bounds.width - sliderWidth) + minValue; if (sliderWidth > 0) slider.x = mousePoint.x - slider.width/2; // Slider else if (sliderWidth == 0) slider.width = (float)sliderValue; // SliderBar @@ -2495,8 +2745,8 @@ float GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight else state = STATE_FOCUSED; } - if (value > maxValue) value = maxValue; - else if (value < minValue) value = minValue; + if (*value > maxValue) *value = maxValue; + else if (*value < minValue) *value = minValue; } // Bar limits check @@ -2543,35 +2793,39 @@ float GuiSliderPro(Rectangle bounds, const char *textLeft, const char *textRight } //-------------------------------------------------------------------- - return value; + return result; } // Slider control extended, returns selected value and has text -float GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue) +int GuiSlider(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue) { return GuiSliderPro(bounds, textLeft, textRight, value, minValue, maxValue, GuiGetStyle(SLIDER, SLIDER_WIDTH)); } // Slider Bar control extended, returns selected value -float GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue) +int GuiSliderBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue) { return GuiSliderPro(bounds, textLeft, textRight, value, minValue, maxValue, 0); } // Progress Bar control extended, shows current progress value -float GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float value, float minValue, float maxValue) +int GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRight, float *value, float minValue, float maxValue) { + int result = 0; GuiState state = guiState; + float temp = (maxValue - minValue)/2.0f; + if (value == NULL) value = &temp; + Rectangle progress = { bounds.x + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH), bounds.y + GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) + GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING), 0, bounds.height - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH) - 2*GuiGetStyle(PROGRESSBAR, PROGRESS_PADDING) }; // Update control //-------------------------------------------------------------------- - if (value > maxValue) value = maxValue; + if (*value > maxValue) *value = maxValue; - if (state != STATE_DISABLED) progress.width = ((float)(value/(maxValue - minValue))*(float)(bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH))); + if (state != STATE_DISABLED) progress.width = ((float)(*value/(maxValue - minValue))*(float)(bounds.width - 2*GuiGetStyle(PROGRESSBAR, BORDER_WIDTH))); //-------------------------------------------------------------------- // Draw control @@ -2606,12 +2860,13 @@ float GuiProgressBar(Rectangle bounds, const char *textLeft, const char *textRig } //-------------------------------------------------------------------- - return value; + return result; } // Status Bar control -void GuiStatusBar(Rectangle bounds, const char *text) +int GuiStatusBar(Rectangle bounds, const char *text) { + int result = 0; GuiState state = guiState; // Draw control @@ -2620,16 +2875,19 @@ void GuiStatusBar(Rectangle bounds, const char *text) Fade(GetColor(GuiGetStyle(STATUSBAR, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); GuiDrawText(text, GetTextBounds(STATUSBAR, bounds), GuiGetStyle(STATUSBAR, TEXT_ALIGNMENT), Fade(GetColor(GuiGetStyle(STATUSBAR, (state != STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha)); //-------------------------------------------------------------------- + + return result; } // Dummy rectangle control, intended for placeholding -void GuiDummyRec(Rectangle bounds, const char *text) +int GuiDummyRec(Rectangle bounds, const char *text) { + int result = 0; GuiState state = guiState; // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -2647,25 +2905,32 @@ void GuiDummyRec(Rectangle bounds, const char *text) GuiDrawRectangle(bounds, 0, BLANK, Fade(GetColor(GuiGetStyle(DEFAULT, (state != STATE_DISABLED)? BASE_COLOR_NORMAL : BASE_COLOR_DISABLED)), guiAlpha)); GuiDrawText(text, GetTextBounds(DEFAULT, bounds), TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(BUTTON, (state != STATE_DISABLED)? TEXT_COLOR_NORMAL : TEXT_COLOR_DISABLED)), guiAlpha)); //------------------------------------------------------------------ + + return result; } // List View control -int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int active) +int GuiListView(Rectangle bounds, const char *text, int *scrollIndex, int *active) { + int result = 0; int itemCount = 0; const char **items = NULL; - if (text != NULL) items = GuiTextSplit(text, &itemCount, NULL); + if (text != NULL) items = GuiTextSplit(text, ';', &itemCount, NULL); - return GuiListViewEx(bounds, items, itemCount, NULL, scrollIndex, active); + result = GuiListViewEx(bounds, items, itemCount, scrollIndex, active, NULL); + + return result; } // List View control with extended parameters -int GuiListViewEx(Rectangle bounds, const char **text, int count, int *focus, int *scrollIndex, int active) +int GuiListViewEx(Rectangle bounds, const char **text, int count, int *scrollIndex, int *active, int *focus) { + int result = 0; GuiState state = guiState; + int itemFocused = (focus == NULL)? -1 : *focus; - int itemSelected = active; + int itemSelected = *active; // Check if we need a scroll bar bool useScrollBar = false; @@ -2689,7 +2954,7 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *focus, in // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -2800,19 +3065,21 @@ int GuiListViewEx(Rectangle bounds, const char **text, int count, int *focus, in if (focus != NULL) *focus = itemFocused; if (scrollIndex != NULL) *scrollIndex = startIndex; - return itemSelected; + *active = itemSelected; + return result; } // Color Panel control -Color GuiColorPanel(Rectangle bounds, const char *text, Color color) +int GuiColorPanel(Rectangle bounds, const char *text, Color *color) { - const Color colWhite = { 255, 255, 255, 255 }; - const Color colBlack = { 0, 0, 0, 255 }; - + int result = 0; GuiState state = guiState; Vector2 pickerSelector = { 0 }; - Vector3 vcolor = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f }; + const Color colWhite = { 255, 255, 255, 255 }; + const Color colBlack = { 0, 0, 0, 255 }; + + Vector3 vcolor = { (float)color->r/255.0f, (float)color->g/255.0f, (float)color->b/255.0f }; Vector3 hsv = ConvertRGBtoHSV(vcolor); pickerSelector.x = bounds.x + (float)hsv.y*bounds.width; // HSV: Saturation @@ -2827,7 +3094,7 @@ Color GuiColorPanel(Rectangle bounds, const char *text, Color color) // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { Vector2 mousePoint = GetMousePosition(); @@ -2850,10 +3117,10 @@ Color GuiColorPanel(Rectangle bounds, const char *text, Color color) Vector3 rgb = ConvertHSVtoRGB(hsv); // NOTE: Vector3ToColor() only available on raylib 1.8.1 - color = RAYGUI_CLITERAL(Color){ (unsigned char)(255.0f*rgb.x), + *color = RAYGUI_CLITERAL(Color){ (unsigned char)(255.0f*rgb.x), (unsigned char)(255.0f*rgb.y), (unsigned char)(255.0f*rgb.z), - (unsigned char)(255.0f*(float)color.a/255.0f) }; + (unsigned char)(255.0f*(float)color->a/255.0f) }; } else state = STATE_FOCUSED; @@ -2880,19 +3147,20 @@ Color GuiColorPanel(Rectangle bounds, const char *text, Color color) GuiDrawRectangle(bounds, GuiGetStyle(COLORPICKER, BORDER_WIDTH), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha), BLANK); //-------------------------------------------------------------------- - return color; + return result; } // Color Bar Alpha control // NOTE: Returns alpha value normalized [0..1] -float GuiColorBarAlpha(Rectangle bounds, const char *text, float alpha) +int GuiColorBarAlpha(Rectangle bounds, const char *text, float *alpha) { #if !defined(RAYGUI_COLORBARALPHA_CHECKED_SIZE) #define RAYGUI_COLORBARALPHA_CHECKED_SIZE 10 #endif + int result = 0; GuiState state = guiState; - Rectangle selector = { (float)bounds.x + alpha*bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2, (float)bounds.y - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT), (float)bounds.height + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2 }; + Rectangle selector = { (float)bounds.x + (*alpha)*bounds.width - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2, (float)bounds.y - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT), (float)bounds.height + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2 }; // Update control //-------------------------------------------------------------------- @@ -2900,16 +3168,34 @@ float GuiColorBarAlpha(Rectangle bounds, const char *text, float alpha) { Vector2 mousePoint = GetMousePosition(); - if (CheckCollisionPointRec(mousePoint, bounds) || - CheckCollisionPointRec(mousePoint, selector)) + if (guiSliderDragging) // Keep dragging outside of bounds + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) + { + *alpha = (mousePoint.x - bounds.x)/bounds.width; + if (*alpha <= 0.0f) *alpha = 0.0f; + if (*alpha >= 1.0f) *alpha = 1.0f; + } + } + else + { + guiSliderDragging = false; + guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector)) { if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { state = STATE_PRESSED; + guiSliderDragging = true; + guiSliderActive = bounds; // Store bounds as an identifier when dragging starts - alpha = (mousePoint.x - bounds.x)/bounds.width; - if (alpha <= 0.0f) alpha = 0.0f; - if (alpha >= 1.0f) alpha = 1.0f; + *alpha = (mousePoint.x - bounds.x)/bounds.width; + if (*alpha <= 0.0f) *alpha = 0.0f; + if (*alpha >= 1.0f) *alpha = 1.0f; //selector.x = bounds.x + (int)(((alpha - 0)/(100 - 0))*(bounds.width - 2*GuiGetStyle(SLIDER, BORDER_WIDTH))) - selector.width/2; } else state = STATE_FOCUSED; @@ -2945,7 +3231,7 @@ float GuiColorBarAlpha(Rectangle bounds, const char *text, float alpha) GuiDrawRectangle(selector, 0, BLANK, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); //-------------------------------------------------------------------- - return alpha; + return result; } // Color Bar Hue control @@ -2954,10 +3240,11 @@ float GuiColorBarAlpha(Rectangle bounds, const char *text, float alpha) // Color GuiColorBarSat() [WHITE->color] // Color GuiColorBarValue() [BLACK->color], HSV/HSL // float GuiColorBarLuminance() [BLACK->WHITE] -float GuiColorBarHue(Rectangle bounds, const char *text, float hue) +int GuiColorBarHue(Rectangle bounds, const char *text, float *hue) { + int result = 0; GuiState state = guiState; - Rectangle selector = { (float)bounds.x - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (float)bounds.y + hue/360.0f*bounds.height - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2, (float)bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2, (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT) }; + Rectangle selector = { (float)bounds.x - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW), (float)bounds.y + (*hue)/360.0f*bounds.height - GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT)/2, (float)bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW)*2, (float)GuiGetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT) }; // Update control //-------------------------------------------------------------------- @@ -2965,16 +3252,34 @@ float GuiColorBarHue(Rectangle bounds, const char *text, float hue) { Vector2 mousePoint = GetMousePosition(); - if (CheckCollisionPointRec(mousePoint, bounds) || - CheckCollisionPointRec(mousePoint, selector)) + if (guiSliderDragging) // Keep dragging outside of bounds + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) + { + *hue = (mousePoint.y - bounds.y)*360/bounds.height; + if (*hue <= 0.0f) *hue = 0.0f; + if (*hue >= 359.0f) *hue = 359.0f; + } + } + else + { + guiSliderDragging = false; + guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds) || CheckCollisionPointRec(mousePoint, selector)) { if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { state = STATE_PRESSED; + guiSliderDragging = true; + guiSliderActive = bounds; // Store bounds as an identifier when dragging starts - hue = (mousePoint.y - bounds.y)*360/bounds.height; - if (hue <= 0.0f) hue = 0.0f; - if (hue >= 359.0f) hue = 359.0f; + *hue = (mousePoint.y - bounds.y)*360/bounds.height; + if (*hue <= 0.0f) *hue = 0.0f; + if (*hue >= 359.0f) *hue = 359.0f; } else state = STATE_FOCUSED; @@ -2998,12 +3303,12 @@ float GuiColorBarHue(Rectangle bounds, const char *text, float hue) if (state != STATE_DISABLED) { // Draw hue bar:color bars - DrawRectangleGradientV((int)bounds.x, (int)(bounds.y), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color) { 255, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color) { 255, 255, 0, 255 }, guiAlpha)); - DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + bounds.height/6), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color) { 255, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color) { 0, 255, 0, 255 }, guiAlpha)); - DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 2*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color) { 0, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color) { 0, 255, 255, 255 }, guiAlpha)); - DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 3*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color) { 0, 255, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color) { 0, 0, 255, 255 }, guiAlpha)); - DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 4*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color) { 0, 0, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color) { 255, 0, 255, 255 }, guiAlpha)); - DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 5*(bounds.height/6)), (int)bounds.width, (int)(bounds.height/6), Fade(RAYGUI_CLITERAL(Color) { 255, 0, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color) { 255, 0, 0, 255 }, guiAlpha)); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha)); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + bounds.height/6), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 0, 255 }, guiAlpha)); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 2*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 0, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 255, 255 }, guiAlpha)); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 3*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 255, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 255, 255 }, guiAlpha)); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 4*(bounds.height/6)), (int)bounds.width, (int)ceilf(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 0, 0, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 255, 255 }, guiAlpha)); + DrawRectangleGradientV((int)bounds.x, (int)(bounds.y + 5*(bounds.height/6)), (int)bounds.width, (int)(bounds.height/6), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 255, 255 }, guiAlpha), Fade(RAYGUI_CLITERAL(Color){ 255, 0, 0, 255 }, guiAlpha)); } else DrawRectangleGradientV((int)bounds.x, (int)bounds.y, (int)bounds.width, (int)bounds.height, Fade(Fade(GetColor(GuiGetStyle(COLORPICKER, BASE_COLOR_DISABLED)), 0.1f), guiAlpha), Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER_COLOR_DISABLED)), guiAlpha)); @@ -3013,7 +3318,7 @@ float GuiColorBarHue(Rectangle bounds, const char *text, float hue) GuiDrawRectangle(selector, 0, BLANK, Fade(GetColor(GuiGetStyle(COLORPICKER, BORDER + state*3)), guiAlpha)); //-------------------------------------------------------------------- - return hue; + return result; } // Color Picker control @@ -3022,21 +3327,28 @@ float GuiColorBarHue(Rectangle bounds, const char *text, float hue) // float GuiColorBarAlpha(Rectangle bounds, float alpha) // float GuiColorBarHue(Rectangle bounds, float value) // NOTE: bounds define GuiColorPanel() size -Color GuiColorPicker(Rectangle bounds, const char *text, Color color) +int GuiColorPicker(Rectangle bounds, const char *text, Color *color) { - color = GuiColorPanel(bounds, NULL, color); + int result = 0; + + Color temp = { 200, 0, 0, 255 }; + if (color == NULL) color = &temp; + + GuiColorPanel(bounds, NULL, color); Rectangle boundsHue = { (float)bounds.x + bounds.width + GuiGetStyle(COLORPICKER, HUEBAR_PADDING), (float)bounds.y, (float)GuiGetStyle(COLORPICKER, HUEBAR_WIDTH), (float)bounds.height }; //Rectangle boundsAlpha = { bounds.x, bounds.y + bounds.height + GuiGetStyle(COLORPICKER, BARS_PADDING), bounds.width, GuiGetStyle(COLORPICKER, BARS_THICK) }; - Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ color.r/255.0f, color.g/255.0f, color.b/255.0f }); - hsv.x = GuiColorBarHue(boundsHue, NULL, hsv.x); + Vector3 hsv = ConvertRGBtoHSV(RAYGUI_CLITERAL(Vector3){ (*color).r/255.0f, (*color).g/255.0f, (*color).b/255.0f }); + + GuiColorBarHue(boundsHue, NULL, &hsv.x); + //color.a = (unsigned char)(GuiColorBarAlpha(boundsAlpha, (float)color.a/255.0f)*255.0f); Vector3 rgb = ConvertHSVtoRGB(hsv); - color = RAYGUI_CLITERAL(Color){ (unsigned char)roundf(rgb.x*255.0f), (unsigned char)roundf(rgb.y*255.0f), (unsigned char)roundf(rgb.z*255.0f), color.a }; + *color = RAYGUI_CLITERAL(Color){ (unsigned char)roundf(rgb.x*255.0f), (unsigned char)roundf(rgb.y*255.0f), (unsigned char)roundf(rgb.z*255.0f), (*color).a }; - return color; + return result; } // Message Box control @@ -3049,27 +3361,27 @@ int GuiMessageBox(Rectangle bounds, const char *title, const char *message, cons #define RAYGUI_MESSAGEBOX_BUTTON_PADDING 12 #endif - int clicked = -1; // Returns clicked button from buttons list, 0 refers to closed window button + int result = -1; // Returns clicked button from buttons list, 0 refers to closed window button int buttonCount = 0; - const char **buttonsText = GuiTextSplit(buttons, &buttonCount, NULL); + const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); Rectangle buttonBounds = { 0 }; buttonBounds.x = bounds.x + RAYGUI_MESSAGEBOX_BUTTON_PADDING; buttonBounds.y = bounds.y + bounds.height - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT - RAYGUI_MESSAGEBOX_BUTTON_PADDING; buttonBounds.width = (bounds.width - RAYGUI_MESSAGEBOX_BUTTON_PADDING*(buttonCount + 1))/buttonCount; buttonBounds.height = RAYGUI_MESSAGEBOX_BUTTON_HEIGHT; - Vector2 textSize = MeasureTextEx(guiFont, message, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), 1); + int textWidth = GetTextWidth(message); Rectangle textBounds = { 0 }; - textBounds.x = bounds.x + bounds.width/2 - textSize.x/2; + textBounds.x = bounds.x + bounds.width/2 - textWidth/2; textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + RAYGUI_MESSAGEBOX_BUTTON_PADDING; - textBounds.width = textSize.x; + textBounds.width = (float)textWidth; textBounds.height = bounds.height - RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT - 3*RAYGUI_MESSAGEBOX_BUTTON_PADDING - RAYGUI_MESSAGEBOX_BUTTON_HEIGHT; // Draw control //-------------------------------------------------------------------- - if (GuiWindowBox(bounds, title)) clicked = 0; + if (GuiWindowBox(bounds, title)) result = 0; int prevTextAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); @@ -3081,37 +3393,37 @@ int GuiMessageBox(Rectangle bounds, const char *title, const char *message, cons for (int i = 0; i < buttonCount; i++) { - if (GuiButton(buttonBounds, buttonsText[i])) clicked = i + 1; + if (GuiButton(buttonBounds, buttonsText[i])) result = i + 1; buttonBounds.x += (buttonBounds.width + RAYGUI_MESSAGEBOX_BUTTON_PADDING); } GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevTextAlignment); //-------------------------------------------------------------------- - return clicked; + return result; } // Text Input Box control, ask for text -int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, int *secretViewActive) +int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, const char *buttons, char *text, int textMaxSize, bool *secretViewActive) { #if !defined(RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT) - #define RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT 28 + #define RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT 24 #endif #if !defined(RAYGUI_TEXTINPUTBOX_BUTTON_PADDING) #define RAYGUI_TEXTINPUTBOX_BUTTON_PADDING 12 #endif #if !defined(RAYGUI_TEXTINPUTBOX_HEIGHT) - #define RAYGUI_TEXTINPUTBOX_HEIGHT 28 + #define RAYGUI_TEXTINPUTBOX_HEIGHT 26 #endif // Used to enable text edit mode // WARNING: No more than one GuiTextInputBox() should be open at the same time static bool textEditMode = false; - int btnIndex = -1; + int result = -1; int buttonCount = 0; - const char **buttonsText = GuiTextSplit(buttons, &buttonCount, NULL); + const char **buttonsText = GuiTextSplit(buttons, ';', &buttonCount, NULL); Rectangle buttonBounds = { 0 }; buttonBounds.x = bounds.x + RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; buttonBounds.y = bounds.y + bounds.height - RAYGUI_TEXTINPUTBOX_BUTTON_HEIGHT - RAYGUI_TEXTINPUTBOX_BUTTON_PADDING; @@ -3123,12 +3435,12 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co Rectangle textBounds = { 0 }; if (message != NULL) { - Vector2 textSize = MeasureTextEx(guiFont, message, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), 1); + int textSize = GetTextWidth(message) + 2; - textBounds.x = bounds.x + bounds.width/2 - textSize.x/2; - textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + messageInputHeight/4 - textSize.y/2; - textBounds.width = textSize.x; - textBounds.height = textSize.y; + textBounds.x = bounds.x + bounds.width/2 - textSize/2; + textBounds.y = bounds.y + RAYGUI_WINDOWBOX_STATUSBAR_HEIGHT + messageInputHeight/4 - (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/2; + textBounds.width = (float)textSize; + textBounds.height = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); } Rectangle textBoxBounds = { 0 }; @@ -3141,7 +3453,7 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co // Draw control //-------------------------------------------------------------------- - if (GuiWindowBox(bounds, title)) btnIndex = 0; + if (GuiWindowBox(bounds, title)) result = 0; // Draw message if available if (message != NULL) @@ -3155,10 +3467,10 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co if (secretViewActive != NULL) { static char stars[] = "****************"; - if (GuiTextBox(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x, textBoxBounds.y, textBoxBounds.width - 4 - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.height }, + if (GuiTextBox(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x, textBoxBounds.y, textBoxBounds.width - 4 - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.height }, ((*secretViewActive == 1) || textEditMode)? text : stars, textMaxSize, textEditMode)) textEditMode = !textEditMode; - *secretViewActive = GuiToggle(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x + textBoxBounds.width - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.y, RAYGUI_TEXTINPUTBOX_HEIGHT, RAYGUI_TEXTINPUTBOX_HEIGHT }, (*secretViewActive == 1)? "#44#" : "#45#", *secretViewActive); + GuiToggle(RAYGUI_CLITERAL(Rectangle){ textBoxBounds.x + textBoxBounds.width - RAYGUI_TEXTINPUTBOX_HEIGHT, textBoxBounds.y, RAYGUI_TEXTINPUTBOX_HEIGHT, RAYGUI_TEXTINPUTBOX_HEIGHT }, (*secretViewActive == 1)? "#44#" : "#45#", secretViewActive); } else { @@ -3170,52 +3482,53 @@ int GuiTextInputBox(Rectangle bounds, const char *title, const char *message, co for (int i = 0; i < buttonCount; i++) { - if (GuiButton(buttonBounds, buttonsText[i])) btnIndex = i + 1; + if (GuiButton(buttonBounds, buttonsText[i])) result = i + 1; buttonBounds.x += (buttonBounds.width + RAYGUI_MESSAGEBOX_BUTTON_PADDING); } + if (result >= 0) textEditMode = false; + GuiSetStyle(BUTTON, TEXT_ALIGNMENT, prevBtnTextAlignment); //-------------------------------------------------------------------- - return btnIndex; + return result; // Result is the pressed button index } // Grid control // NOTE: Returns grid mouse-hover selected cell // About drawing lines at subpixel spacing, simple put, not easy solution: // https://stackoverflow.com/questions/4435450/2d-opengl-drawing-lines-that-dont-exactly-fit-pixel-raster -Vector2 GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs) +int GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs, Vector2 *mouseCell) { // Grid lines alpha amount #if !defined(RAYGUI_GRID_ALPHA) #define RAYGUI_GRID_ALPHA 0.15f #endif + int result = 0; GuiState state = guiState; + Vector2 mousePoint = GetMousePosition(); - Vector2 currentCell = { -1, -1 }; + Vector2 currentMouseCell = { 0 }; int linesV = ((int)(bounds.width/spacing))*subdivs + 1; int linesH = ((int)(bounds.height/spacing))*subdivs + 1; // Update control //-------------------------------------------------------------------- - if ((state != STATE_DISABLED) && !guiLocked) + if ((state != STATE_DISABLED) && !guiLocked && !guiSliderDragging) { if (CheckCollisionPointRec(mousePoint, bounds)) { - // NOTE: Cell values must be rounded to int - currentCell.x = (float)((mousePoint.x - bounds.x)/spacing); - currentCell.y = (float)((mousePoint.y - bounds.y)/spacing); + // NOTE: Cell values must be the upper left of the cell the mouse is in + currentMouseCell.x = floorf((mousePoint.x - bounds.x)/spacing); + currentMouseCell.y = floorf((mousePoint.y - bounds.y)/spacing); } } //-------------------------------------------------------------------- // Draw control //-------------------------------------------------------------------- - - // TODO: Draw background panel? - switch (state) { case STATE_NORMAL: @@ -3240,9 +3553,24 @@ Vector2 GuiGrid(Rectangle bounds, const char *text, float spacing, int subdivs) default: break; } - return currentCell; + if (mouseCell != NULL) *mouseCell = currentMouseCell; + return result; } +//---------------------------------------------------------------------------------- +// Tooltip management functions +// NOTE: Tooltips requires some global variables: tooltipPtr +//---------------------------------------------------------------------------------- +// Enable gui tooltips (global state) +void GuiEnableTooltip(void) { guiTooltip = true; } + +// Disable gui tooltips (global state) +void GuiDisableTooltip(void) { guiTooltip = false; } + +// Set tooltip string +void GuiSetTooltip(const char *tooltip) { guiTooltipPtr = tooltip; } + + //---------------------------------------------------------------------------------- // Styles loading functions //---------------------------------------------------------------------------------- @@ -3338,125 +3666,24 @@ void GuiLoadStyle(const char *fileName) { rgsFile = fopen(fileName, "rb"); - if (rgsFile == NULL) return; - - char signature[5] = { 0 }; - short version = 0; - short reserved = 0; - int propertyCount = 0; - - fread(signature, 1, 4, rgsFile); - fread(&version, 1, sizeof(short), rgsFile); - fread(&reserved, 1, sizeof(short), rgsFile); - fread(&propertyCount, 1, sizeof(int), rgsFile); - - if ((signature[0] == 'r') && - (signature[1] == 'G') && - (signature[2] == 'S') && - (signature[3] == ' ')) + if (rgsFile != NULL) { - short controlId = 0; - short propertyId = 0; - unsigned int propertyValue = 0; + fseek(rgsFile, 0, SEEK_END); + int fileDataSize = ftell(rgsFile); + fseek(rgsFile, 0, SEEK_SET); - for (int i = 0; i < propertyCount; i++) + if (fileDataSize > 0) { - fread(&controlId, 1, sizeof(short), rgsFile); - fread(&propertyId, 1, sizeof(short), rgsFile); - fread(&propertyValue, 1, sizeof(unsigned int), rgsFile); + unsigned char *fileData = (unsigned char *)RL_MALLOC(fileDataSize*sizeof(unsigned char)); + fread(fileData, sizeof(unsigned char), fileDataSize, rgsFile); - if (controlId == 0) // DEFAULT control - { - // If a DEFAULT property is loaded, it is propagated to all controls - // NOTE: All DEFAULT properties should be defined first in the file - GuiSetStyle(0, (int)propertyId, propertyValue); + GuiLoadStyleFromMemory(fileData, fileDataSize); - if (propertyId < RAYGUI_MAX_PROPS_BASE) for (int i = 1; i < RAYGUI_MAX_CONTROLS; i++) GuiSetStyle(i, (int)propertyId, propertyValue); - } - else GuiSetStyle((int)controlId, (int)propertyId, propertyValue); + RL_FREE(fileData); } - // Font loading is highly dependant on raylib API to load font data and image -#if !defined(RAYGUI_STANDALONE) - // Load custom font if available - int fontDataSize = 0; - fread(&fontDataSize, 1, sizeof(int), rgsFile); - - if (fontDataSize > 0) - { - Font font = { 0 }; - int fontType = 0; // 0-Normal, 1-SDF - Rectangle whiteRec = { 0 }; - - fread(&font.baseSize, 1, sizeof(int), rgsFile); - fread(&font.glyphCount, 1, sizeof(int), rgsFile); - fread(&fontType, 1, sizeof(int), rgsFile); - - // Load font white rectangle - fread(&whiteRec, 1, sizeof(Rectangle), rgsFile); - - // Load font image parameters - int fontImageUncompSize = 0; - int fontImageCompSize = 0; - fread(&fontImageUncompSize, 1, sizeof(int), rgsFile); - fread(&fontImageCompSize, 1, sizeof(int), rgsFile); - - Image imFont = { 0 }; - imFont.mipmaps = 1; - fread(&imFont.width, 1, sizeof(int), rgsFile); - fread(&imFont.height, 1, sizeof(int), rgsFile); - fread(&imFont.format, 1, sizeof(int), rgsFile); - - if (fontImageCompSize < fontImageUncompSize) - { - // Compressed font atlas image data (DEFLATE), it requires DecompressData() - int dataUncompSize = 0; - unsigned char *compData = (unsigned char *)RAYGUI_MALLOC(fontImageCompSize); - fread(compData, 1, fontImageCompSize, rgsFile); - imFont.data = DecompressData(compData, fontImageCompSize, &dataUncompSize); - - // Security check, dataUncompSize must match the provided fontImageUncompSize - if (dataUncompSize != fontImageUncompSize) RAYGUI_LOG("WARNING: Uncompressed font atlas image data could be corrupted"); - - RAYGUI_FREE(compData); - } - else - { - // Font atlas image data is not compressed - imFont.data = (unsigned char *)RAYGUI_MALLOC(fontImageUncompSize); - fread(imFont.data, 1, fontImageUncompSize, rgsFile); - } - - if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture); - font.texture = LoadTextureFromImage(imFont); - if (font.texture.id == 0) font = GetFontDefault(); - - RAYGUI_FREE(imFont.data); - - // Load font recs data - font.recs = (Rectangle *)RAYGUI_CALLOC(font.glyphCount, sizeof(Rectangle)); - for (int i = 0; i < font.glyphCount; i++) fread(&font.recs[i], 1, sizeof(Rectangle), rgsFile); - - // Load font chars info data - font.glyphs = (GlyphInfo *)RAYGUI_CALLOC(font.glyphCount, sizeof(GlyphInfo)); - for (int i = 0; i < font.glyphCount; i++) - { - fread(&font.glyphs[i].value, 1, sizeof(int), rgsFile); - fread(&font.glyphs[i].offsetX, 1, sizeof(int), rgsFile); - fread(&font.glyphs[i].offsetY, 1, sizeof(int), rgsFile); - fread(&font.glyphs[i].advanceX, 1, sizeof(int), rgsFile); - } - - GuiSetFont(font); - - // Set font texture source rectangle to be used as white texture to draw shapes - // NOTE: This way, all gui can be draw using a single draw call - if ((whiteRec.width != 0) && (whiteRec.height != 0)) SetShapesTexture(font.texture, whiteRec); - } -#endif + fclose(rgsFile); } - - fclose(rgsFile); } } @@ -3493,9 +3720,9 @@ void GuiLoadStyleDefault(void) GuiSetStyle(CHECKBOX, TEXT_ALIGNMENT, TEXT_ALIGN_RIGHT); GuiSetStyle(TEXTBOX, TEXT_PADDING, 4); GuiSetStyle(TEXTBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); - GuiSetStyle(VALUEBOX, TEXT_PADDING, 4); + GuiSetStyle(VALUEBOX, TEXT_PADDING, 0); GuiSetStyle(VALUEBOX, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); - GuiSetStyle(SPINNER, TEXT_PADDING, 4); + GuiSetStyle(SPINNER, TEXT_PADDING, 0); GuiSetStyle(SPINNER, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); GuiSetStyle(STATUSBAR, TEXT_PADDING, 8); GuiSetStyle(STATUSBAR, TEXT_ALIGNMENT, TEXT_ALIGN_LEFT); @@ -3515,7 +3742,7 @@ void GuiLoadStyleDefault(void) GuiSetStyle(COMBOBOX, COMBO_BUTTON_SPACING, 2); GuiSetStyle(DROPDOWNBOX, ARROW_PADDING, 16); GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 2); - GuiSetStyle(TEXTBOX, TEXT_LINES_SPACING, 4); + GuiSetStyle(TEXTBOX, TEXT_LINES_SPACING, (int)((float)GuiGetStyle(DEFAULT, TEXT_SIZE)*1.5f)); GuiSetStyle(TEXTBOX, TEXT_INNER_PADDING, 4); GuiSetStyle(SPINNER, SPIN_BUTTON_WIDTH, 24); GuiSetStyle(SPINNER, SPIN_BUTTON_SPACING, 2); @@ -3526,7 +3753,7 @@ void GuiLoadStyleDefault(void) GuiSetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE, 16); GuiSetStyle(SCROLLBAR, SCROLL_PADDING, 0); GuiSetStyle(SCROLLBAR, SCROLL_SPEED, 12); - GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 24); + GuiSetStyle(LISTVIEW, LIST_ITEMS_HEIGHT, 28); GuiSetStyle(LISTVIEW, LIST_ITEMS_SPACING, 2); GuiSetStyle(LISTVIEW, SCROLLBAR_WIDTH, 12); GuiSetStyle(LISTVIEW, SCROLLBAR_SIDE, SCROLLBAR_RIGHT_SIDE); @@ -3536,7 +3763,19 @@ void GuiLoadStyleDefault(void) GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_HEIGHT, 8); GuiSetStyle(COLORPICKER, HUEBAR_SELECTOR_OVERFLOW, 2); - guiFont = GetFontDefault(); // Initialize default font + if (guiFont.texture.id != GetFontDefault().texture.id) + { + // Unload previous font texture + UnloadTexture(guiFont.texture); + + // Setup default raylib font + guiFont = GetFontDefault(); + + // NOTE: Default raylib font character 95 is a white square + Rectangle whiteChar = guiFont.recs[95]; + // NOTE: We set up a 1px padding on char rectangle to avoid pixel bleeding on MSAA filtering + SetShapesTexture(guiFont.texture, RAYGUI_CLITERAL(Rectangle){ whiteChar.x + 1, whiteChar.y + 1, whiteChar.width - 2, whiteChar.height - 2 }); + } } // Get text with icon id prepended @@ -3554,28 +3793,27 @@ const char *GuiIconText(int iconId, const char *text) { memset(buffer, 0, 1024); sprintf(buffer, "#%03i#", iconId); - + for (int i = 5; i < 1024; i++) { buffer[i] = text[i - 5]; if (text[i - 5] == '\0') break; } - + return buffer; } - else + else { sprintf(iconBuffer, "#%03i#", iconId & 0x1ff); - + return iconBuffer; } #endif } #if !defined(RAYGUI_NO_ICONS) - // Get full icons data pointer -unsigned int *GuiGetIcons(void) { return guiIcons; } +unsigned int *GuiGetIcons(void) { return guiIconsPtr; } // Load raygui icons file (.rgi) // NOTE: In case nameIds are required, they can be requested with loadIconsName, @@ -3620,10 +3858,10 @@ char **GuiLoadIcons(const char *fileName, bool loadIconsName) short iconSize = 0; fread(signature, 1, 4, rgiFile); - fread(&version, 1, sizeof(short), rgiFile); - fread(&reserved, 1, sizeof(short), rgiFile); - fread(&iconCount, 1, sizeof(short), rgiFile); - fread(&iconSize, 1, sizeof(short), rgiFile); + fread(&version, sizeof(short), 1, rgiFile); + fread(&reserved, sizeof(short), 1, rgiFile); + fread(&iconCount, sizeof(short), 1, rgiFile); + fread(&iconSize, sizeof(short), 1, rgiFile); if ((signature[0] == 'r') && (signature[1] == 'G') && @@ -3636,13 +3874,13 @@ char **GuiLoadIcons(const char *fileName, bool loadIconsName) for (int i = 0; i < iconCount; i++) { guiIconsName[i] = (char *)RAYGUI_MALLOC(RAYGUI_ICON_MAX_NAME_LENGTH); - fread(guiIconsName[i], RAYGUI_ICON_MAX_NAME_LENGTH, 1, rgiFile); + fread(guiIconsName[i], 1, RAYGUI_ICON_MAX_NAME_LENGTH, rgiFile); } } else fseek(rgiFile, iconCount*RAYGUI_ICON_MAX_NAME_LENGTH, SEEK_CUR); - // Read icons data directly over guiIcons data array - fread(guiIcons, iconCount*(iconSize*iconSize/32), sizeof(unsigned int), rgiFile); + // Read icons data directly over internal icons array + fread(guiIconsPtr, sizeof(unsigned int), iconCount*(iconSize*iconSize/32), rgiFile); } fclose(rgiFile); @@ -3660,7 +3898,7 @@ void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color) { for (int k = 0; k < 32; k++) { - if (BIT_CHECK(guiIcons[iconId*RAYGUI_ICON_DATA_ELEMENTS + i], k)) + if (BIT_CHECK(guiIconsPtr[iconId*RAYGUI_ICON_DATA_ELEMENTS + i], k)) { #if !defined(RAYGUI_STANDALONE) DrawRectangle(posX + (k%RAYGUI_ICON_SIZE)*pixelSize, posY + y*pixelSize, pixelSize, pixelSize, color); @@ -3672,63 +3910,156 @@ void GuiDrawIcon(int iconId, int posX, int posY, int pixelSize, Color color) } } -// Get icon bit data -// NOTE: Bit data array grouped as unsigned int (RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32 elements) -unsigned int *GuiGetIconData(int iconId) +// Set icon drawing size +void GuiSetIconScale(int scale) { - static unsigned int iconData[RAYGUI_ICON_DATA_ELEMENTS] = { 0 }; - memset(iconData, 0, RAYGUI_ICON_DATA_ELEMENTS*sizeof(unsigned int)); - - if (iconId < RAYGUI_ICON_MAX_ICONS) memcpy(iconData, &guiIcons[iconId*RAYGUI_ICON_DATA_ELEMENTS], RAYGUI_ICON_DATA_ELEMENTS*sizeof(unsigned int)); - - return iconData; + if (scale >= 1) guiIconScale = scale; } -// Set icon bit data -// NOTE: Data must be provided as unsigned int array (RAYGUI_ICON_SIZE*RAYGUI_ICON_SIZE/32 elements) -void GuiSetIconData(int iconId, unsigned int *data) -{ - if (iconId < RAYGUI_ICON_MAX_ICONS) memcpy(&guiIcons[iconId*RAYGUI_ICON_DATA_ELEMENTS], data, RAYGUI_ICON_DATA_ELEMENTS*sizeof(unsigned int)); -} - -// Set icon scale (1 by default) -void GuiSetIconScale(unsigned int scale) -{ - guiIconScale = (scale < 1)? 1 : scale; -} - -// Set icon pixel value -void GuiSetIconPixel(int iconId, int x, int y) -{ - #define BIT_SET(a,b) ((a) |= (1u<<(b))) - - // This logic works for any RAYGUI_ICON_SIZE pixels icons, - // For example, in case of 16x16 pixels, every 2 lines fit in one unsigned int data element - BIT_SET(guiIcons[iconId*RAYGUI_ICON_DATA_ELEMENTS + y/(sizeof(unsigned int)*8/RAYGUI_ICON_SIZE)], x + (y%(sizeof(unsigned int)*8/RAYGUI_ICON_SIZE)*RAYGUI_ICON_SIZE)); -} - -// Clear icon pixel value -void GuiClearIconPixel(int iconId, int x, int y) -{ - #define BIT_CLEAR(a,b) ((a) &= ~((1u)<<(b))) - - // This logic works for any RAYGUI_ICON_SIZE pixels icons, - // For example, in case of 16x16 pixels, every 2 lines fit in one unsigned int data element - BIT_CLEAR(guiIcons[iconId*RAYGUI_ICON_DATA_ELEMENTS + y/(sizeof(unsigned int)*8/RAYGUI_ICON_SIZE)], x + (y%(sizeof(unsigned int)*8/RAYGUI_ICON_SIZE)*RAYGUI_ICON_SIZE)); -} - -// Check icon pixel value -bool GuiCheckIconPixel(int iconId, int x, int y) -{ - #define BIT_CHECK(a,b) ((a) & (1u<<(b))) - - return (BIT_CHECK(guiIcons[iconId*8 + y/2], x + (y%2*16))); -} #endif // !RAYGUI_NO_ICONS //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- + +// Load style from memory (binary only) +static void GuiLoadStyleFromMemory(const unsigned char *fileData, int dataSize) +{ + unsigned char *fileDataPtr = (unsigned char *)fileData; + + char signature[5] = { 0 }; + short version = 0; + short reserved = 0; + int propertyCount = 0; + + memcpy(signature, fileDataPtr, 4); + memcpy(&version, fileDataPtr + 4, sizeof(short)); + memcpy(&reserved, fileDataPtr + 4 + 2, sizeof(short)); + memcpy(&propertyCount, fileDataPtr + 4 + 2 + 2, sizeof(int)); + fileDataPtr += 12; + + if ((signature[0] == 'r') && + (signature[1] == 'G') && + (signature[2] == 'S') && + (signature[3] == ' ')) + { + short controlId = 0; + short propertyId = 0; + unsigned int propertyValue = 0; + + for (int i = 0; i < propertyCount; i++) + { + memcpy(&controlId, fileDataPtr, sizeof(short)); + memcpy(&propertyId, fileDataPtr + 2, sizeof(short)); + memcpy(&propertyValue, fileDataPtr + 2 + 2, sizeof(unsigned int)); + fileDataPtr += 8; + + if (controlId == 0) // DEFAULT control + { + // If a DEFAULT property is loaded, it is propagated to all controls + // NOTE: All DEFAULT properties should be defined first in the file + GuiSetStyle(0, (int)propertyId, propertyValue); + + if (propertyId < RAYGUI_MAX_PROPS_BASE) for (int i = 1; i < RAYGUI_MAX_CONTROLS; i++) GuiSetStyle(i, (int)propertyId, propertyValue); + } + else GuiSetStyle((int)controlId, (int)propertyId, propertyValue); + } + + // Font loading is highly dependant on raylib API to load font data and image + +#if !defined(RAYGUI_STANDALONE) + // Load custom font if available + int fontDataSize = 0; + memcpy(&fontDataSize, fileDataPtr, sizeof(int)); + fileDataPtr += 4; + + if (fontDataSize > 0) + { + Font font = { 0 }; + int fontType = 0; // 0-Normal, 1-SDF + Rectangle whiteRec = { 0 }; + + memcpy(&font.baseSize, fileDataPtr, sizeof(int)); + memcpy(&font.glyphCount, fileDataPtr + 4, sizeof(int)); + memcpy(&fontType, fileDataPtr + 4 + 4, sizeof(int)); + fileDataPtr += 12; + + // Load font white rectangle + memcpy(&whiteRec, fileDataPtr, sizeof(Rectangle)); + fileDataPtr += 16; + + // Load font image parameters + int fontImageUncompSize = 0; + int fontImageCompSize = 0; + memcpy(&fontImageUncompSize, fileDataPtr, sizeof(int)); + memcpy(&fontImageCompSize, fileDataPtr + 4, sizeof(int)); + fileDataPtr += 8; + + Image imFont = { 0 }; + imFont.mipmaps = 1; + memcpy(&imFont.width, fileDataPtr, sizeof(int)); + memcpy(&imFont.height, fileDataPtr + 4, sizeof(int)); + memcpy(&imFont.format, fileDataPtr + 4 + 4, sizeof(int)); + fileDataPtr += 12; + + if (fontImageCompSize < fontImageUncompSize) + { + // Compressed font atlas image data (DEFLATE), it requires DecompressData() + int dataUncompSize = 0; + unsigned char *compData = (unsigned char *)RAYGUI_MALLOC(fontImageCompSize); + memcpy(compData, fileDataPtr, fontImageCompSize); + fileDataPtr += fontImageCompSize; + + imFont.data = DecompressData(compData, fontImageCompSize, &dataUncompSize); + + // Security check, dataUncompSize must match the provided fontImageUncompSize + if (dataUncompSize != fontImageUncompSize) RAYGUI_LOG("WARNING: Uncompressed font atlas image data could be corrupted"); + + RAYGUI_FREE(compData); + } + else + { + // Font atlas image data is not compressed + imFont.data = (unsigned char *)RAYGUI_MALLOC(fontImageUncompSize); + memcpy(imFont.data, fileDataPtr, fontImageUncompSize); + fileDataPtr += fontImageUncompSize; + } + + if (font.texture.id != GetFontDefault().texture.id) UnloadTexture(font.texture); + font.texture = LoadTextureFromImage(imFont); + if (font.texture.id == 0) font = GetFontDefault(); + + RAYGUI_FREE(imFont.data); + + // Load font recs data + font.recs = (Rectangle *)RAYGUI_CALLOC(font.glyphCount, sizeof(Rectangle)); + for (int i = 0; i < font.glyphCount; i++) + { + memcpy(&font.recs[i], fileDataPtr, sizeof(Rectangle)); + fileDataPtr += sizeof(Rectangle); + } + + // Load font chars info data + font.glyphs = (GlyphInfo *)RAYGUI_CALLOC(font.glyphCount, sizeof(GlyphInfo)); + for (int i = 0; i < font.glyphCount; i++) + { + memcpy(&font.glyphs[i].value, fileDataPtr, sizeof(int)); + memcpy(&font.glyphs[i].offsetX, fileDataPtr + 4, sizeof(int)); + memcpy(&font.glyphs[i].offsetY, fileDataPtr + 8, sizeof(int)); + memcpy(&font.glyphs[i].advanceX, fileDataPtr + 12, sizeof(int)); + fileDataPtr += 16; + } + + GuiSetFont(font); + + // Set font texture source rectangle to be used as white texture to draw shapes + // NOTE: This way, all gui can be draw using a single draw call + if ((whiteRec.width != 0) && (whiteRec.height != 0)) SetShapesTexture(font.texture, whiteRec); + } +#endif + } +} + // Gui get text width considering icon static int GetTextWidth(const char *text) { @@ -3736,7 +4067,7 @@ static int GetTextWidth(const char *text) #define ICON_TEXT_PADDING 4 #endif - Vector2 size = { 0 }; + Vector2 textSize = { 0 }; int textIconOffset = 0; if ((text != NULL) && (text[0] != '\0')) @@ -3752,15 +4083,43 @@ static int GetTextWidth(const char *text) } } } - + + text += textIconOffset; + // Make sure guiFont is set, GuiGetStyle() initializes it lazynessly float fontSize = (float)GuiGetStyle(DEFAULT, TEXT_SIZE); - - size = MeasureTextEx(guiFont, text + textIconOffset, fontSize, (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); - if (textIconOffset > 0) size.x += (RAYGUI_ICON_SIZE - ICON_TEXT_PADDING); + + // Custom MeasureText() implementation + if ((guiFont.texture.id > 0) && (text != NULL)) + { + // Get size in bytes of text, considering end of line and line break + int size = 0; + for (int i = 0; i < MAX_LINE_BUFFER_SIZE; i++) + { + if ((text[i] != '\0') && (text[i] != '\n')) size++; + else break; + } + + float scaleFactor = fontSize/(float)guiFont.baseSize; + textSize.y = (float)guiFont.baseSize*scaleFactor; + float glyphWidth = 0.0f; + + for (int i = 0, codepointSize = 0; i < size; i += codepointSize) + { + int codepoint = GetCodepointNext(&text[i], &codepointSize); + int codepointIndex = GetGlyphIndex(guiFont, codepoint); + + if (guiFont.glyphs[codepointIndex].advanceX == 0) glyphWidth = ((float)guiFont.recs[codepointIndex].width*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + else glyphWidth = ((float)guiFont.glyphs[codepointIndex].advanceX*scaleFactor + GuiGetStyle(DEFAULT, TEXT_SPACING)); + + textSize.x += glyphWidth; + } + } + + if (textIconOffset > 0) textSize.x += (RAYGUI_ICON_SIZE - ICON_TEXT_PADDING); } - return (int)size.x; + return (int)textSize.x; } // Get text bounds considering control bounds @@ -3769,25 +4128,23 @@ static Rectangle GetTextBounds(int control, Rectangle bounds) Rectangle textBounds = bounds; textBounds.x = bounds.x + GuiGetStyle(control, BORDER_WIDTH); - textBounds.y = bounds.y + GuiGetStyle(control, BORDER_WIDTH); - textBounds.width = bounds.width - 2*GuiGetStyle(control, BORDER_WIDTH); - textBounds.height = bounds.height - 2*GuiGetStyle(control, BORDER_WIDTH); + textBounds.y = bounds.y + GuiGetStyle(control, BORDER_WIDTH) + GuiGetStyle(control, TEXT_PADDING); + textBounds.width = bounds.width - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING); + textBounds.height = bounds.height - 2*GuiGetStyle(control, BORDER_WIDTH) - 2*GuiGetStyle(control, TEXT_PADDING); // Consider TEXT_PADDING properly, depends on control type and TEXT_ALIGNMENT + // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, LISTVIEW (scrollbar?) + // More special cases (label on side): CHECKBOX, SLIDER, VALUEBOX, SPINNER switch (control) { - case COMBOBOX: bounds.width -= (GuiGetStyle(control, COMBO_BUTTON_WIDTH) + GuiGetStyle(control, COMBO_BUTTON_SPACING)); break; - case VALUEBOX: break; // NOTE: ValueBox text value always centered, text padding applies to label default: { if (GuiGetStyle(control, TEXT_ALIGNMENT) == TEXT_ALIGN_RIGHT) textBounds.x -= GuiGetStyle(control, TEXT_PADDING); else textBounds.x += GuiGetStyle(control, TEXT_PADDING); - } break; + } + break; } - // TODO: Special cases (no label): COMBOBOX, DROPDOWNBOX, LISTVIEW (scrollbar?) - // More special cases (label on side): CHECKBOX, SLIDER, VALUEBOX, SPINNER - return textBounds; } @@ -3822,6 +4179,39 @@ static const char *GetTextIcon(const char *text, int *iconId) return text; } +// Get text divided into lines (by line-breaks '\n') +const char **GetTextLines(const char *text, int *count) +{ + #define RAYGUI_MAX_TEXT_LINES 128 + + static const char *lines[RAYGUI_MAX_TEXT_LINES] = { 0 }; + for (int i = 0; i < RAYGUI_MAX_TEXT_LINES; i++) lines[i] = NULL; // Init NULL pointers to substrings + + int textSize = (int)strlen(text); + + lines[0] = text; + int len = 0; + *count = 1; + int lineSize = 0; // Stores current line size, not returned + + for (int i = 0, k = 0; (i < textSize) && (*count < RAYGUI_MAX_TEXT_LINES); i++) + { + if (text[i] == '\n') + { + lineSize = len; + k++; + lines[k] = &text[i + 1]; // WARNING: next value is valid? + len = 0; + *count += 1; + } + else len++; + } + + //lines[*count - 1].size = len; + + return lines; +} + // Gui draw text using default font static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color tint) { @@ -3831,70 +4221,120 @@ static void GuiDrawText(const char *text, Rectangle bounds, int alignment, Color #define ICON_TEXT_PADDING 4 #endif + int alignmentVertical = GuiGetStyle(TEXTBOX, TEXT_ALIGNMENT_VERTICAL); + + // We process the text lines one by one if ((text != NULL) && (text[0] != '\0')) { - int iconId = 0; - text = GetTextIcon(text, &iconId); // Check text for icon and move cursor + // Get text lines ('\n' delimiter) to process lines individually + // NOTE: We can't use GuiTextSplit() because it can be already used before calling + // GuiDrawText() and static buffer would be overriden :( + int lineCount = 0; + const char **lines = GetTextLines(text, &lineCount); - // Get text position depending on alignment and iconId - //--------------------------------------------------------------------------------- - Vector2 position = { bounds.x, bounds.y }; + //Rectangle textBounds = GetTextBounds(LABEL, bounds); + float totalHeight = (float)(lineCount*GuiGetStyle(DEFAULT, TEXT_SIZE) + (lineCount - 1)*GuiGetStyle(DEFAULT, TEXT_SIZE)/2); + float posOffsetY = 0; - // NOTE: We get text size after icon has been processed - // TODO: REVIEW: We consider text size in case of line breaks! -> MeasureTextEx() depends on raylib! - Vector2 textSize = MeasureTextEx(GuiGetFont(), text, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); - //int textWidth = GetTextWidth(text); - //int textHeight = GuiGetStyle(DEFAULT, TEXT_SIZE); - - // If text requires an icon, add size to measure - if (iconId >= 0) + for (int i = 0; i < lineCount; i++) { - textSize.x += RAYGUI_ICON_SIZE*guiIconScale; + int iconId = 0; + lines[i] = GetTextIcon(lines[i], &iconId); // Check text for icon and move cursor - // WARNING: If only icon provided, text could be pointing to EOF character: '\0' - if ((text != NULL) && (text[0] != '\0')) textSize.x += ICON_TEXT_PADDING; - } + // Get text position depending on alignment and iconId + //--------------------------------------------------------------------------------- + Vector2 position = { bounds.x, bounds.y }; - // Check guiTextAlign global variables - switch (alignment) - { - case TEXT_ALIGN_LEFT: + // NOTE: We get text size after icon has been processed + // WARNING: GetTextWidth() also processes text icon to get width! -> Really needed? + int textSizeX = GetTextWidth(lines[i]); + + // If text requires an icon, add size to measure + if (iconId >= 0) { - position.x = bounds.x; - position.y = bounds.y + bounds.height/2 - textSize.y/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); - } break; - case TEXT_ALIGN_CENTER: - { - position.x = bounds.x + bounds.width/2 - textSize.x/2; - position.y = bounds.y + bounds.height/2 - textSize.y/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); - } break; - case TEXT_ALIGN_RIGHT: - { - position.x = bounds.x + bounds.width - textSize.x; - position.y = bounds.y + bounds.height/2 - textSize.y/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); - } break; - default: break; - } + textSizeX += RAYGUI_ICON_SIZE*guiIconScale; - // NOTE: Make sure we get pixel-perfect coordinates, - // In case of decimals we got weird text positioning - position.x = (float)((int)position.x); - position.y = (float)((int)position.y); - //--------------------------------------------------------------------------------- + // WARNING: If only icon provided, text could be pointing to EOF character: '\0' + if ((lines[i] != NULL) && (lines[i][0] != '\0')) textSizeX += ICON_TEXT_PADDING; + } - // Draw text (with icon if available) - //--------------------------------------------------------------------------------- + // Check guiTextAlign global variables + switch (alignment) + { + case TEXT_ALIGN_LEFT: position.x = bounds.x; break; + case TEXT_ALIGN_CENTER: position.x = bounds.x + bounds.width/2 - textSizeX/2; break; + case TEXT_ALIGN_RIGHT: position.x = bounds.x + bounds.width - textSizeX; break; + default: break; + } + + switch (alignmentVertical) + { + case 0: position.y = bounds.y + posOffsetY + bounds.height/2 - totalHeight/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); break; // CENTERED + case 1: position.y = bounds.y + posOffsetY; break; // UP + case 2: position.y = bounds.y + posOffsetY + bounds.height - totalHeight + TEXT_VALIGN_PIXEL_OFFSET(bounds.height); break; // DOWN + default: break; + } + + // NOTE: Make sure we get pixel-perfect coordinates, + // In case of decimals we got weird text positioning + position.x = (float)((int)position.x); + position.y = (float)((int)position.y); + //--------------------------------------------------------------------------------- + + // Draw text (with icon if available) + //--------------------------------------------------------------------------------- #if !defined(RAYGUI_NO_ICONS) - if (iconId >= 0) - { - // NOTE: We consider icon height, probably different than text size - GuiDrawIcon(iconId, (int)position.x, (int)(bounds.y + bounds.height/2 - RAYGUI_ICON_SIZE*guiIconScale/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height)), guiIconScale, tint); - position.x += (RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); - } + if (iconId >= 0) + { + // NOTE: We consider icon height, probably different than text size + GuiDrawIcon(iconId, (int)position.x, (int)(bounds.y + bounds.height/2 - RAYGUI_ICON_SIZE*guiIconScale/2 + TEXT_VALIGN_PIXEL_OFFSET(bounds.height)), guiIconScale, tint); + position.x += (RAYGUI_ICON_SIZE*guiIconScale + ICON_TEXT_PADDING); + } #endif - DrawTextEx(guiFont, text, position, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING), tint); - //--------------------------------------------------------------------------------- + //DrawTextEx(guiFont, text, position, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING), tint); + + // Get size in bytes of text, + // considering end of line and line break + int size = 0; + for (int c = 0; (lines[i][c] != '\0') && (lines[i][c] != '\n'); c++, size++){ } + float scaleFactor = (float)GuiGetStyle(DEFAULT, TEXT_SIZE)/guiFont.baseSize; + + int textOffsetY = 0; + float textOffsetX = 0.0f; + for (int c = 0, codepointSize = 0; c < size; c += codepointSize) + { + int codepoint = GetCodepointNext(&lines[i][c], &codepointSize); + int index = GetGlyphIndex(guiFont, codepoint); + + // NOTE: Normally we exit the decoding sequence as soon as a bad byte is found (and return 0x3f) + // but we need to draw all of the bad bytes using the '?' symbol moving one byte + if (codepoint == 0x3f) codepointSize = 1; + + if (codepoint == '\n') break; // WARNING: Lines are already processed manually, no need to keep drawing after this codepoint + else + { + if ((codepoint != ' ') && (codepoint != '\t')) + { + // Draw only required text glyphs fitting the bounds.width + if (textOffsetX < (bounds.width - guiFont.recs[index].width)) + { + DrawTextCodepoint(guiFont, codepoint, RAYGUI_CLITERAL(Vector2){ position.x + textOffsetX, position.y + textOffsetY }, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), tint); + } + } + + if (guiFont.glyphs[index].advanceX == 0) textOffsetX += ((float)guiFont.recs[index].width*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + else textOffsetX += ((float)guiFont.glyphs[index].advanceX*scaleFactor + (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + } + } + + // TODO: Allow users to set line spacing for text: GuiSetStyle(TEXTBOX, TEXT_LINES_SPACING, x) + posOffsetY += (float)GuiGetStyle(DEFAULT, TEXT_SIZE)*1.5f; + //--------------------------------------------------------------------------------- + } } +#if defined(RAYGUI_DEBUG_TEXT_BOUNDS) + GuiDrawRectangle(bounds, 0, WHITE, Fade(RED, 0.4f)); +#endif } // Gui draw rectangle using default raygui plain style with borders @@ -3916,9 +4356,30 @@ static void GuiDrawRectangle(Rectangle rec, int borderWidth, Color borderColor, } } +// Draw tooltip using control bounds +static void GuiTooltip(Rectangle controlRec) +{ + if (!guiLocked && guiTooltip && (guiTooltipPtr != NULL) && !guiSliderDragging) + { + Vector2 textSize = MeasureTextEx(GuiGetFont(), guiTooltipPtr, (float)GuiGetStyle(DEFAULT, TEXT_SIZE), (float)GuiGetStyle(DEFAULT, TEXT_SPACING)); + + if ((controlRec.x + textSize.x + 16) > GetScreenWidth()) controlRec.x -= (textSize.x + 16 - controlRec.width); + + GuiPanel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.f }, NULL); + + int textPadding = GuiGetStyle(LABEL, TEXT_PADDING); + int textAlignment = GuiGetStyle(LABEL, TEXT_ALIGNMENT); + GuiSetStyle(LABEL, TEXT_PADDING, 0); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, TEXT_ALIGN_CENTER); + GuiLabel(RAYGUI_CLITERAL(Rectangle){ controlRec.x, controlRec.y + controlRec.height + 4, textSize.x + 16, GuiGetStyle(DEFAULT, TEXT_SIZE) + 8.f }, guiTooltipPtr); + GuiSetStyle(LABEL, TEXT_ALIGNMENT, textAlignment); + GuiSetStyle(LABEL, TEXT_PADDING, textPadding); + } +} + // Split controls text into multiple strings // Also check for multiple columns (required by GuiToggleGroup()) -static const char **GuiTextSplit(const char *text, int *count, int *textRow) +static const char **GuiTextSplit(const char *text, char delimiter, int *count, int *textRow) { // NOTE: Current implementation returns a copy of the provided string with '\0' (string end delimiter) // inserted between strings defined by "delimiter" parameter. No memory is dynamically allocated, @@ -3927,15 +4388,18 @@ static const char **GuiTextSplit(const char *text, int *count, int *textRow) // 2. Maximum size of text to split is RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE // NOTE: Those definitions could be externally provided if required + // TODO: HACK: GuiTextSplit() - Review how textRows are returned to user + // textRow is an externally provided array of integers that stores row number for every splitted string + #if !defined(RAYGUI_TEXTSPLIT_MAX_ITEMS) - #define RAYGUI_TEXTSPLIT_MAX_ITEMS 128 + #define RAYGUI_TEXTSPLIT_MAX_ITEMS 128 #endif #if !defined(RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE) - #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE 1024 + #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE 1024 #endif - static const char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL }; - static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 }; + static const char *result[RAYGUI_TEXTSPLIT_MAX_ITEMS] = { NULL }; // String pointers array (points to buffer data) + static char buffer[RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE] = { 0 }; // Buffer data (text input copy with '\0' added) memset(buffer, 0, RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE); result[0] = buffer; @@ -3948,7 +4412,7 @@ static const char **GuiTextSplit(const char *text, int *count, int *textRow) { buffer[i] = text[i]; if (buffer[i] == '\0') break; - else if ((buffer[i] == ';') || (buffer[i] == '\n')) + else if ((buffer[i] == delimiter) || (buffer[i] == '\n')) { result[counter] = buffer + i + 1; @@ -4100,10 +4564,12 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) GuiState state = guiState; // Is the scrollbar horizontal or vertical? - bool isVertical = (bounds.width > bounds.height) ? false : true; + bool isVertical = (bounds.width > bounds.height)? false : true; // The size (width or height depending on scrollbar type) of the spinner buttons - const int spinnerSize = GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE) ? (isVertical ? (int)bounds.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) : (int)bounds.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH)) : 0; + const int spinnerSize = GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE)? + (isVertical? (int)bounds.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) : + (int)bounds.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH)) : 0; // Arrow buttons [<] [>] [∧] [∨] Rectangle arrowUpLeft = { 0 }; @@ -4119,25 +4585,40 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) if (value > maxValue) value = maxValue; if (value < minValue) value = minValue; - const int range = maxValue - minValue; + const int valueRange = maxValue - minValue; int sliderSize = GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_SIZE); // Calculate rectangles for all of the components - arrowUpLeft = RAYGUI_CLITERAL(Rectangle) { (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize }; + arrowUpLeft = RAYGUI_CLITERAL(Rectangle){ + (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), + (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), + (float)spinnerSize, (float)spinnerSize }; if (isVertical) { - arrowDownRight = RAYGUI_CLITERAL(Rectangle) { (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + bounds.height - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize }; - scrollbar = RAYGUI_CLITERAL(Rectangle) { bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING), arrowUpLeft.y + arrowUpLeft.height, bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING)), bounds.height - arrowUpLeft.height - arrowDownRight.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) }; - sliderSize = (sliderSize >= scrollbar.height) ? ((int)scrollbar.height - 2) : sliderSize; // Make sure the slider won't get outside of the scrollbar - slider = RAYGUI_CLITERAL(Rectangle) { (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING), (float)scrollbar.y + (int)(((float)(value - minValue)/range)*(scrollbar.height - sliderSize)), (float)bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)), (float)sliderSize }; + arrowDownRight = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + bounds.height - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize }; + scrollbar = RAYGUI_CLITERAL(Rectangle){ bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING), arrowUpLeft.y + arrowUpLeft.height, bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING)), bounds.height - arrowUpLeft.height - arrowDownRight.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) }; + + // Make sure the slider won't get outside of the scrollbar + sliderSize = (sliderSize >= scrollbar.height)? ((int)scrollbar.height - 2) : sliderSize; + slider = RAYGUI_CLITERAL(Rectangle){ + bounds.x + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING), + scrollbar.y + (int)(((float)(value - minValue)/valueRange)*(scrollbar.height - sliderSize)), + bounds.width - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)), + (float)sliderSize }; } - else + else // horizontal { - arrowDownRight = RAYGUI_CLITERAL(Rectangle) { (float)bounds.x + bounds.width - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize }; - scrollbar = RAYGUI_CLITERAL(Rectangle) { arrowUpLeft.x + arrowUpLeft.width, bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING), bounds.width - arrowUpLeft.width - arrowDownRight.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH), bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING)) }; - sliderSize = (sliderSize >= scrollbar.width) ? ((int)scrollbar.width - 2) : sliderSize; // Make sure the slider won't get outside of the scrollbar - slider = RAYGUI_CLITERAL(Rectangle) { (float)scrollbar.x + (int)(((float)(value - minValue)/range)*(scrollbar.width - sliderSize)), (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING), (float)sliderSize, (float)bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)) }; + arrowDownRight = RAYGUI_CLITERAL(Rectangle){ (float)bounds.x + bounds.width - spinnerSize - GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH), (float)spinnerSize, (float)spinnerSize }; + scrollbar = RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x + arrowUpLeft.width, bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING), bounds.width - arrowUpLeft.width - arrowDownRight.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH), bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_PADDING)) }; + + // Make sure the slider won't get outside of the scrollbar + sliderSize = (sliderSize >= scrollbar.width)? ((int)scrollbar.width - 2) : sliderSize; + slider = RAYGUI_CLITERAL(Rectangle){ + scrollbar.x + (int)(((float)(value - minValue)/valueRange)*(scrollbar.width - sliderSize)), + bounds.y + GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING), + (float)sliderSize, + bounds.height - 2*(GuiGetStyle(SCROLLBAR, BORDER_WIDTH) + GuiGetStyle(SCROLLBAR, SCROLL_SLIDER_PADDING)) }; } // Update control @@ -4146,34 +4627,66 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) { Vector2 mousePoint = GetMousePosition(); - if (CheckCollisionPointRec(mousePoint, bounds)) + if (guiSliderDragging) // Keep dragging outside of bounds + { + if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) + { + if (CHECK_BOUNDS_ID(bounds, guiSliderActive)) + { + if (isVertical) value += (int)(GetMouseDelta().y/(scrollbar.height - slider.height)*valueRange); + else value += (int)(GetMouseDelta().x/(scrollbar.width - slider.width)*valueRange); + } + } + else + { + guiSliderDragging = false; + guiSliderActive = RAYGUI_CLITERAL(Rectangle){ 0, 0, 0, 0 }; + } + } + else if (CheckCollisionPointRec(mousePoint, bounds)) { state = STATE_FOCUSED; + guiSliderDragging = true; + guiSliderActive = bounds; // Store bounds as an identifier when dragging starts // Handle mouse wheel int wheel = (int)GetMouseWheelMove(); if (wheel != 0) value += wheel; + // Handle mouse button down if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON)) { - if (CheckCollisionPointRec(mousePoint, arrowUpLeft)) value -= range/GuiGetStyle(SCROLLBAR, SCROLL_SPEED); - else if (CheckCollisionPointRec(mousePoint, arrowDownRight)) value += range/GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + // Check arrows click + if (CheckCollisionPointRec(mousePoint, arrowUpLeft)) value -= valueRange/GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + else if (CheckCollisionPointRec(mousePoint, arrowDownRight)) value += valueRange/GuiGetStyle(SCROLLBAR, SCROLL_SPEED); + else if (!CheckCollisionPointRec(mousePoint, slider)) + { + // If click on scrollbar position but not on slider, place slider directly on that position + if (isVertical) value = (int)(((float)(mousePoint.y - scrollbar.y - slider.height/2)*valueRange)/(scrollbar.height - slider.height) + minValue); + else value = (int)(((float)(mousePoint.x - scrollbar.x - slider.width/2)*valueRange)/(scrollbar.width - slider.width) + minValue); + } state = STATE_PRESSED; } else if (IsMouseButtonDown(MOUSE_LEFT_BUTTON)) { - if (!isVertical) - { - Rectangle scrollArea = { arrowUpLeft.x + arrowUpLeft.width, arrowUpLeft.y, scrollbar.width, bounds.height - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH) }; - if (CheckCollisionPointRec(mousePoint, scrollArea)) value = (int)(((float)(mousePoint.x - scrollArea.x - slider.width/2)*range)/(scrollArea.width - slider.width) + minValue); - } - else - { - Rectangle scrollArea = { arrowUpLeft.x, arrowUpLeft.y+arrowUpLeft.height, bounds.width - 2*GuiGetStyle(SCROLLBAR, BORDER_WIDTH), scrollbar.height }; - if (CheckCollisionPointRec(mousePoint, scrollArea)) value = (int)(((float)(mousePoint.y - scrollArea.y - slider.height/2)*range)/(scrollArea.height - slider.height) + minValue); - } + if (isVertical) value += (int)(GetMouseDelta().y/(scrollbar.height - slider.height)*valueRange); + else value += (int)(GetMouseDelta().x/(scrollbar.width - slider.width)*valueRange); } + + // Keyboard control on mouse hover scrollbar + /* + if (isVertical) + { + if (IsKeyDown(KEY_DOWN)) value += 5; + else if (IsKeyDown(KEY_UP)) value -= 5; + } + else + { + if (IsKeyDown(KEY_RIGHT)) value += 5; + else if (IsKeyDown(KEY_LEFT)) value -= 5; + } + */ } // Normalize value @@ -4193,14 +4706,18 @@ static int GuiScrollBar(Rectangle bounds, int value, int minValue, int maxValue) if (GuiGetStyle(SCROLLBAR, ARROWS_VISIBLE)) { #if defined(RAYGUI_NO_ICONS) - GuiDrawText(isVertical ? "^" : "<", RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical ? bounds.width : bounds.height, isVertical ? bounds.width : bounds.height }, + GuiDrawText(isVertical? "^" : "<", + RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); - GuiDrawText(isVertical ? "v" : ">", RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical ? bounds.width : bounds.height, isVertical ? bounds.width : bounds.height }, + GuiDrawText(isVertical? "v" : ">", + RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(DROPDOWNBOX, TEXT + (state*3))), guiAlpha)); #else - GuiDrawText(isVertical ? "#121#" : "#118#", RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical ? bounds.width : bounds.height, isVertical ? bounds.width : bounds.height }, + GuiDrawText(isVertical? "#121#" : "#118#", + RAYGUI_CLITERAL(Rectangle){ arrowUpLeft.x, arrowUpLeft.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3)), guiAlpha)); // ICON_ARROW_UP_FILL / ICON_ARROW_LEFT_FILL - GuiDrawText(isVertical ? "#120#" : "#119#", RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical ? bounds.width : bounds.height, isVertical ? bounds.width : bounds.height }, + GuiDrawText(isVertical? "#120#" : "#119#", + RAYGUI_CLITERAL(Rectangle){ arrowDownRight.x, arrowDownRight.y, isVertical? bounds.width : bounds.height, isVertical? bounds.width : bounds.height }, TEXT_ALIGN_CENTER, Fade(GetColor(GuiGetStyle(SCROLLBAR, TEXT + state*3)), guiAlpha)); // ICON_ARROW_DOWN_FILL / ICON_ARROW_RIGHT_FILL #endif } @@ -4286,7 +4803,7 @@ const char **TextSplit(const char *text, char delimiter, int *count) // 2. Maximum size of text to split is RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE #if !defined(RAYGUI_TEXTSPLIT_MAX_ITEMS) - #define RAYGUI_TEXTSPLIT_MAX_ITEMS 128 + #define RAYGUI_TEXTSPLIT_MAX_ITEMS 128 #endif #if !defined(RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE) #define RAYGUI_TEXTSPLIT_MAX_TEXT_SIZE 1024 @@ -4384,107 +4901,43 @@ static const char *CodepointToUTF8(int codepoint, int *byteSize) // Total number of bytes processed are returned as a parameter // NOTE: the standard says U+FFFD should be returned in case of errors // but that character is not supported by the default font in raylib -static int GetCodepoint(const char *text, int *bytesProcessed) +static int GetCodepointNext(const char *text, int *codepointSize) { -/* - UTF-8 specs from https://www.ietf.org/rfc/rfc3629.txt + const char *ptr = text; + int codepoint = 0x3f; // Codepoint (defaults to '?') + *codepointSize = 1; - Char. number range | UTF-8 octet sequence - (hexadecimal) | (binary) - --------------------+--------------------------------------------- - 0000 0000-0000 007F | 0xxxxxxx - 0000 0080-0000 07FF | 110xxxxx 10xxxxxx - 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx - 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx -*/ - // NOTE: on decode errors we return as soon as possible - - int code = 0x3f; // Codepoint (defaults to '?') - int octet = (unsigned char)(text[0]); // The first UTF8 octet - *bytesProcessed = 1; - - if (octet <= 0x7f) + // Get current codepoint and bytes processed + if (0xf0 == (0xf8 & ptr[0])) { - // Only one octet (ASCII range x00-7F) - code = text[0]; + // 4 byte UTF-8 codepoint + if(((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80) || ((ptr[3] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks + codepoint = ((0x07 & ptr[0]) << 18) | ((0x3f & ptr[1]) << 12) | ((0x3f & ptr[2]) << 6) | (0x3f & ptr[3]); + *codepointSize = 4; } - else if ((octet & 0xe0) == 0xc0) + else if (0xe0 == (0xf0 & ptr[0])) { - // Two octets - - // [0]xC2-DF [1]UTF8-tail(x80-BF) - unsigned char octet1 = text[1]; - - if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence - - if ((octet >= 0xc2) && (octet <= 0xdf)) - { - code = ((octet & 0x1f) << 6) | (octet1 & 0x3f); - *bytesProcessed = 2; - } + // 3 byte UTF-8 codepoint */ + if(((ptr[1] & 0xC0) ^ 0x80) || ((ptr[2] & 0xC0) ^ 0x80)) { return codepoint; } //10xxxxxx checks + codepoint = ((0x0f & ptr[0]) << 12) | ((0x3f & ptr[1]) << 6) | (0x3f & ptr[2]); + *codepointSize = 3; } - else if ((octet & 0xf0) == 0xe0) + else if (0xc0 == (0xe0 & ptr[0])) { - // Three octets - unsigned char octet1 = text[1]; - unsigned char octet2 = '\0'; - - if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence - - octet2 = text[2]; - - if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence - - // [0]xE0 [1]xA0-BF [2]UTF8-tail(x80-BF) - // [0]xE1-EC [1]UTF8-tail [2]UTF8-tail(x80-BF) - // [0]xED [1]x80-9F [2]UTF8-tail(x80-BF) - // [0]xEE-EF [1]UTF8-tail [2]UTF8-tail(x80-BF) - - if (((octet == 0xe0) && !((octet1 >= 0xa0) && (octet1 <= 0xbf))) || - ((octet == 0xed) && !((octet1 >= 0x80) && (octet1 <= 0x9f)))) { *bytesProcessed = 2; return code; } - - if ((octet >= 0xe0) && (0 <= 0xef)) - { - code = ((octet & 0xf) << 12) | ((octet1 & 0x3f) << 6) | (octet2 & 0x3f); - *bytesProcessed = 3; - } + // 2 byte UTF-8 codepoint + if((ptr[1] & 0xC0) ^ 0x80) { return codepoint; } //10xxxxxx checks + codepoint = ((0x1f & ptr[0]) << 6) | (0x3f & ptr[1]); + *codepointSize = 2; } - else if ((octet & 0xf8) == 0xf0) + else if (0x00 == (0x80 & ptr[0])) { - // Four octets - if (octet > 0xf4) return code; - - unsigned char octet1 = text[1]; - unsigned char octet2 = '\0'; - unsigned char octet3 = '\0'; - - if ((octet1 == '\0') || ((octet1 >> 6) != 2)) { *bytesProcessed = 2; return code; } // Unexpected sequence - - octet2 = text[2]; - - if ((octet2 == '\0') || ((octet2 >> 6) != 2)) { *bytesProcessed = 3; return code; } // Unexpected sequence - - octet3 = text[3]; - - if ((octet3 == '\0') || ((octet3 >> 6) != 2)) { *bytesProcessed = 4; return code; } // Unexpected sequence - - // [0]xF0 [1]x90-BF [2]UTF8-tail [3]UTF8-tail - // [0]xF1-F3 [1]UTF8-tail [2]UTF8-tail [3]UTF8-tail - // [0]xF4 [1]x80-8F [2]UTF8-tail [3]UTF8-tail - - if (((octet == 0xf0) && !((octet1 >= 0x90) && (octet1 <= 0xbf))) || - ((octet == 0xf4) && !((octet1 >= 0x80) && (octet1 <= 0x8f)))) { *bytesProcessed = 2; return code; } // Unexpected sequence - - if (octet >= 0xf0) - { - code = ((octet & 0x7) << 18) | ((octet1 & 0x3f) << 12) | ((octet2 & 0x3f) << 6) | (octet3 & 0x3f); - *bytesProcessed = 4; - } + // 1 byte UTF-8 codepoint + codepoint = ptr[0]; + *codepointSize = 1; } - if (code > 0x10ffff) code = 0x3f; // Codepoints after U+10ffff are invalid - return code; + return codepoint; } #endif // RAYGUI_STANDALONE diff --git a/examples/shapes/shapes_draw_circle_sector.c b/examples/shapes/shapes_draw_circle_sector.c index c95f43643..1c283e151 100644 --- a/examples/shapes/shapes_draw_circle_sector.c +++ b/examples/shapes/shapes_draw_circle_sector.c @@ -35,8 +35,8 @@ int main(void) float outerRadius = 180.0f; float startAngle = 0.0f; float endAngle = 180.0f; - int segments = 0; - int minSegments = 4; + float segments = 10.0f; + float minSegments = 4; SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- @@ -58,16 +58,16 @@ int main(void) DrawLine(500, 0, 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.6f)); DrawRectangle(500, 0, GetScreenWidth() - 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.3f)); - DrawCircleSector(center, outerRadius, startAngle, endAngle, segments, Fade(MAROON, 0.3f)); - DrawCircleSectorLines(center, outerRadius, startAngle, endAngle, segments, Fade(MAROON, 0.6f)); + DrawCircleSector(center, outerRadius, startAngle, endAngle, (int)segments, Fade(MAROON, 0.3f)); + DrawCircleSectorLines(center, outerRadius, startAngle, endAngle, (int)segments, Fade(MAROON, 0.6f)); // Draw GUI controls //------------------------------------------------------------------------------ - startAngle = GuiSliderBar((Rectangle){ 600, 40, 120, 20}, "StartAngle", NULL, startAngle, 0, 720); - endAngle = GuiSliderBar((Rectangle){ 600, 70, 120, 20}, "EndAngle", NULL, endAngle, 0, 720); + GuiSliderBar((Rectangle){ 600, 40, 120, 20}, "StartAngle", NULL, &startAngle, 0, 720); + GuiSliderBar((Rectangle){ 600, 70, 120, 20}, "EndAngle", NULL, &endAngle, 0, 720); - outerRadius = GuiSliderBar((Rectangle){ 600, 140, 120, 20}, "Radius", NULL, outerRadius, 0, 200); - segments = (int)GuiSliderBar((Rectangle){ 600, 170, 120, 20}, "Segments", NULL, (float)segments, 0, 100); + GuiSliderBar((Rectangle){ 600, 140, 120, 20}, "Radius", NULL, &outerRadius, 0, 200); + GuiSliderBar((Rectangle){ 600, 170, 120, 20}, "Segments", NULL, &segments, 0, 100); //------------------------------------------------------------------------------ minSegments = (int)ceilf((endAngle - startAngle) / 90); diff --git a/examples/shapes/shapes_draw_rectangle_rounded.c b/examples/shapes/shapes_draw_rectangle_rounded.c index 8150e0946..a10e3c848 100644 --- a/examples/shapes/shapes_draw_rectangle_rounded.c +++ b/examples/shapes/shapes_draw_rectangle_rounded.c @@ -31,10 +31,10 @@ int main(void) InitWindow(screenWidth, screenHeight, "raylib [shapes] example - draw rectangle rounded"); float roundness = 0.2f; - int width = 200; - int height = 100; - int segments = 0; - int lineThick = 1; + float width = 200.0f; + float height = 100.0f; + float segments = 0.0f; + float lineThick = 1.0f; bool drawRect = false; bool drawRoundedRect = true; @@ -61,20 +61,20 @@ int main(void) DrawRectangle(560, 0, GetScreenWidth() - 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.3f)); if (drawRect) DrawRectangleRec(rec, Fade(GOLD, 0.6f)); - if (drawRoundedRect) DrawRectangleRounded(rec, roundness, segments, Fade(MAROON, 0.2f)); - if (drawRoundedLines) DrawRectangleRoundedLines(rec,roundness, segments, (float)lineThick, Fade(MAROON, 0.4f)); + if (drawRoundedRect) DrawRectangleRounded(rec, roundness, (int)segments, Fade(MAROON, 0.2f)); + if (drawRoundedLines) DrawRectangleRoundedLines(rec, roundness, (int)segments, lineThick, Fade(MAROON, 0.4f)); // Draw GUI controls //------------------------------------------------------------------------------ - width = (int)GuiSliderBar((Rectangle){ 640, 40, 105, 20 }, "Width", NULL, (float)width, 0, (float)GetScreenWidth() - 300); - height = (int)GuiSliderBar((Rectangle){ 640, 70, 105, 20 }, "Height", NULL, (float)height, 0, (float)GetScreenHeight() - 50); - roundness = GuiSliderBar((Rectangle){ 640, 140, 105, 20 }, "Roundness", NULL, roundness, 0.0f, 1.0f); - lineThick = (int)GuiSliderBar((Rectangle){ 640, 170, 105, 20 }, "Thickness", NULL, (float)lineThick, 0, 20); - segments = (int)GuiSliderBar((Rectangle){ 640, 240, 105, 20}, "Segments", NULL, (float)segments, 0, 60); + GuiSliderBar((Rectangle){ 640, 40, 105, 20 }, "Width", NULL, &width, 0, (float)GetScreenWidth() - 300); + GuiSliderBar((Rectangle){ 640, 70, 105, 20 }, "Height", NULL, &height, 0, (float)GetScreenHeight() - 50); + GuiSliderBar((Rectangle){ 640, 140, 105, 20 }, "Roundness", NULL, &roundness, 0.0f, 1.0f); + GuiSliderBar((Rectangle){ 640, 170, 105, 20 }, "Thickness", NULL, &lineThick, 0, 20); + GuiSliderBar((Rectangle){ 640, 240, 105, 20}, "Segments", NULL, &segments, 0, 60); - drawRoundedRect = GuiCheckBox((Rectangle){ 640, 320, 20, 20 }, "DrawRoundedRect", drawRoundedRect); - drawRoundedLines = GuiCheckBox((Rectangle){ 640, 350, 20, 20 }, "DrawRoundedLines", drawRoundedLines); - drawRect = GuiCheckBox((Rectangle){ 640, 380, 20, 20}, "DrawRect", drawRect); + GuiCheckBox((Rectangle){ 640, 320, 20, 20 }, "DrawRoundedRect", &drawRoundedRect); + GuiCheckBox((Rectangle){ 640, 350, 20, 20 }, "DrawRoundedLines", &drawRoundedLines); + GuiCheckBox((Rectangle){ 640, 380, 20, 20}, "DrawRect", &drawRect); //------------------------------------------------------------------------------ DrawText(TextFormat("MODE: %s", (segments >= 4)? "MANUAL" : "AUTO"), 640, 280, 10, (segments >= 4)? MAROON : DARKGRAY); diff --git a/examples/shapes/shapes_draw_ring.c b/examples/shapes/shapes_draw_ring.c index b001b9216..47327b7ad 100644 --- a/examples/shapes/shapes_draw_ring.c +++ b/examples/shapes/shapes_draw_ring.c @@ -37,7 +37,7 @@ int main(void) float startAngle = 0.0f; float endAngle = 360.0f; - int segments = 0; + float segments = 0.0f; bool drawRing = true; bool drawRingLines = false; @@ -63,23 +63,23 @@ int main(void) DrawLine(500, 0, 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.6f)); DrawRectangle(500, 0, GetScreenWidth() - 500, GetScreenHeight(), Fade(LIGHTGRAY, 0.3f)); - if (drawRing) DrawRing(center, innerRadius, outerRadius, startAngle, endAngle, segments, Fade(MAROON, 0.3f)); - if (drawRingLines) DrawRingLines(center, innerRadius, outerRadius, startAngle, endAngle, segments, Fade(BLACK, 0.4f)); - if (drawCircleLines) DrawCircleSectorLines(center, outerRadius, startAngle, endAngle, segments, Fade(BLACK, 0.4f)); + if (drawRing) DrawRing(center, innerRadius, outerRadius, startAngle, endAngle, (int)segments, Fade(MAROON, 0.3f)); + if (drawRingLines) DrawRingLines(center, innerRadius, outerRadius, startAngle, endAngle, (int)segments, Fade(BLACK, 0.4f)); + if (drawCircleLines) DrawCircleSectorLines(center, outerRadius, startAngle, endAngle, (int)segments, Fade(BLACK, 0.4f)); // Draw GUI controls //------------------------------------------------------------------------------ - startAngle = GuiSliderBar((Rectangle){ 600, 40, 120, 20 }, "StartAngle", NULL, startAngle, -450, 450); - endAngle = GuiSliderBar((Rectangle){ 600, 70, 120, 20 }, "EndAngle", NULL, endAngle, -450, 450); + GuiSliderBar((Rectangle){ 600, 40, 120, 20 }, "StartAngle", NULL, &startAngle, -450, 450); + GuiSliderBar((Rectangle){ 600, 70, 120, 20 }, "EndAngle", NULL, &endAngle, -450, 450); - innerRadius = GuiSliderBar((Rectangle){ 600, 140, 120, 20 }, "InnerRadius", NULL, innerRadius, 0, 100); - outerRadius = GuiSliderBar((Rectangle){ 600, 170, 120, 20 }, "OuterRadius", NULL, outerRadius, 0, 200); + GuiSliderBar((Rectangle){ 600, 140, 120, 20 }, "InnerRadius", NULL, &innerRadius, 0, 100); + GuiSliderBar((Rectangle){ 600, 170, 120, 20 }, "OuterRadius", NULL, &outerRadius, 0, 200); - segments = (int)GuiSliderBar((Rectangle){ 600, 240, 120, 20 }, "Segments", NULL, (float)segments, 0, 100); + GuiSliderBar((Rectangle){ 600, 240, 120, 20 }, "Segments", NULL, &segments, 0, 100); - drawRing = GuiCheckBox((Rectangle){ 600, 320, 20, 20 }, "Draw Ring", drawRing); - drawRingLines = GuiCheckBox((Rectangle){ 600, 350, 20, 20 }, "Draw RingLines", drawRingLines); - drawCircleLines = GuiCheckBox((Rectangle){ 600, 380, 20, 20 }, "Draw CircleLines", drawCircleLines); + GuiCheckBox((Rectangle){ 600, 320, 20, 20 }, "Draw Ring", &drawRing); + GuiCheckBox((Rectangle){ 600, 350, 20, 20 }, "Draw RingLines", &drawRingLines); + GuiCheckBox((Rectangle){ 600, 380, 20, 20 }, "Draw CircleLines", &drawCircleLines); //------------------------------------------------------------------------------ int minSegments = (int)ceilf((endAngle - startAngle)/90); From 2e00d16f3d3808defe76d9af75df2dd714c66af2 Mon Sep 17 00:00:00 2001 From: Charles Date: Sat, 10 Jun 2023 16:15:24 -0400 Subject: [PATCH 0111/1350] GLTF: fix segfault in animNormals memcpy when mesh.normals == NULL (#3103) --- src/rmodels.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rmodels.c b/src/rmodels.c index c4073eed3..23a5d16a0 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5201,7 +5201,9 @@ static Model LoadGLTF(const char *fileName) model.meshes[meshIndex].animVertices = RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float)); memcpy(model.meshes[meshIndex].animVertices, model.meshes[meshIndex].vertices, model.meshes[meshIndex].vertexCount*3*sizeof(float)); model.meshes[meshIndex].animNormals = RL_CALLOC(model.meshes[meshIndex].vertexCount*3, sizeof(float)); - memcpy(model.meshes[meshIndex].animNormals, model.meshes[meshIndex].normals, model.meshes[meshIndex].vertexCount*3*sizeof(float)); + if (model.meshes[meshIndex].normals != NULL) { + memcpy(model.meshes[meshIndex].animNormals, model.meshes[meshIndex].normals, model.meshes[meshIndex].vertexCount*3*sizeof(float)); + } meshIndex++; // Move to next mesh } From f385d0ce1cb71338c49025db43a78ac25ed0c35f Mon Sep 17 00:00:00 2001 From: Chema Guerra <33331554+chemaguerra@users.noreply.github.com> Date: Mon, 12 Jun 2023 08:18:31 +0200 Subject: [PATCH 0112/1350] Continuation of support for ES3/WebGL2 (#3107) * Continuation of support for ES3/WebGL2 * GetTouchPointState() * Amends to the WebGL2 PR --------- Co-authored-by: root Co-authored-by: chemguerra --- src/Makefile | 1 + src/rlgl.h | 52 ++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/src/Makefile b/src/Makefile index c6cd30e81..fc4c5b0c7 100644 --- a/src/Makefile +++ b/src/Makefile @@ -246,6 +246,7 @@ endif ifeq ($(PLATFORM),PLATFORM_WEB) # On HTML5 OpenGL ES 2.0 is used, emscripten translates it to WebGL 1.0 GRAPHICS = GRAPHICS_API_OPENGL_ES2 + #GRAPHICS = GRAPHICS_API_OPENGL_ES3 # Uncomment to use ES3/WebGL2 (preliminary support). endif ifeq ($(PLATFORM),PLATFORM_ANDROID) # By default use OpenGL ES 2.0 on Android diff --git a/src/rlgl.h b/src/rlgl.h index 5ba2593f2..8c101a79c 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -148,7 +148,8 @@ !defined(GRAPHICS_API_OPENGL_21) && \ !defined(GRAPHICS_API_OPENGL_33) && \ !defined(GRAPHICS_API_OPENGL_43) && \ - !defined(GRAPHICS_API_OPENGL_ES2) + !defined(GRAPHICS_API_OPENGL_ES2) && \ + !defined(GRAPHICS_API_OPENGL_ES3) #define GRAPHICS_API_OPENGL_33 #endif @@ -1711,7 +1712,7 @@ void rlDisableFramebuffer(void) // NOTE: One color buffer is always active by default void rlActiveDrawBuffers(int count) { -#if (defined(GRAPHICS_API_OPENGL_33) && defined(RLGL_RENDER_TEXTURES_HINT)) +#if ((defined(GRAPHICS_API_OPENGL_33) || defined(GRAPHICS_API_OPENGL_ES3)) && defined(RLGL_RENDER_TEXTURES_HINT)) // NOTE: Maximum number of draw buffers supported is implementation dependant, // it can be queried with glGet*() but it must be at least 8 //GLint maxDrawBuffers = 0; @@ -1723,6 +1724,16 @@ void rlActiveDrawBuffers(int count) else { unsigned int buffers[8] = { +#if defined(GRAPHICS_API_OPENGL_ES3) + GL_COLOR_ATTACHMENT0_EXT, + GL_COLOR_ATTACHMENT1_EXT, + GL_COLOR_ATTACHMENT2_EXT, + GL_COLOR_ATTACHMENT3_EXT, + GL_COLOR_ATTACHMENT4_EXT, + GL_COLOR_ATTACHMENT5_EXT, + GL_COLOR_ATTACHMENT6_EXT, + GL_COLOR_ATTACHMENT7_EXT, +#else GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, @@ -1731,9 +1742,14 @@ void rlActiveDrawBuffers(int count) GL_COLOR_ATTACHMENT5, GL_COLOR_ATTACHMENT6, GL_COLOR_ATTACHMENT7, +#endif }; +#if defined(GRAPHICS_API_OPENGL_ES3) + glDrawBuffersEXT(count, buffers); +#else glDrawBuffers(count, buffers); +#endif } } else TRACELOG(LOG_WARNING, "GL: One color buffer active by default"); @@ -2201,7 +2217,29 @@ void rlLoadExtensions(void *loader) #endif // GRAPHICS_API_OPENGL_33 -#if defined(GRAPHICS_API_OPENGL_ES2) +#if defined(GRAPHICS_API_OPENGL_ES3) + // Register supported extensions flags + // OpenGL ES 3.0 extensions supported by default + RLGL.ExtSupported.vao = true; + RLGL.ExtSupported.instancing = true; + RLGL.ExtSupported.texNPOT = true; + RLGL.ExtSupported.texFloat32 = true; + RLGL.ExtSupported.texDepth = true; + RLGL.ExtSupported.texDepthWebGL = true; + RLGL.ExtSupported.maxDepthBits = 24; + RLGL.ExtSupported.texAnisoFilter = true; + RLGL.ExtSupported.texMirrorClamp = true; + // TODO: Make sure that the ones above are actually present by default + // TODO: Check for these... + // RLGL.ExtSupported.texCompDXT + // RLGL.ExtSupported.texCompETC1 + // RLGL.ExtSupported.texCompETC2 + // RLGL.ExtSupported.texCompPVRT + // RLGL.ExtSupported.texCompASTC + // RLGL.ExtSupported.computeShader + // RLGL.ExtSupported.ssbo + // RLGL.ExtSupported.maxAnisotropyLevel +#elif defined(GRAPHICS_API_OPENGL_ES2) #if defined(PLATFORM_DESKTOP) // TODO: Support OpenGL ES 3.0 @@ -3057,7 +3095,7 @@ unsigned int rlLoadTextureDepth(int width, int height, bool useRenderBuffer) // Possible formats: GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32 and GL_DEPTH_COMPONENT32F unsigned int glInternalFormat = GL_DEPTH_COMPONENT; -#if defined(GRAPHICS_API_OPENGL_ES2) +#if (defined(GRAPHICS_API_OPENGL_ES2) || defined(GRAPHICS_API_OPENGL_ES3)) // WARNING: WebGL platform requires unsized internal format definition (GL_DEPTH_COMPONENT) // while other platforms using OpenGL ES 2.0 require/support sized internal formats depending on the GPU capabilities if (!RLGL.ExtSupported.texDepthWebGL || useRenderBuffer) @@ -3214,10 +3252,16 @@ void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned case RL_PIXELFORMAT_UNCOMPRESSED_R4G4B4A4: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_SHORT_4_4_4_4; break; case RL_PIXELFORMAT_UNCOMPRESSED_R8G8B8A8: *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_UNSIGNED_BYTE; break; #if !defined(GRAPHICS_API_OPENGL_11) + #if defined(GRAPHICS_API_OPENGL_ES3) + case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_R32F_EXT; *glFormat = GL_RED_EXT; *glType = GL_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB32F_EXT; *glFormat = GL_RGB; *glType = GL_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA32F_EXT; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; + #else case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float #endif + #endif #elif defined(GRAPHICS_API_OPENGL_33) case RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_R8; *glFormat = GL_RED; *glType = GL_UNSIGNED_BYTE; break; case RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_RG8; *glFormat = GL_RG; *glType = GL_UNSIGNED_BYTE; break; From 7392c4b0c592b45d113c7c869fbfdfc8d8de3d59 Mon Sep 17 00:00:00 2001 From: iacore <74560659+iacore@users.noreply.github.com> Date: Wed, 14 Jun 2023 18:46:10 +0000 Subject: [PATCH 0113/1350] Better examples/core_input_gamepad (#3110) * examples/core_input_gamepad: Add visuals for LT,RT * examples/core_input_gamepad: arrows left/right to choose gamepad * Style change --- examples/core/core_input_gamepad.c | 108 ++++++++++++++++------------- 1 file changed, 61 insertions(+), 47 deletions(-) diff --git a/examples/core/core_input_gamepad.c b/examples/core/core_input_gamepad.c index cd0c867d7..1eb516e85 100644 --- a/examples/core/core_input_gamepad.c +++ b/examples/core/core_input_gamepad.c @@ -49,6 +49,8 @@ int main(void) SetTargetFPS(60); // Set our game to run at 60 frames-per-second //-------------------------------------------------------------------------------------- + int gamepad = 0; // which gamepad to display + // Main game loop while (!WindowShouldClose()) // Detect window close button or ESC key { @@ -63,102 +65,114 @@ int main(void) ClearBackground(RAYWHITE); - if (IsGamepadAvailable(0)) - { - DrawText(TextFormat("GP1: %s", GetGamepadName(0)), 10, 10, 10, BLACK); + if (IsKeyPressed(KEY_LEFT) && gamepad > 0) gamepad--; + if (IsKeyPressed(KEY_RIGHT)) gamepad++; - if (TextIsEqual(GetGamepadName(0), XBOX360_NAME_ID) || TextIsEqual(GetGamepadName(0), XBOX360_LEGACY_NAME_ID)) + if (IsGamepadAvailable(gamepad)) + { + DrawText(TextFormat("GP%d: %s", gamepad, GetGamepadName(gamepad)), 10, 10, 10, BLACK); + + if (true) { DrawTexture(texXboxPad, 0, 0, DARKGRAY); // Draw buttons: xbox home - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_MIDDLE)) DrawCircle(394, 89, 19, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE)) DrawCircle(394, 89, 19, RED); // Draw buttons: basic - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_MIDDLE_RIGHT)) DrawCircle(436, 150, 9, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_MIDDLE_LEFT)) DrawCircle(352, 150, 9, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_LEFT)) DrawCircle(501, 151, 15, BLUE); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) DrawCircle(536, 187, 15, LIME); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT)) DrawCircle(572, 151, 15, MAROON); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_UP)) DrawCircle(536, 115, 15, GOLD); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_RIGHT)) DrawCircle(436, 150, 9, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_LEFT)) DrawCircle(352, 150, 9, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_LEFT)) DrawCircle(501, 151, 15, BLUE); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) DrawCircle(536, 187, 15, LIME); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT)) DrawCircle(572, 151, 15, MAROON); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_UP)) DrawCircle(536, 115, 15, GOLD); // Draw buttons: d-pad DrawRectangle(317, 202, 19, 71, BLACK); DrawRectangle(293, 228, 69, 19, BLACK); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_UP)) DrawRectangle(317, 202, 19, 26, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_DOWN)) DrawRectangle(317, 202 + 45, 19, 26, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_LEFT)) DrawRectangle(292, 228, 25, 19, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_RIGHT)) DrawRectangle(292 + 44, 228, 26, 19, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_UP)) DrawRectangle(317, 202, 19, 26, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_DOWN)) DrawRectangle(317, 202 + 45, 19, 26, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_LEFT)) DrawRectangle(292, 228, 25, 19, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_RIGHT)) DrawRectangle(292 + 44, 228, 26, 19, RED); // Draw buttons: left-right back - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_TRIGGER_1)) DrawCircle(259, 61, 20, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_TRIGGER_1)) DrawCircle(536, 61, 20, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_TRIGGER_1)) DrawCircle(259, 61, 20, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_TRIGGER_1)) DrawCircle(536, 61, 20, RED); // Draw axis: left joystick + + Color leftGamepadColor = BLACK; + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_THUMB)) leftGamepadColor = RED; DrawCircle(259, 152, 39, BLACK); DrawCircle(259, 152, 34, LIGHTGRAY); - DrawCircle(259 + (int)(GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X)*20), - 152 + (int)(GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y)*20), 25, BLACK); + DrawCircle(259 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_X)*20), + 152 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_Y)*20), 25, leftGamepadColor); // Draw axis: right joystick + Color rightGamepadColor = BLACK; + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_THUMB)) rightGamepadColor = RED; DrawCircle(461, 237, 38, BLACK); DrawCircle(461, 237, 33, LIGHTGRAY); - DrawCircle(461 + (int)(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_X)*20), - 237 + (int)(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_Y)*20), 25, BLACK); + DrawCircle(461 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_X)*20), + 237 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_Y)*20), 25, rightGamepadColor); // Draw axis: left-right triggers DrawRectangle(170, 30, 15, 70, GRAY); DrawRectangle(604, 30, 15, 70, GRAY); - DrawRectangle(170, 30, 15, (int)(((1 + GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_TRIGGER))/2)*70), RED); - DrawRectangle(604, 30, 15, (int)(((1 + GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_TRIGGER))/2)*70), RED); + DrawRectangle(170, 30, 15, (int)(((1 + GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_TRIGGER))/2)*70), RED); + DrawRectangle(604, 30, 15, (int)(((1 + GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_TRIGGER))/2)*70), RED); - //DrawText(TextFormat("Xbox axis LT: %02.02f", GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_TRIGGER)), 10, 40, 10, BLACK); - //DrawText(TextFormat("Xbox axis RT: %02.02f", GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_TRIGGER)), 10, 60, 10, BLACK); + //DrawText(TextFormat("Xbox axis LT: %02.02f", GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_TRIGGER)), 10, 40, 10, BLACK); + //DrawText(TextFormat("Xbox axis RT: %02.02f", GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_TRIGGER)), 10, 60, 10, BLACK); } - else if (TextIsEqual(GetGamepadName(0), PS3_NAME_ID)) + else if (TextIsEqual(GetGamepadName(gamepad), PS3_NAME_ID)) { DrawTexture(texPs3Pad, 0, 0, DARKGRAY); // Draw buttons: ps - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_MIDDLE)) DrawCircle(396, 222, 13, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE)) DrawCircle(396, 222, 13, RED); // Draw buttons: basic - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_MIDDLE_LEFT)) DrawRectangle(328, 170, 32, 13, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_MIDDLE_RIGHT)) DrawTriangle((Vector2){ 436, 168 }, (Vector2){ 436, 185 }, (Vector2){ 464, 177 }, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_UP)) DrawCircle(557, 144, 13, LIME); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT)) DrawCircle(586, 173, 13, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) DrawCircle(557, 203, 13, VIOLET); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_FACE_LEFT)) DrawCircle(527, 173, 13, PINK); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_LEFT)) DrawRectangle(328, 170, 32, 13, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_MIDDLE_RIGHT)) DrawTriangle((Vector2){ 436, 168 }, (Vector2){ 436, 185 }, (Vector2){ 464, 177 }, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_UP)) DrawCircle(557, 144, 13, LIME); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT)) DrawCircle(586, 173, 13, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_DOWN)) DrawCircle(557, 203, 13, VIOLET); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_FACE_LEFT)) DrawCircle(527, 173, 13, PINK); // Draw buttons: d-pad DrawRectangle(225, 132, 24, 84, BLACK); DrawRectangle(195, 161, 84, 25, BLACK); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_UP)) DrawRectangle(225, 132, 24, 29, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_DOWN)) DrawRectangle(225, 132 + 54, 24, 30, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_LEFT)) DrawRectangle(195, 161, 30, 25, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_FACE_RIGHT)) DrawRectangle(195 + 54, 161, 30, 25, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_UP)) DrawRectangle(225, 132, 24, 29, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_DOWN)) DrawRectangle(225, 132 + 54, 24, 30, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_LEFT)) DrawRectangle(195, 161, 30, 25, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_FACE_RIGHT)) DrawRectangle(195 + 54, 161, 30, 25, RED); // Draw buttons: left-right back buttons - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_LEFT_TRIGGER_1)) DrawCircle(239, 82, 20, RED); - if (IsGamepadButtonDown(0, GAMEPAD_BUTTON_RIGHT_TRIGGER_1)) DrawCircle(557, 82, 20, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_TRIGGER_1)) DrawCircle(239, 82, 20, RED); + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_TRIGGER_1)) DrawCircle(557, 82, 20, RED); // Draw axis: left joystick - DrawCircle(319, 255, 35, BLACK); + Color leftGamepadColor = BLACK; + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_LEFT_THUMB)) leftGamepadColor = RED; + DrawCircle(319, 255, 35, leftGamepadColor); DrawCircle(319, 255, 31, LIGHTGRAY); - DrawCircle(319 + (int)(GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) * 20), - 255 + (int)(GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_Y) * 20), 25, BLACK); + DrawCircle(319 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_X) * 20), + 255 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_Y) * 20), 25, leftGamepadColor); // Draw axis: right joystick + Color rightGamepadColor = BLACK; + if (IsGamepadButtonDown(gamepad, GAMEPAD_BUTTON_RIGHT_THUMB)) rightGamepadColor = RED; DrawCircle(475, 255, 35, BLACK); DrawCircle(475, 255, 31, LIGHTGRAY); - DrawCircle(475 + (int)(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_X) * 20), - 255 + (int)(GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_Y) * 20), 25, BLACK); + DrawCircle(475 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_X) * 20), + 255 + (int)(GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_Y) * 20), 25, rightGamepadColor); // Draw axis: left-right triggers DrawRectangle(169, 48, 15, 70, GRAY); DrawRectangle(611, 48, 15, 70, GRAY); - DrawRectangle(169, 48, 15, (int)(((1 - GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_TRIGGER)) / 2) * 70), RED); - DrawRectangle(611, 48, 15, (int)(((1 - GetGamepadAxisMovement(0, GAMEPAD_AXIS_RIGHT_TRIGGER)) / 2) * 70), RED); + DrawRectangle(169, 48, 15, (int)(((1 - GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_LEFT_TRIGGER)) / 2) * 70), RED); + DrawRectangle(611, 48, 15, (int)(((1 - GetGamepadAxisMovement(gamepad, GAMEPAD_AXIS_RIGHT_TRIGGER)) / 2) * 70), RED); } else { @@ -179,7 +193,7 @@ int main(void) } else { - DrawText("GP1: NOT DETECTED", 10, 10, 10, GRAY); + DrawText(TextFormat("GP%d: NOT DETECTED", gamepad), 10, 10, 10, GRAY); DrawTexture(texXboxPad, 0, 0, LIGHTGRAY); } From a0a18384cb2505437695c60a9c917ca13a82f0d4 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 16 Jun 2023 16:34:47 +0200 Subject: [PATCH 0114/1350] Fix typo --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index ee682e8b0..2701fe0e3 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -915,7 +915,7 @@ void InitWindow(int width, int height, const char *title) #endif #if defined(PLATFORM_WEB) - // Setup callback funtions for the DOM events + // Setup callback functions for the DOM events emscripten_set_fullscreenchange_callback("#canvas", NULL, 1, EmscriptenFullscreenChangeCallback); // WARNING: Below resize code was breaking fullscreen mode for sample games and examples, it needs review From 2209e5b0caa0b8b55bfea104b7102a2990797297 Mon Sep 17 00:00:00 2001 From: lesleyrs <19632758+lesleyrs@users.noreply.github.com> Date: Fri, 16 Jun 2023 18:08:10 +0200 Subject: [PATCH 0115/1350] fix window flags order (#3114) --- src/rcore.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 2701fe0e3..27b60ae4a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1492,13 +1492,6 @@ void ClearWindowState(unsigned int flags) CORE.Window.flags &= ~FLAG_WINDOW_RESIZABLE; } - // State change: FLAG_WINDOW_UNDECORATED - if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) && ((flags & FLAG_WINDOW_UNDECORATED) > 0)) - { - glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); - CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; - } - // State change: FLAG_WINDOW_HIDDEN if (((CORE.Window.flags & FLAG_WINDOW_HIDDEN) > 0) && ((flags & FLAG_WINDOW_HIDDEN) > 0)) { @@ -1518,6 +1511,13 @@ void ClearWindowState(unsigned int flags) RestoreWindow(); // NOTE: Window state flag updated inside function } + // State change: FLAG_WINDOW_UNDECORATED + if (((CORE.Window.flags & FLAG_WINDOW_UNDECORATED) > 0) && ((flags & FLAG_WINDOW_UNDECORATED) > 0)) + { + glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); + CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; + } + // State change: FLAG_WINDOW_UNFOCUSED if (((CORE.Window.flags & FLAG_WINDOW_UNFOCUSED) > 0) && ((flags & FLAG_WINDOW_UNFOCUSED) > 0)) { From 830e328df09bd597d9a0dbb974f5e7b54c6219f1 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 17 Jun 2023 16:48:18 +0200 Subject: [PATCH 0116/1350] Remove trailing spaces --- src/raylib.h | 2 +- src/rlgl.h | 2 +- src/rmodels.c | 2 +- src/rtextures.c | 14 +++++++------- src/utils.c | 2 +- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index f6a48bc73..cceaa2101 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1277,7 +1277,7 @@ RLAPI void ImageMipmaps(Image *image); RLAPI void ImageDither(Image *image, int rBpp, int gBpp, int bBpp, int aBpp); // Dither image data to 16bpp or lower (Floyd-Steinberg dithering) RLAPI void ImageFlipVertical(Image *image); // Flip image vertically RLAPI void ImageFlipHorizontal(Image *image); // Flip image horizontally -RLAPI void ImageRotate(Image *image, int degrees); // Rotate image by input angle in degrees (-359 to 359) +RLAPI void ImageRotate(Image *image, int degrees); // Rotate image by input angle in degrees (-359 to 359) RLAPI void ImageRotateCW(Image *image); // Rotate image clockwise 90deg RLAPI void ImageRotateCCW(Image *image); // Rotate image counter-clockwise 90deg RLAPI void ImageColorTint(Image *image, Color color); // Modify image color: tint diff --git a/src/rlgl.h b/src/rlgl.h index 8c101a79c..a104175c2 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -390,7 +390,7 @@ typedef enum { RL_OPENGL_33, // OpenGL 3.3 (GLSL 330) RL_OPENGL_43, // OpenGL 4.3 (using GLSL 330) RL_OPENGL_ES_20, // OpenGL ES 2.0 (GLSL 100) - RL_OPENGL_ES_30 // OpenGL ES 3.0 (GLSL 300 es) + RL_OPENGL_ES_30 // OpenGL ES 3.0 (GLSL 300 es) } rlGlVersion; // Trace log level diff --git a/src/rmodels.c b/src/rmodels.c index 23a5d16a0..73fb02353 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -5379,7 +5379,7 @@ static ModelAnimation *LoadModelAnimationsGLTF(const char *fileName, unsigned in strncpy(animations[i].name, animData.name, sizeof(animations[i].name)); animations[i].name[sizeof(animations[i].name) - 1] = '\0'; - + animations[i].frameCount = (int)(animDuration*1000.0f/GLTF_ANIMDELAY); animations[i].framePoses = RL_MALLOC(animations[i].frameCount*sizeof(Transform *)); diff --git a/src/rtextures.c b/src/rtextures.c index 7d76616c1..b6186d9e8 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -610,7 +610,7 @@ unsigned char *ExportImageToMemory(Image image, const char *fileType, int *dataS { unsigned char *fileData = NULL; *dataSize = 0; - + if ((image.width == 0) || (image.height == 0) || (image.data == NULL)) return NULL; #if defined(SUPPORT_IMAGE_EXPORT) @@ -629,7 +629,7 @@ unsigned char *ExportImageToMemory(Image image, const char *fileType, int *dataS #endif #endif - + return fileData; } @@ -713,7 +713,7 @@ Image GenImageColor(int width, int height, Color color) #if defined(SUPPORT_IMAGE_GENERATION) // Generate image: linear gradient -// The direction value specifies the direction of the gradient (in degrees) +// The direction value specifies the direction of the gradient (in degrees) // with 0 being vertical (from top to bottom), 90 being horizontal (from left to right). // The gradient effectively rotates counter-clockwise by the specified amount. Image GenImageGradientLinear(int width, int height, int direction, Color start, Color end) @@ -898,21 +898,21 @@ Image GenImagePerlinNoise(int width, int height, int offsetX, int offsetY, float { float nx = (float)(x + offsetX)*(scale/(float)width); float ny = (float)(y + offsetY)*(scale/(float)height); - + // Basic perlin noise implementation (not used) //float p = (stb_perlin_noise3(nx, ny, 0.0f, 0, 0, 0); - + // Calculate a better perlin noise using fbm (fractal brownian motion) // Typical values to start playing with: // lacunarity = ~2.0 -- spacing between successive octaves (use exactly 2.0 for wrapping output) // gain = 0.5 -- relative weighting applied to each successive octave // octaves = 6 -- number of "octaves" of noise3() to sum float p = stb_perlin_fbm_noise3(nx, ny, 1.0f, 2.0f, 0.5f, 6); - + // Clamp between -1.0f and 1.0f if (p < -1.0f) p = -1.0f; if (p > 1.0f) p = 1.0f; - + // We need to normalize the data from [-1..1] to [0..1] float np = (p + 1.0f)/2.0f; diff --git a/src/utils.c b/src/utils.c index aa2bfc40e..ac57a2157 100644 --- a/src/utils.c +++ b/src/utils.c @@ -348,7 +348,7 @@ char *LoadFileText(const char *fileName) if (size > 0) { text = (char *)RL_MALLOC((size + 1)*sizeof(char)); - + if (text != NULL) { unsigned int count = (unsigned int)fread(text, sizeof(char), size, file); From 3a90acf08ee97ba42bc21decfc52b41c7ef4d68d Mon Sep 17 00:00:00 2001 From: Dante Catalfamo <43040593+dantecatalfamo@users.noreply.github.com> Date: Sun, 18 Jun 2023 05:48:50 -0400 Subject: [PATCH 0117/1350] Add options to zig compile (#3115) * Add options to zig compile options Support for compiling with raygui, raymath, and physac. Also outputs the required headers. Raygui should be located `../raygui` relative to the repo root Physac should be located `../physac` relative to the repo root This behavior matches options in the Makefile * Move Options struct * Remove physac, explicit raymath, always copy rlgl.h and raymath.h * Remove unused options from build.zig * Add srcdir as include path for raygui.h --- src/build.zig | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/build.zig b/src/build.zig index 59d8241e7..8485322d5 100644 --- a/src/build.zig +++ b/src/build.zig @@ -1,7 +1,7 @@ const std = @import("std"); // This has been tested to work with zig master branch as of commit 87de821 or May 14 2023 -pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep { +pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.builtin.OptimizeMode, options: Options) *std.Build.CompileStep { const raylib_flags = &[_][]const u8{ "-std=gnu99", "-D_GNU_SOURCE", @@ -28,6 +28,16 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built srcdir ++ "/utils.c", }, raylib_flags); + var gen_step = std.build.Step.WriteFile.create(b); + raylib.step.dependOn(&gen_step.step); + + if (options.raygui) { + _ = gen_step.add(srcdir ++ "/raygui.c", "#define RAYGUI_IMPLEMENTATION\n#include \"raygui.h\"\n"); + raylib.addCSourceFile(srcdir ++ "/raygui.c", raylib_flags); + raylib.addIncludePath(srcdir); + raylib.addIncludePath(srcdir ++ "/../../raygui/src"); + } + switch (target.getOsTag()) { .windows => { raylib.addCSourceFiles(&.{srcdir ++ "/rglfw.c"}, raylib_flags); @@ -105,6 +115,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built return raylib; } +const Options = struct { + raygui: bool = false, +}; + pub fn build(b: *std.Build) void { // Standard target options allows the person running `zig build` to choose // what target to build for. Here we do not override the defaults, which @@ -116,8 +130,20 @@ pub fn build(b: *std.Build) void { // set a preferred release mode, allowing the user to decide how to optimize. const optimize = b.standardOptimizeOption(.{}); - const lib = addRaylib(b, target, optimize); + const raygui = b.option(bool, "raygui", "Compile with raygui support"); + + const lib = addRaylib(b, target, optimize, .{ + .raygui = raygui orelse false, + }); + lib.installHeader("src/raylib.h", "raylib.h"); + lib.installHeader("src/raymath.h", "raymath.h"); + lib.installHeader("src/rlgl.h", "rlgl.h"); + + if (raygui orelse false) { + lib.installHeader("../raygui/src/raygui.h", "raygui.h"); + } + b.installArtifact(lib); } From 5e1a81555ca130e2c6544add0e2391a8763e7e2a Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Tue, 20 Jun 2023 11:01:57 +0200 Subject: [PATCH 0118/1350] Review `android_main()` in rcore.c (#3121) --- src/rcore.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 27b60ae4a..d02ea2b9b 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -717,17 +717,17 @@ void android_main(struct android_app *app) char arg0[] = "raylib"; // NOTE: argv[] are mutable CORE.Android.app = app; - // NOTE: Return codes != 0 are skipped - (void)main(1, (char *[]) { arg0, NULL }); + // NOTE: We get the main return for exit() + int ret = main(1, (char *[]) { arg0, NULL }); - // Finish native activity - ANativeActivity_finish(CORE.Android.app->activity); + // Request to end the native activity + ANativeActivity_finish(app->activity); // Android ALooper_pollAll() variables int pollResult = 0; int pollEvents = 0; - // Wait for app events to close + // Waiting for application events before complete finishing while (!CORE.Android.app->destroyRequested) { while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void **)&CORE.Android.source)) >= 0) @@ -736,8 +736,11 @@ void android_main(struct android_app *app) } } - // WARNING: Check for deallocation and ensure no other processes are running from the application - exit(0); // Closes the application completely without going through Java + // WARNING: Make sure you free resources properly and no other process is running from Java code or other. + // NOTE: You can use JNI to call a NativeLoader method (which will call finish() from the UI thread) + // to handle the full close from Java, without using exit(0) like here. + + exit(ret); // Close the application directly, without going through Java } // NOTE: Add this to header (if apps really need it) From fa698fb05e7687c8a43b76d9e1ca9c6a12d99b59 Mon Sep 17 00:00:00 2001 From: Ian Rash Date: Sat, 24 Jun 2023 04:22:23 -0700 Subject: [PATCH 0119/1350] Update BINDINGS.md for Crystal (#3132) Just updated --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index a60979fc4..272bc2da5 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -13,7 +13,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | cl-raylib | 4.0 | [Common Lisp](https://common-lisp.net/) | MIT | https://github.com/longlene/cl-raylib | | claylib/wrap | **4.5** | [Common Lisp](https://common-lisp.net/) | Zlib | https://github.com/defun-games/claylib | | chez-raylib | **auto** | [Chez Scheme](https://cisco.github.io/ChezScheme/) | GPLv3 | https://github.com/Yunoinsky/chez-raylib | -| raylib-cr | **4.5-dev (7e7939e)** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | +| raylib-cr | **4.6-dev (5e1a81)** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | | ray-cyber | 4.5 | [Cyber](https://cyberscript.dev) | MIT | https://github.com/fubark/ray-cyber | | raylib-c3 | **4.5** | [C3](https://c3-lang.org/) | MIT | https://github.com/Its-Kenta/Raylib-C3 | | dart-raylib | 4.0 | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | From 974460b0723c4ab550779fbc46fb88f4cf6cac41 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 24 Jun 2023 13:26:43 +0200 Subject: [PATCH 0120/1350] REVIEWED: `rlLoadShaderBuffer()` #3104 --- src/rlgl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rlgl.h b/src/rlgl.h index a104175c2..470295b0a 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -4143,7 +4143,7 @@ unsigned int rlLoadShaderBuffer(unsigned int size, const void *data, int usageHi glGenBuffers(1, &ssbo); glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); glBufferData(GL_SHADER_STORAGE_BUFFER, size, data, usageHint? usageHint : RL_STREAM_COPY); - glClearBufferData(GL_SHADER_STORAGE_BUFFER, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, 0); + if (data == NULL) glClearBufferData(GL_SHADER_STORAGE_BUFFER, GL_R8UI, GL_RED_INTEGER, GL_UNSIGNED_BYTE, NULL); // Clear buffer data to 0 glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); #endif From 2d518bfbcdaf673c813d6fc56218d906678f42be Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 24 Jun 2023 13:32:13 +0200 Subject: [PATCH 0121/1350] REVIEWED: `ProcessMaterialsOBJ()` #3125 --- src/rmodels.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 73fb02353..49f922695 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -1857,35 +1857,34 @@ bool ExportMesh(Mesh mesh, const char *fileName) #if defined(SUPPORT_FILEFORMAT_OBJ) || defined(SUPPORT_FILEFORMAT_MTL) // Process obj materials -static void ProcessMaterialsOBJ(Material *rayMaterials, tinyobj_material_t *materials, int materialCount) +static void ProcessMaterialsOBJ(Material *materials, tinyobj_material_t *mats, int materialCount) { - // Init model materials + // Init model mats for (int m = 0; m < materialCount; m++) { // Init material to default // NOTE: Uses default shader, which only supports MATERIAL_MAP_DIFFUSE - rayMaterials[m] = LoadMaterialDefault(); + materials[m] = LoadMaterialDefault(); // Get default texture, in case no texture is defined // NOTE: rlgl default texture is a 1x1 pixel UNCOMPRESSED_R8G8B8A8 - rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; + materials[m].maps[MATERIAL_MAP_DIFFUSE].texture = (Texture2D){ rlGetTextureIdDefault(), 1, 1, 1, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8 }; - if (materials[m].diffuse_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTexture(materials[m].diffuse_texname); //char *diffuse_texname; // map_Kd + if (mats[m].diffuse_texname != NULL) materials[m].maps[MATERIAL_MAP_DIFFUSE].texture = LoadTexture(mats[m].diffuse_texname); //char *diffuse_texname; // map_Kd + else materials[m].maps[MATERIAL_MAP_DIFFUSE].color = (Color){ (unsigned char)(mats[m].diffuse[0]*255.0f), (unsigned char)(mats[m].diffuse[1]*255.0f), (unsigned char)(mats[m].diffuse[2] * 255.0f), 255 }; //float diffuse[3]; + materials[m].maps[MATERIAL_MAP_DIFFUSE].value = 0.0f; - rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].color = (Color){ (unsigned char)(materials[m].diffuse[0]*255.0f), (unsigned char)(materials[m].diffuse[1]*255.0f), (unsigned char)(materials[m].diffuse[2] * 255.0f), 255 }; //float diffuse[3]; - rayMaterials[m].maps[MATERIAL_MAP_DIFFUSE].value = 0.0f; + if (mats[m].specular_texname != NULL) materials[m].maps[MATERIAL_MAP_SPECULAR].texture = LoadTexture(mats[m].specular_texname); //char *specular_texname; // map_Ks + materials[m].maps[MATERIAL_MAP_SPECULAR].color = (Color){ (unsigned char)(mats[m].specular[0]*255.0f), (unsigned char)(mats[m].specular[1]*255.0f), (unsigned char)(mats[m].specular[2] * 255.0f), 255 }; //float specular[3]; + materials[m].maps[MATERIAL_MAP_SPECULAR].value = 0.0f; - if (materials[m].specular_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].texture = LoadTexture(materials[m].specular_texname); //char *specular_texname; // map_Ks - rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].color = (Color){ (unsigned char)(materials[m].specular[0]*255.0f), (unsigned char)(materials[m].specular[1]*255.0f), (unsigned char)(materials[m].specular[2] * 255.0f), 255 }; //float specular[3]; - rayMaterials[m].maps[MATERIAL_MAP_SPECULAR].value = 0.0f; + if (mats[m].bump_texname != NULL) materials[m].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture(mats[m].bump_texname); //char *bump_texname; // map_bump, bump + materials[m].maps[MATERIAL_MAP_NORMAL].color = WHITE; + materials[m].maps[MATERIAL_MAP_NORMAL].value = mats[m].shininess; - if (materials[m].bump_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_NORMAL].texture = LoadTexture(materials[m].bump_texname); //char *bump_texname; // map_bump, bump - rayMaterials[m].maps[MATERIAL_MAP_NORMAL].color = WHITE; - rayMaterials[m].maps[MATERIAL_MAP_NORMAL].value = materials[m].shininess; + materials[m].maps[MATERIAL_MAP_EMISSION].color = (Color){ (unsigned char)(mats[m].emission[0]*255.0f), (unsigned char)(mats[m].emission[1]*255.0f), (unsigned char)(mats[m].emission[2] * 255.0f), 255 }; //float emission[3]; - rayMaterials[m].maps[MATERIAL_MAP_EMISSION].color = (Color){ (unsigned char)(materials[m].emission[0]*255.0f), (unsigned char)(materials[m].emission[1]*255.0f), (unsigned char)(materials[m].emission[2] * 255.0f), 255 }; //float emission[3]; - - if (materials[m].displacement_texname != NULL) rayMaterials[m].maps[MATERIAL_MAP_HEIGHT].texture = LoadTexture(materials[m].displacement_texname); //char *displacement_texname; // disp + if (mats[m].displacement_texname != NULL) materials[m].maps[MATERIAL_MAP_HEIGHT].texture = LoadTexture(mats[m].displacement_texname); //char *displacement_texname; // disp } } #endif From 0c126af7171e51fff9f94c4f2e498f43f60d617b Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Sat, 24 Jun 2023 14:38:09 -0700 Subject: [PATCH 0122/1350] casting warnings in rtextures (#3134) --- src/rtextures.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index b6186d9e8..2d422b179 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -721,8 +721,8 @@ Image GenImageGradientLinear(int width, int height, int direction, Color start, Color *pixels = (Color *)RL_MALLOC(width*height*sizeof(Color)); float radianDirection = (float)(90 - direction)/180.f*3.14159f; - float cosDir = cos(radianDirection); - float sinDir = sin(radianDirection); + float cosDir = cosf(radianDirection); + float sinDir = sinf(radianDirection); for (int i = 0; i < width; i++) { @@ -812,7 +812,7 @@ Image GenImageGradientSquare(int width, int height, float density, Color inner, float normalizedDistY = distY / centerY; // Calculate the total normalized Manhattan distance - float manhattanDist = fmax(normalizedDistX, normalizedDistY); + float manhattanDist = fmaxf(normalizedDistX, normalizedDistY); // Subtract the density from the manhattanDist, then divide by (1 - density) // This makes the gradient start from the center when density is 0, and from the edge when density is 1 @@ -2157,11 +2157,11 @@ void ImageRotate(Image *image, int degrees) else { float rad = degrees*PI/180.0f; - float sinRadius = sin(rad); - float cosRadius = cos(rad); + float sinRadius = sinf(rad); + float cosRadius = cosf(rad); - int width = fabsf(image->width*cosRadius) + fabsf(image->height*sinRadius); - int height = fabsf(image->height*cosRadius) + fabsf(image->width*sinRadius); + int width = (int)(fabsf(image->width*cosRadius) + fabsf(image->height*sinRadius)); + int height = (int)(fabsf(image->height*cosRadius) + fabsf(image->width*sinRadius)); int bytesPerPixel = GetPixelDataSize(1, 1, image->format); unsigned char *rotatedData = (unsigned char *)RL_CALLOC(width*height, bytesPerPixel); From 5834e970ebb50edc95cb5b5dbc6dd772f99c366f Mon Sep 17 00:00:00 2001 From: Kenta <106167071+Its-Kenta@users.noreply.github.com> Date: Tue, 27 Jun 2023 21:03:42 +0100 Subject: [PATCH 0123/1350] Update BINDINGS.md (#3138) Change C3 to Zlib Update Raylib-nelua's name as Raylib.nelua to reflect repo change. Update Raylib.nelua licence to Zlib --- BINDINGS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BINDINGS.md b/BINDINGS.md index 272bc2da5..f040599ca 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -15,7 +15,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | chez-raylib | **auto** | [Chez Scheme](https://cisco.github.io/ChezScheme/) | GPLv3 | https://github.com/Yunoinsky/chez-raylib | | raylib-cr | **4.6-dev (5e1a81)** | [Crystal](https://crystal-lang.org/) | Apache-2.0 | https://github.com/sol-vin/raylib-cr | | ray-cyber | 4.5 | [Cyber](https://cyberscript.dev) | MIT | https://github.com/fubark/ray-cyber | -| raylib-c3 | **4.5** | [C3](https://c3-lang.org/) | MIT | https://github.com/Its-Kenta/Raylib-C3 | +| raylib-c3 | **4.5** | [C3](https://c3-lang.org/) | Zlib | https://github.com/Its-Kenta/Raylib-C3 | | dart-raylib | 4.0 | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | | bindbc-raylib3 | 4.0 | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | | dray | 4.2 | [D](https://dlang.org/) | Apache-2.0 | https://github.com/redthing1/dray | @@ -40,7 +40,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-lua | **4.5** | [Lua](http://www.lua.org/) | ISC | https://github.com/TSnake41/raylib-lua | | raylua | 4.0 | [Lua](http://www.lua.org/) | MIT | https://github.com/Rabios/raylua | | nelua-raylib | 4.0 | [nelua](https://nelua.io/) | MIT | https://github.com/AKDev21/nelua-raylib | -| Raylib-Nelua | **4.5-dev** | [nelua](https://nelua.io/) | MIT | https://github.com/Its-Kenta/Raylib-Nelua | +| Raylib.nelua | **4.5** | [nelua](https://nelua.io/) | Zlib | https://github.com/Its-Kenta/Raylib-Nelua | | NimraylibNow! | 4.2 | [Nim](https://nim-lang.org/) | MIT | https://github.com/greenfork/nimraylib_now | | raylib-Forever | auto | [Nim](https://nim-lang.org/) | ? | https://github.com/Guevara-chan/Raylib-Forever | | naylib | auto | [Nim](https://nim-lang.org/) | MIT | https://github.com/planetis-m/naylib | From ceafbcf9d2d722886c40d0ba12eaad2f232a2782 Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 28 Jun 2023 00:37:24 +0200 Subject: [PATCH 0124/1350] REVIEWED: `SetShapesTexture()`, allow reseting --- src/rshapes.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index 278886423..d726ff0e8 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -80,7 +80,7 @@ //---------------------------------------------------------------------------------- // Global Variables Definition //---------------------------------------------------------------------------------- -Texture2D texShapes = { 1, 1, 1, 1, 7 }; // Texture used on shapes drawing (usually a white pixel) +Texture2D texShapes = { 1, 1, 1, 1, 7 }; // Texture used on shapes drawing (white pixel loaded by rlgl) Rectangle texShapesRec = { 0.0f, 0.0f, 1.0f, 1.0f }; // Texture source rectangle used on shapes drawing //---------------------------------------------------------------------------------- @@ -97,8 +97,19 @@ static float EaseCubicInOut(float t, float b, float c, float d); // Cubic eas // defining a font char white rectangle would allow drawing everything in a single draw call void SetShapesTexture(Texture2D texture, Rectangle source) { - texShapes = texture; - texShapesRec = source; + // Reset texture to default pixel if required + // WARNING: Shapes texture should be probably better validated, + // it can break the rendering of all shapes if missused + if ((texture.id == 0) || (source.width == 0) || (source.height == 0)) + { + texShapes = (Texture2D){ 1, 1, 1, 1, 7 }; + texShapesRec = (Rectangle){ 0.0f, 0.0f, 1.0f, 1.0f }; + } + else + { + texShapes = texture; + texShapesRec = source; + } } // Draw a pixel From e190b7eee9199b681a8c50fb69f2fce07e92c7af Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 30 Jun 2023 09:47:16 +0200 Subject: [PATCH 0125/1350] UPDATED: `sdefl` and `sinfl` compression libraries --- src/external/sdefl.h | 195 ++++++++++++++++++++++++++++++------------- src/external/sinfl.h | 28 ++++--- 2 files changed, 155 insertions(+), 68 deletions(-) diff --git a/src/external/sdefl.h b/src/external/sdefl.h index 56539a56e..36015b95b 100644 --- a/src/external/sdefl.h +++ b/src/external/sdefl.h @@ -71,7 +71,7 @@ Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License -Copyright (c) 2020 Micha Mettke +Copyright (c) 2020-2023 Micha Mettke Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to @@ -125,7 +125,7 @@ extern "C" { #define SDEFL_MIN_MATCH 4 #define SDEFL_BLK_MAX (256*1024) -#define SDEFL_SEQ_SIZ ((SDEFL_BLK_MAX + SDEFL_MIN_MATCH)/SDEFL_MIN_MATCH) +#define SDEFL_SEQ_SIZ ((SDEFL_BLK_MAX+2)/3) #define SDEFL_SYM_MAX (288) #define SDEFL_OFF_MAX (32) @@ -185,6 +185,7 @@ extern int zsdeflate(struct sdefl *s, void *o, const void *i, int n, int lvl); #define SDEFL_MAX_CODE_LEN (15) #define SDEFL_SYM_BITS (10u) #define SDEFL_SYM_MSK ((1u << SDEFL_SYM_BITS)-1u) +#define SDEFL_RAW_BLK_SIZE (65535) #define SDEFL_LIT_LEN_CODES (14) #define SDEFL_OFF_CODES (15) #define SDEFL_PRE_CODES (7) @@ -192,6 +193,7 @@ extern int zsdeflate(struct sdefl *s, void *o, const void *i, int n, int lvl); #define SDEFL_EOB (256) #define sdefl_npow2(n) (1 << (sdefl_ilog2((n)-1) + 1)) +#define sdefl_div_round_up(n,d) (((n)+((d)-1))/(d)) static int sdefl_ilog2(int n) { @@ -438,12 +440,12 @@ sdefl_precode(struct sdefl_symcnt *cnt, unsigned *freqs, unsigned *items, } while (run_start != total); cnt->items = (int)(at - items); } -struct sdefl_match_codes { +struct sdefl_match_codest { int ls, lc; int dc, dx; }; static void -sdefl_match_codes(struct sdefl_match_codes *cod, int dist, int len) { +sdefl_match_codes(struct sdefl_match_codest *cod, int dist, int len) { static const short dxmax[] = {0,6,12,24,48,96,192,384,768,1536,3072,6144,12288,24576}; static const unsigned char lslot[258+1] = { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, @@ -471,6 +473,44 @@ sdefl_match_codes(struct sdefl_match_codes *cod, int dist, int len) { cod->dx = sdefl_ilog2(sdefl_npow2(dist) >> 2); cod->dc = cod->dx ? ((cod->dx + 1) << 1) + (dist > dxmax[cod->dx]) : dist-1; } +enum sdefl_blk_type { + SDEFL_BLK_UCOMPR, + SDEFL_BLK_DYN +}; +static enum sdefl_blk_type +sdefl_blk_type(const struct sdefl *s, int blk_len, int pre_item_len, + const unsigned *pre_freq, const unsigned char *pre_len) { + static const unsigned char x_pre_bits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + static const unsigned char x_len_bits[] = {0,0,0,0,0,0,0,0, 1,1,1,1,2,2,2,2, + 3,3,3,3,4,4,4,4, 5,5,5,5,0}; + static const unsigned char x_off_bits[] = {0,0,0,0,1,1,2,2, 3,3,4,4,5,5,6,6, + 7,7,8,8,9,9,10,10, 11,11,12,12,13,13}; + + int dyn_cost = 0; + int fix_cost = 0; + int sym = 0; + + dyn_cost += 5 + 5 + 4 + (3 * pre_item_len); + for (sym = 0; sym < SDEFL_PRE_MAX; sym++) + dyn_cost += pre_freq[sym] * (x_pre_bits[sym] + pre_len[sym]); + for (sym = 0; sym < 256; sym++) + dyn_cost += s->freq.lit[sym] * s->cod.len.lit[sym]; + dyn_cost += s->cod.len.lit[SDEFL_EOB]; + for (sym = 257; sym < 286; sym++) + dyn_cost += s->freq.lit[sym] * (x_len_bits[sym - 257] + s->cod.len.lit[sym]); + for (sym = 0; sym < 30; sym++) + dyn_cost += s->freq.off[sym] * (x_off_bits[sym] + s->cod.len.off[sym]); + + fix_cost += 8*(5 * sdefl_div_round_up(blk_len, SDEFL_RAW_BLK_SIZE) + blk_len + 1 + 2); + return (dyn_cost < fix_cost) ? SDEFL_BLK_DYN : SDEFL_BLK_UCOMPR; +} +static void +sdefl_put16(unsigned char **dst, unsigned short x) { + unsigned char *val = *dst; + val[0] = (unsigned char)(x & 0xff); + val[1] = (unsigned char)(x >> 8); + *dst = val + 2; +} static void sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) { static const char lxn[] = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; @@ -479,7 +519,7 @@ sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) { static const short dmin[] = {1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257, 385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577}; - struct sdefl_match_codes cod; + struct sdefl_match_codest cod; sdefl_match_codes(&cod, dist, len); sdefl_put(dst, s, (int)s->cod.word.lit[cod.lc], s->cod.len.lit[cod.lc]); sdefl_put(dst, s, len - lmin[cod.ls], lxn[cod.ls]); @@ -488,7 +528,8 @@ sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) { } static void sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, - const unsigned char *in) { + const unsigned char *in, int blk_begin, int blk_end) { + int blk_len = blk_end - blk_begin; int j, i = 0, item_cnt = 0; struct sdefl_symcnt symcnt = {0}; unsigned codes[SDEFL_PRE_MAX]; @@ -498,7 +539,7 @@ sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, static const unsigned char perm[SDEFL_PRE_MAX] = {16,17,18,0,8,7,9,6,10,5,11, 4,12,3,13,2,14,1,15}; - /* huffman codes */ + /* calculate huffman codes */ s->freq.lit[SDEFL_EOB]++; sdefl_huff(s->cod.len.lit, s->cod.word.lit, s->freq.lit, SDEFL_SYM_MAX, SDEFL_LIT_LEN_CODES); sdefl_huff(s->cod.len.off, s->cod.word.off, s->freq.off, SDEFL_OFF_MAX, SDEFL_OFF_CODES); @@ -509,35 +550,58 @@ sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, break; } } - /* block header */ - sdefl_put(dst, s, is_last ? 0x01 : 0x00, 1); /* block */ - sdefl_put(dst, s, 0x02, 2); /* dynamic huffman */ - sdefl_put(dst, s, symcnt.lit - 257, 5); - sdefl_put(dst, s, symcnt.off - 1, 5); - sdefl_put(dst, s, item_cnt - 4, 4); - for (i = 0; i < item_cnt; ++i) { - sdefl_put(dst, s, lens[perm[i]], 3); - } - for (i = 0; i < symcnt.items; ++i) { - unsigned sym = items[i] & 0x1F; - sdefl_put(dst, s, (int)codes[sym], lens[sym]); - if (sym < 16) continue; - if (sym == 16) sdefl_put(dst, s, items[i] >> 5, 2); - else if(sym == 17) sdefl_put(dst, s, items[i] >> 5, 3); - else sdefl_put(dst, s, items[i] >> 5, 7); - } - /* block sequences */ - for (i = 0; i < s->seq_cnt; ++i) { - if (s->seq[i].off >= 0) { - for (j = 0; j < s->seq[i].len; ++j) { - int c = in[s->seq[i].off + j]; - sdefl_put(dst, s, (int)s->cod.word.lit[c], s->cod.len.lit[c]); + /* write block */ + switch (sdefl_blk_type(s, blk_len, item_cnt, freqs, lens)) { + case SDEFL_BLK_UCOMPR: { + /* uncompressed blocks */ + int n = sdefl_div_round_up(blk_len, SDEFL_RAW_BLK_SIZE); + for (i = 0; i < n; ++i) { + int fin = is_last && (i + 1 == n); + int amount = blk_len < SDEFL_RAW_BLK_SIZE ? blk_len : SDEFL_RAW_BLK_SIZE; + sdefl_put(dst, s, !!fin, 1); /* block */ + sdefl_put(dst, s, 0x00, 2); /* stored block */ + if (s->bitcnt) { + sdefl_put(dst, s, 0x00, 8 - s->bitcnt); } - } else { - sdefl_match(dst, s, -s->seq[i].off, s->seq[i].len); + assert(s->bitcnt == 0); + sdefl_put16(dst, (unsigned short)amount); + sdefl_put16(dst, ~(unsigned short)amount); + memcpy(*dst, in + blk_begin + i * SDEFL_RAW_BLK_SIZE, amount); + *dst = *dst + amount; + blk_len -= amount; } - } - sdefl_put(dst, s, (int)(s)->cod.word.lit[SDEFL_EOB], (s)->cod.len.lit[SDEFL_EOB]); + } break; + case SDEFL_BLK_DYN: { + /* dynamic huffman block */ + sdefl_put(dst, s, !!is_last, 1); /* block */ + sdefl_put(dst, s, 0x02, 2); /* dynamic huffman */ + sdefl_put(dst, s, symcnt.lit - 257, 5); + sdefl_put(dst, s, symcnt.off - 1, 5); + sdefl_put(dst, s, item_cnt - 4, 4); + for (i = 0; i < item_cnt; ++i) { + sdefl_put(dst, s, lens[perm[i]], 3); + } + for (i = 0; i < symcnt.items; ++i) { + unsigned sym = items[i] & 0x1F; + sdefl_put(dst, s, (int)codes[sym], lens[sym]); + if (sym < 16) continue; + if (sym == 16) sdefl_put(dst, s, items[i] >> 5, 2); + else if(sym == 17) sdefl_put(dst, s, items[i] >> 5, 3); + else sdefl_put(dst, s, items[i] >> 5, 7); + } + /* block sequences */ + for (i = 0; i < s->seq_cnt; ++i) { + if (s->seq[i].off >= 0) { + for (j = 0; j < s->seq[i].len; ++j) { + int c = in[s->seq[i].off + j]; + sdefl_put(dst, s, (int)s->cod.word.lit[c], s->cod.len.lit[c]); + } + } else { + sdefl_match(dst, s, -s->seq[i].off, s->seq[i].len); + } + } + sdefl_put(dst, s, (int)(s)->cod.word.lit[SDEFL_EOB], (s)->cod.len.lit[SDEFL_EOB]); + } break;} memset(&s->freq, 0, sizeof(s->freq)); s->seq_cnt = 0; } @@ -550,8 +614,12 @@ sdefl_seq(struct sdefl *s, int off, int len) { } static void sdefl_reg_match(struct sdefl *s, int off, int len) { - struct sdefl_match_codes cod; + struct sdefl_match_codest cod; sdefl_match_codes(&cod, off, len); + + assert(cod.lc < SDEFL_SYM_MAX); + assert(cod.dc < SDEFL_OFF_MAX); + s->freq.lit[cod.lc]++; s->freq.off[cod.dc]++; } @@ -560,22 +628,35 @@ struct sdefl_match { int len; }; static void -sdefl_fnd(struct sdefl_match *m, const struct sdefl *s, - int chain_len, int max_match, const unsigned char *in, int p) { - int i = s->tbl[sdefl_hash32(&in[p])]; - int limit = ((p-SDEFL_WIN_SIZ)tbl[sdefl_hash32(in + p)]; + int limit = ((p - SDEFL_WIN_SIZ) < SDEFL_NIL) ? SDEFL_NIL : (p-SDEFL_WIN_SIZ); + + assert(p < e); + assert(p + max_match <= e); while (i > limit) { - if (in[i+m->len] == in[p+m->len] && - (sdefl_uload32(&in[i]) == sdefl_uload32(&in[p]))){ + assert(i + m->len < e); + assert(p + m->len < e); + assert(i + SDEFL_MIN_MATCH < e); + assert(p + SDEFL_MIN_MATCH < e); + + if (in[i + m->len] == in[p + m->len] && + (sdefl_uload32(&in[i]) == sdefl_uload32(&in[p]))) { int n = SDEFL_MIN_MATCH; - while (n < max_match && in[i+n] == in[p+n]) n++; + while (n < max_match && in[i + n] == in[p + n]) { + assert(i + n < e); + assert(p + n < e); + n++; + } if (n > m->len) { m->len = n, m->off = p - i; - if (n == max_match) break; + if (n == max_match) + break; } } if (!(--chain_len)) break; - i = s->prv[i&SDEFL_WIN_MSK]; + i = s->prv[i & SDEFL_WIN_MSK]; } } static int @@ -588,19 +669,20 @@ sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in, for (n = 0; n < SDEFL_HASH_SIZ; ++n) { s->tbl[n] = SDEFL_NIL; } - do {int blk_end = ((i + SDEFL_BLK_MAX) < in_len) ? (i + SDEFL_BLK_MAX) : in_len; + do {int blk_begin = i; + int blk_end = ((i + SDEFL_BLK_MAX) < in_len) ? (i + SDEFL_BLK_MAX) : in_len; while (i < blk_end) { struct sdefl_match m = {0}; int left = blk_end - i; - int max_match = (left >= SDEFL_MAX_MATCH) ? SDEFL_MAX_MATCH : left; + int max_match = (left > SDEFL_MAX_MATCH) ? SDEFL_MAX_MATCH : left; int nice_match = pref[lvl] < max_match ? pref[lvl] : max_match; int run = 1, inc = 1, run_inc = 0; if (max_match > SDEFL_MIN_MATCH) { - sdefl_fnd(&m, s, max_chain, max_match, in, i); + sdefl_fnd(&m, s, max_chain, max_match, in, i, in_len); } - if (lvl >= 5 && m.len >= SDEFL_MIN_MATCH && m.len < nice_match){ + if (lvl >= 5 && m.len >= SDEFL_MIN_MATCH && m.len + 1 < nice_match){ struct sdefl_match m2 = {0}; - sdefl_fnd(&m2, s, max_chain, m.len+1, in, i+1); + sdefl_fnd(&m2, s, max_chain, m.len + 1, in, i + 1, in_len); m.len = (m2.len > m.len) ? 0 : m.len; } if (m.len >= SDEFL_MIN_MATCH) { @@ -636,12 +718,12 @@ sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in, sdefl_seq(s, i - litlen, litlen); litlen = 0; } - sdefl_flush(&q, s, blk_end == in_len, in); + sdefl_flush(&q, s, blk_end == in_len, in, blk_begin, blk_end); } while (i < in_len); - - if (s->bitcnt > 0) + if (s->bitcnt) { sdefl_put(&q, s, 0x00, 8 - s->bitcnt); - + } + assert(s->bitcnt == 0); return (int)(q - out); } extern int @@ -701,9 +783,8 @@ zsdeflate(struct sdefl *s, void *out, const void *in, int n, int lvl) { } extern int sdefl_bound(int len) { - int a = 128 + (len * 110) / 100; - int b = 128 + len + ((len / (31 * 1024)) + 1) * 5; - return (a > b) ? a : b; + int max_blocks = 1 + sdefl_div_round_up(len, SDEFL_RAW_BLK_SIZE); + int bound = 5 * max_blocks + len + 1 + 4 + 8; + return bound; } #endif /* SDEFL_IMPLEMENTATION */ - diff --git a/src/external/sinfl.h b/src/external/sinfl.h index 915da9d23..8979fcd7f 100644 --- a/src/external/sinfl.h +++ b/src/external/sinfl.h @@ -72,7 +72,7 @@ Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License -Copyright (c) 2020 Micha Mettke +Copyright (c) 2020-2023 Micha Mettke Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to @@ -400,17 +400,21 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) } break; case stored: { /* uncompressed block */ - int len, nlen; - sinfl_refill(&s); + unsigned len, nlen; sinfl__get(&s,s.bitcnt & 7); - len = sinfl__get(&s,16); - nlen = sinfl__get(&s,16); - in -= 2; s.bitcnt = 0; + len = (unsigned short)sinfl__get(&s,16); + nlen = (unsigned short)sinfl__get(&s,16); + s.bitptr -= s.bitcnt / 8; + s.bitbuf = s.bitcnt = 0; - if (len > (e-in) || !len) + if ((unsigned short)len != (unsigned short)~nlen) return (int)(out-o); - memcpy(out, in, (size_t)len); - in += len, out += len; + if (len > (e - s.bitptr) || !len) + return (int)(out-o); + + memcpy(out, s.bitptr, (size_t)len); + s.bitptr += len, out += len; + if (last) return (int)(out-o); state = hdr; } break; case fixed: { @@ -443,8 +447,9 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) /* decode code lengths */ for (n = 0; n < nlit + ndist;) { + int sym = 0; sinfl_refill(&s); - int sym = sinfl_decode(&s, hlens, 7); + sym = sinfl_decode(&s, hlens, 7); switch (sym) {default: lens[n++] = (unsigned char)sym; break; case 16: for (i=3+sinfl_get(&s,2);i;i--,n++) lens[n]=lens[n-1]; break; case 17: for (i=3+sinfl_get(&s,3);i;i--,n++) lens[n]=0; break; @@ -458,8 +463,9 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) case blk: { /* decompress block */ while (1) { + int sym; sinfl_refill(&s); - int sym = sinfl_decode(&s, s.lits, 10); + sym = sinfl_decode(&s, s.lits, 10); if (sym < 256) { /* literal */ if (sinfl_unlikely(out >= oe)) { From df90da0b3750b654c1ec660f83e461aae03a3b6b Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 2 Jul 2023 10:50:40 +0200 Subject: [PATCH 0126/1350] Update rcore.c --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index d02ea2b9b..bba8f5efe 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3219,7 +3219,7 @@ const char *GetApplicationDirectory(void) #if defined(_WIN32) int len = 0; -#if defined (UNICODE) +#if defined(UNICODE) unsigned short widePath[MAX_PATH]; len = GetModuleFileNameW(NULL, widePath, MAX_PATH); len = WideCharToMultiByte(0, 0, widePath, len, appDir, MAX_PATH, NULL, NULL); From 48e2663d03a74b26d86e06057d659ddc9b1b7693 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 2 Jul 2023 10:51:39 +0200 Subject: [PATCH 0127/1350] REVIEWED: Issue #3105 --- src/rtext.c | 2 +- src/rtextures.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index dce838c29..61f2e04cb 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1227,7 +1227,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing if (tempTextWidth < textWidth) tempTextWidth = textWidth; - textSize.x = tempTextWidth*scaleFactor + (float)((tempByteCounter - 1)*spacing); // Adds chars spacing to measure + textSize.x = (tempTextWidth + (float)((tempByteCounter - 1)*spacing))*scaleFactor; textSize.y = textHeight*scaleFactor; return textSize; diff --git a/src/rtextures.c b/src/rtextures.c index 2d422b179..bc53e1cec 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3290,8 +3290,7 @@ void ImageDrawText(Image *dst, const char *text, int posX, int posY, int fontSiz if (GetFontDefault().texture.id == 0) LoadFontDefault(); Vector2 position = { (float)posX, (float)posY }; - // NOTE: For default font, spacing is set to desired font size / default font size (10) - ImageDrawTextEx(dst, GetFontDefault(), text, position, (float)fontSize, (float)fontSize/10, color); // WARNING: Module required: rtext + ImageDrawTextEx(dst, GetFontDefault(), text, position, (float)fontSize, 1.0f, color); // WARNING: Module required: rtext #else TRACELOG(LOG_WARNING, "IMAGE: ImageDrawText() requires module: rtext"); #endif From 3e4e4b32fdcaa2cff8854e11ff2179af4ff2afe9 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 2 Jul 2023 10:52:20 +0200 Subject: [PATCH 0128/1350] WARNING: BREAKING: ADDED: `SetTextLineSpacing()` --- src/raylib.h | 1 + src/rtext.c | 32 +++++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index cceaa2101..539a31355 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1381,6 +1381,7 @@ RLAPI void DrawTextCodepoint(Font font, int codepoint, Vector2 position, float f RLAPI void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 position, float fontSize, float spacing, Color tint); // Draw multiple character (codepoint) // Text font info functions +RLAPI void SetTextLineSpacing(int spacing); // Set vertical line spacing when drawing with line-breaks RLAPI int MeasureText(const char *text, int fontSize); // Measure string width for default font RLAPI Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing); // Measure string size for Font RLAPI int GetGlyphIndex(Font font, int codepoint); // Get glyph index position in font for a codepoint (unicode character), fallback to '?' if not found diff --git a/src/rtext.c b/src/rtext.c index 61f2e04cb..cb609cf62 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -111,7 +111,8 @@ static Font defaultFont = { 0 }; // Module specific Functions Declaration //---------------------------------------------------------------------------------- #if defined(SUPPORT_FILEFORMAT_FNT) -static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file) +static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file) +static int textLineSpacing = 15; // Text vertical line spacing in pixels #endif #if defined(SUPPORT_DEFAULT_FONT) @@ -123,7 +124,6 @@ extern void UnloadFontDefault(void); // Module Functions Definition //---------------------------------------------------------------------------------- #if defined(SUPPORT_DEFAULT_FONT) - // Load raylib default font extern void LoadFontDefault(void) { @@ -702,6 +702,8 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC totalWidth += chars[i].image.width + 2*padding; } +//#define SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE +#if defined(SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE) int rowCount = 0; int imageSize = 64; // Define minimum starting value to avoid unnecessary calculation steps for very small images @@ -711,6 +713,12 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC imageSize *= 2; // Double the size of image (to keep POT) rowCount = imageSize/(fontSize + 2*padding); // Calculate new row count for the new image size } +#else + // No need for a so-conservative atlas generation + float totalArea = totalWidth*fontSize*1.3f; + float imageMinSize = sqrtf(totalArea); + int imageSize = (int)powf(2, ceilf(logf(imageMinSize)/logf(2))); +#endif atlas.width = imageSize; // Atlas bitmap width atlas.height = imageSize; // Atlas bitmap height @@ -1071,9 +1079,8 @@ void DrawTextEx(Font font, const char *text, Vector2 position, float fontSize, f if (codepoint == '\n') { - // NOTE: Fixed line spacing of 1.5 line-height - // TODO: Support custom line spacing defined by user - textOffsetY += (int)((font.baseSize + font.baseSize/2.0f)*scaleFactor); + // NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup + textOffsetY += textLineSpacing; textOffsetX = 0.0f; } else @@ -1143,9 +1150,8 @@ void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 pos if (codepoints[i] == '\n') { - // NOTE: Fixed line spacing of 1.5 line-height - // TODO: Support custom line spacing defined by user - textOffsetY += (int)((font.baseSize + font.baseSize/2.0f)*scaleFactor); + // NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup + textOffsetY += textLineSpacing; textOffsetX = 0.0f; } else @@ -1161,6 +1167,12 @@ void DrawTextCodepoints(Font font, const int *codepoints, int count, Vector2 pos } } +// Set vertical line spacing when drawing with line-breaks +void SetTextLineSpacing(int spacing) +{ + textLineSpacing = spacing; +} + // Measure string width for default font int MeasureText(const char *text, int fontSize) { @@ -1219,7 +1231,9 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing if (tempTextWidth < textWidth) tempTextWidth = textWidth; byteCounter = 0; textWidth = 0; - textHeight += ((float)font.baseSize*1.5f); // NOTE: Fixed line spacing of 1.5 lines + + // NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup + textHeight += (float)textLineSpacing; } if (tempByteCounter < byteCounter) tempByteCounter = byteCounter; From 4fc5e82e3087b5a9df21db86f6dc4588e5d629b4 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 2 Jul 2023 11:10:42 +0200 Subject: [PATCH 0129/1350] REVIEWED: `TextToUpper()`, `TextToLower()`, `TextToPascal()` --- src/rtext.c | 48 ++++++++++++++++++------------------------------ 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index cb609cf62..0cdac6d10 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1588,7 +1588,8 @@ int TextFindIndex(const char *text, const char *find) } // Get upper case version of provided string -// REQUIRES: toupper() +// WARNING: Limited functionality, only basic characters set +// TODO: Support UTF-8 diacritics to upper-case, check codepoints const char *TextToUpper(const char *text) { static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; @@ -1596,17 +1597,10 @@ const char *TextToUpper(const char *text) if (text != NULL) { - for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++) + for (int i = 0; (i < MAX_TEXT_BUFFER_LENGTH - 1) && (text[i] != '\0'); i++) { - if (text[i] != '\0') - { - buffer[i] = (char)toupper(text[i]); - //if ((text[i] >= 'a') && (text[i] <= 'z')) buffer[i] = text[i] - 32; - - // TODO: Support UTF-8 diacritics to upper-case - //if ((text[i] >= 'à') && (text[i] <= 'ý')) buffer[i] = text[i] - 32; - } - else { buffer[i] = '\0'; break; } + if ((text[i] >= 'a') && (text[i] <= 'z')) buffer[i] = text[i] - 32; + else buffer[i] = text[i]; } } @@ -1614,7 +1608,7 @@ const char *TextToUpper(const char *text) } // Get lower case version of provided string -// REQUIRES: tolower() +// WARNING: Limited functionality, only basic characters set const char *TextToLower(const char *text) { static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; @@ -1622,14 +1616,10 @@ const char *TextToLower(const char *text) if (text != NULL) { - for (int i = 0; i < MAX_TEXT_BUFFER_LENGTH; i++) + for (int i = 0; (i < MAX_TEXT_BUFFER_LENGTH - 1) && (text[i] != '\0'); i++) { - if (text[i] != '\0') - { - buffer[i] = (char)tolower(text[i]); - //if ((text[i] >= 'A') && (text[i] <= 'Z')) buffer[i] = text[i] + 32; - } - else { buffer[i] = '\0'; break; } + if ((text[i] >= 'A') && (text[i] <= 'Z')) buffer[i] = text[i] + 32; + else buffer[i] = text[i]; } } @@ -1637,7 +1627,7 @@ const char *TextToLower(const char *text) } // Get Pascal case notation version of provided string -// REQUIRES: toupper() +// WARNING: Limited functionality, only basic characters set const char *TextToPascal(const char *text) { static char buffer[MAX_TEXT_BUFFER_LENGTH] = { 0 }; @@ -1645,20 +1635,18 @@ const char *TextToPascal(const char *text) if (text != NULL) { - buffer[0] = (char)toupper(text[0]); + // Upper case first character + if ((text[0] >= 'a') && (text[0] <= 'z')) buffer[0] = text[0] - 32; - for (int i = 1, j = 1; i < MAX_TEXT_BUFFER_LENGTH; i++, j++) + // Check for next separator to upper case another character + for (int i = 1, j = 1; (i < MAX_TEXT_BUFFER_LENGTH - 1) && (text[j] != '\0'); i++, j++) { - if (text[j] != '\0') + if (text[j] != '_') buffer[i] = text[j]; + else { - if (text[j] != '_') buffer[i] = text[j]; - else - { - j++; - buffer[i] = (char)toupper(text[j]); - } + j++; + if ((text[j] >= 'a') && (text[j] <= 'z')) buffer[i] = text[j] - 32; } - else { buffer[i] = '\0'; break; } } } From 5361d498c33deeb1e1ad44aed24a53a4f9e97618 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 2 Jul 2023 17:27:38 +0200 Subject: [PATCH 0130/1350] WARNING: REDESIGN: `Vector2Angle()`<-->`Vector2LineAngle()` #2887 --- examples/others/raymath_vector_angle.c | 106 +++++++++++++++++++++++ examples/others/raymath_vector_angle.png | Bin 0 -> 16339 bytes src/raymath.h | 27 +++--- 3 files changed, 120 insertions(+), 13 deletions(-) create mode 100644 examples/others/raymath_vector_angle.c create mode 100644 examples/others/raymath_vector_angle.png diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c new file mode 100644 index 000000000..86cd91a93 --- /dev/null +++ b/examples/others/raymath_vector_angle.c @@ -0,0 +1,106 @@ +/******************************************************************************************* +* +* raylib [shapes] example - Vector Angle +* +* Example originally created with raylib 1.0, last time updated with raylib 4.2 +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2023 Ramon Santamaria (@raysan5) +* +********************************************************************************************/ + +#include "raylib.h" + +#include "raymath.h" + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [math] example - vector angle"); + + Vector2 v0 = { screenWidth/2, screenHeight/2 }; + Vector2 v1 = { 100.0f, 80.0f }; + Vector2 v2 = { 0 }; // Updated with mouse position + + float angle = 0.0f; // Angle in degrees + int angleMode = 0; // 0-Vector2Angle(), 1-Vector2LineAngle() + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KEY_SPACE)) angleMode = !angleMode; + + if (angleMode == 0) + { + // Calculate angle between two vectors, considering a common origin (v0) + v1 = Vector2Add(v0, (Vector2){ 100.0f, 80.0f }); + v2 = GetMousePosition(); + angle = Vector2Angle(Vector2Normalize(Vector2Subtract(v1, v0)), Vector2Normalize(Vector2Subtract(v2, v0)))*RAD2DEG; + } + else if (angleMode == 1) + { + // Calculate angle defined by a two vectors line, in reference to horizontal line + v1 = (Vector2){ screenWidth/2, screenHeight/2 }; + v2 = GetMousePosition(); + angle = Vector2LineAngle(v1, v2)*RAD2DEG; + } + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + if (angleMode == 0) DrawText("v0", v0.x, v0.y, 10, DARKGRAY); + DrawText("v1", v1.x, v1.y, 10, DARKGRAY); + DrawText("v2", v2.x, v2.y, 10, DARKGRAY); + + if (angleMode == 0) + { + DrawText("MODE: Angle between V1 and V2", 10, 10, 20, BLACK); + + DrawLineEx(v0, v1, 2.0f, BLACK); + DrawLineEx(v0, v2, 2.0f, RED); + + // TODO: Properly draw circle sector + DrawCircleSector(v0, 40.0f, Vector2LineAngle(v0, v1)*RAD2DEG, angle, 32, Fade(GREEN, 0.6f)); + } + else if (angleMode == 1) + { + DrawText("MODE: Angle formed by line V1 to V2", 10, 10, 20, BLACK); + + DrawLine(0, screenHeight/2, screenWidth, screenHeight/2, LIGHTGRAY); + DrawLineEx(v1, v2, 2.0f, RED); + + DrawCircleSector(v1, 40.0f, 90.0f, 180 - angle - 90, 32, Fade(GREEN, 0.6f)); + } + + DrawText("Press SPACE to change MODE", 460, 10, 20, DARKGRAY); + DrawText(TextFormat("ANGLE: %2.2f", angle), 10, 40, 20, LIME); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/others/raymath_vector_angle.png b/examples/others/raymath_vector_angle.png new file mode 100644 index 0000000000000000000000000000000000000000..29995e002f283e5bd9a1960085dc25da31d5e9eb GIT binary patch literal 16339 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 N8#8hAQNQ5m{{X8kT3rAD literal 0 HcmV?d00001 diff --git a/src/raymath.h b/src/raymath.h index 47728ff69..6a929cf6e 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -314,8 +314,19 @@ RMAPI float Vector2DistanceSqr(Vector2 v1, Vector2 v2) // NOTE: Angle is calculated from origin point (0, 0) RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) { - float result = atan2f(v2.y - v1.y, v2.x - v1.x); + float result = 0.0f; + + float dot = v1.x*v2.x + v1.y*v2.y; // Dot product + float dotClamp = (dot < -1.0f)? -1.0f : dot; // Clamp + if (dotClamp > 1.0f) dotClamp = 1.0f; + result = acosf(dotClamp); + + // Alternative implementation, more costly + //float v1Length = sqrtf((v1.x*v1.x) + (v1.y*v1.y)); + //float v2Length = sqrtf((v2.x*v2.x) + (v2.y*v2.y)); + //float result = -acosf((v1.x*v2.x + v1.y*v2.y)/(v1Length*v2Length)); + return result; } @@ -325,18 +336,8 @@ RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) RMAPI float Vector2LineAngle(Vector2 start, Vector2 end) { float result = 0.0f; - - float dot = start.x*end.x + start.y*end.y; // Dot product - - float dotClamp = (dot < -1.0f)? -1.0f : dot; // Clamp - if (dotClamp > 1.0f) dotClamp = 1.0f; - - result = acosf(dotClamp); - - // Alternative implementation, more costly - //float v1Length = sqrtf((start.x*start.x) + (start.y*start.y)); - //float v2Length = sqrtf((end.x*end.x) + (end.y*end.y)); - //float result = -acosf((start.x*end.x + start.y*end.y)/(v1Length*v2Length)); + + result = atan2f(end.y - start.y, end.x - start.x); return result; } From d1ab031a273f6d012c81f3ed637771b7f3c72ef4 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 2 Jul 2023 17:48:00 +0200 Subject: [PATCH 0131/1350] ADDED: `SetWindowFocused()` #3142 --- src/raylib.h | 1 + src/rcore.c | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/src/raylib.h b/src/raylib.h index 539a31355..f9f36626e 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -958,6 +958,7 @@ RLAPI void SetWindowMonitor(int monitor); // Set monitor RLAPI void SetWindowMinSize(int width, int height); // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE) RLAPI void SetWindowSize(int width, int height); // Set window dimensions RLAPI void SetWindowOpacity(float opacity); // Set window opacity [0.0f..1.0f] (only PLATFORM_DESKTOP) +RLAPI void SetWindowFocused(void); // Set window focused (only PLATFORM_DESKTOP) RLAPI void *GetWindowHandle(void); // Get native window handle RLAPI int GetScreenWidth(void); // Get current screen width RLAPI int GetScreenHeight(void); // Get current screen height diff --git a/src/rcore.c b/src/rcore.c index bba8f5efe..135ccb6cc 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1705,6 +1705,14 @@ void SetWindowOpacity(float opacity) #endif } +// Set window focused +void SetWindowFocused(void) +{ +#if defined(PLATFORM_DESKTOP) + glfwFocusWindow(CORE.Window.handle); +#endif +} + // Get current screen width int GetScreenWidth(void) { From 64bb2fe3ec4994037467e44525c85abadce892e5 Mon Sep 17 00:00:00 2001 From: Gisteron <56622101+Gisteron@users.noreply.github.com> Date: Sun, 2 Jul 2023 18:51:27 +0200 Subject: [PATCH 0132/1350] fix vector angle example mode 0 circle segment drawing (#3150) --- examples/Makefile | 11 ++++++++++- examples/others/raymath_vector_angle.c | 7 +++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/examples/Makefile b/examples/Makefile index c2ea35bf4..15edf1b36 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -536,12 +536,20 @@ AUDIO = \ audio/audio_stream_effects \ audio/audio_mixed_processor +OTHERS = \ + others/easings_testbed \ + others/embedded_files_loading \ + others/raylib_opengl_interop \ + others/raymath_vector_angle \ + others/rlgl_compute_shader \ + others/rlgl_standalone + CURRENT_MAKEFILE = $(lastword $(MAKEFILE_LIST)) # Define processes to execute #------------------------------------------------------------------------------------------------ # Default target entry -all: $(CORE) $(SHAPES) $(TEXT) $(TEXTURES) $(MODELS) $(SHADERS) $(AUDIO) +all: $(CORE) $(SHAPES) $(TEXT) $(TEXTURES) $(MODELS) $(SHADERS) $(AUDIO) $(OTHERS) core: $(CORE) shapes: $(SHAPES) @@ -550,6 +558,7 @@ text: $(TEXT) models: $(MODELS) shaders: $(SHADERS) audio: $(AUDIO) +others: $(OTHERS) # Generic compilation pattern # NOTE: Examples must be ready for Android compilation! diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c index 86cd91a93..56db373cd 100644 --- a/examples/others/raymath_vector_angle.c +++ b/examples/others/raymath_vector_angle.c @@ -49,14 +49,14 @@ int main(void) // Calculate angle between two vectors, considering a common origin (v0) v1 = Vector2Add(v0, (Vector2){ 100.0f, 80.0f }); v2 = GetMousePosition(); - angle = Vector2Angle(Vector2Normalize(Vector2Subtract(v1, v0)), Vector2Normalize(Vector2Subtract(v2, v0)))*RAD2DEG; + angle = 90 - Vector2LineAngle(v0, v2) * RAD2DEG; } else if (angleMode == 1) { // Calculate angle defined by a two vectors line, in reference to horizontal line v1 = (Vector2){ screenWidth/2, screenHeight/2 }; v2 = GetMousePosition(); - angle = Vector2LineAngle(v1, v2)*RAD2DEG; + angle = Vector2LineAngle(v1, v2) * RAD2DEG; } //---------------------------------------------------------------------------------- @@ -77,8 +77,7 @@ int main(void) DrawLineEx(v0, v1, 2.0f, BLACK); DrawLineEx(v0, v2, 2.0f, RED); - // TODO: Properly draw circle sector - DrawCircleSector(v0, 40.0f, Vector2LineAngle(v0, v1)*RAD2DEG, angle, 32, Fade(GREEN, 0.6f)); + DrawCircleSector(v0, 40.0f, 90 - Vector2LineAngle(v0, v1) * RAD2DEG, angle, 32, Fade(GREEN, 0.6f)); } else if (angleMode == 1) { From e8af87575639d27fa5486ffe88458596d067c21a Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 2 Jul 2023 18:54:50 +0200 Subject: [PATCH 0133/1350] Minor format tweak, another issue introduced... --- examples/others/raymath_vector_angle.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c index 56db373cd..457d69dc3 100644 --- a/examples/others/raymath_vector_angle.c +++ b/examples/others/raymath_vector_angle.c @@ -49,14 +49,14 @@ int main(void) // Calculate angle between two vectors, considering a common origin (v0) v1 = Vector2Add(v0, (Vector2){ 100.0f, 80.0f }); v2 = GetMousePosition(); - angle = 90 - Vector2LineAngle(v0, v2) * RAD2DEG; + angle = 90 - Vector2LineAngle(v0, v2)*RAD2DEG; } else if (angleMode == 1) { // Calculate angle defined by a two vectors line, in reference to horizontal line v1 = (Vector2){ screenWidth/2, screenHeight/2 }; v2 = GetMousePosition(); - angle = Vector2LineAngle(v1, v2) * RAD2DEG; + angle = Vector2LineAngle(v1, v2)*RAD2DEG; } //---------------------------------------------------------------------------------- @@ -77,7 +77,7 @@ int main(void) DrawLineEx(v0, v1, 2.0f, BLACK); DrawLineEx(v0, v2, 2.0f, RED); - DrawCircleSector(v0, 40.0f, 90 - Vector2LineAngle(v0, v1) * RAD2DEG, angle, 32, Fade(GREEN, 0.6f)); + DrawCircleSector(v0, 40.0f, 90 - Vector2LineAngle(v0, v1)*RAD2DEG, angle, 32, Fade(GREEN, 0.6f)); } else if (angleMode == 1) { From fdc28fce80d6e9ae04e900079beba7d79673b25d Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 2 Jul 2023 20:05:15 +0200 Subject: [PATCH 0134/1350] Reviewed vector2angle example --- examples/others/raymath_vector_angle.c | 7 +++++-- src/raymath.h | 5 ++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c index 457d69dc3..5de6116f2 100644 --- a/examples/others/raymath_vector_angle.c +++ b/examples/others/raymath_vector_angle.c @@ -49,7 +49,7 @@ int main(void) // Calculate angle between two vectors, considering a common origin (v0) v1 = Vector2Add(v0, (Vector2){ 100.0f, 80.0f }); v2 = GetMousePosition(); - angle = 90 - Vector2LineAngle(v0, v2)*RAD2DEG; + angle = Vector2Angle(Vector2Normalize(Vector2Subtract(v1, v0)), Vector2Normalize(Vector2Subtract(v2, v0)))*RAD2DEG; } else if (angleMode == 1) { @@ -77,7 +77,10 @@ int main(void) DrawLineEx(v0, v1, 2.0f, BLACK); DrawLineEx(v0, v2, 2.0f, RED); - DrawCircleSector(v0, 40.0f, 90 - Vector2LineAngle(v0, v1)*RAD2DEG, angle, 32, Fade(GREEN, 0.6f)); + float startangle = 90 - Vector2LineAngle(v0, v1)*RAD2DEG; + DrawCircleSector(v0, 40.0f, startangle, angle + startangle, 32, Fade(GREEN, 0.6f)); + + //DrawCircleSector(v0, 40.0f, 90 - Vector2LineAngle(v0, v1)*RAD2DEG, angle, 32, Fade(GREEN, 0.6f)); } else if (angleMode == 1) { diff --git a/src/raymath.h b/src/raymath.h index 6a929cf6e..087410ef3 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -316,16 +316,15 @@ RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) { float result = 0.0f; - float dot = v1.x*v2.x + v1.y*v2.y; // Dot product + float dot = v1.x*v2.x + v1.y*v2.y; // Dot product float dotClamp = (dot < -1.0f)? -1.0f : dot; // Clamp if (dotClamp > 1.0f) dotClamp = 1.0f; - result = acosf(dotClamp); // Alternative implementation, more costly //float v1Length = sqrtf((v1.x*v1.x) + (v1.y*v1.y)); //float v2Length = sqrtf((v2.x*v2.x) + (v2.y*v2.y)); - //float result = -acosf((v1.x*v2.x + v1.y*v2.y)/(v1Length*v2Length)); + //result = -acosf((v1.x*v2.x + v1.y*v2.y)/(v1Length*v2Length)); return result; } From ffe4d36e0af86ee2d72abe7607b0525e1ffcf61f Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Mon, 3 Jul 2023 04:29:26 -0300 Subject: [PATCH 0135/1350] Fix swipe gestures for web (#3151) --- src/rcore.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/rcore.c b/src/rcore.c index 135ccb6cc..6ef25c0bf 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -6131,6 +6131,10 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent { gestureEvent.pointId[i] = CORE.Input.Touch.pointId[i]; gestureEvent.position[i] = CORE.Input.Touch.position[i]; + + // Normalize gestureEvent.position[i] + gestureEvent.position[i].x /= (float)GetScreenWidth(); + gestureEvent.position[i].y /= (float)GetScreenHeight(); } // Gesture data is sent to gestures system for processing From dd2d64e05804e749d470a9d252cea48b8b1d7e17 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Tue, 4 Jul 2023 06:08:58 -0300 Subject: [PATCH 0136/1350] Fix pinch gestures for web (#3153) --- src/rgestures.h | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/rgestures.h b/src/rgestures.h index 0be2fbb5c..1461a1114 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -203,6 +203,8 @@ typedef struct { Vector2 downDragPosition; // Touch drag position Vector2 moveDownPositionA; // First touch down position on move Vector2 moveDownPositionB; // Second touch down position on move + Vector2 previousPositionA; // Previous position A to compare for pinch gestures + Vector2 previousPositionB; // Previous position B to compare for pinch gestures int tapCounter; // TAP counter (one tap implies TOUCH_ACTION_DOWN and TOUCH_ACTION_UP actions) } Touch; struct { @@ -364,6 +366,9 @@ void ProcessGestureEvent(GestureEvent event) GESTURES.Touch.downPositionA = event.position[0]; GESTURES.Touch.downPositionB = event.position[1]; + GESTURES.Touch.previousPositionA = GESTURES.Touch.downPositionA; + GESTURES.Touch.previousPositionB = GESTURES.Touch.downPositionB; + //GESTURES.Pinch.distance = rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.downPositionB); GESTURES.Pinch.vector.x = GESTURES.Touch.downPositionB.x - GESTURES.Touch.downPositionA.x; @@ -376,18 +381,15 @@ void ProcessGestureEvent(GestureEvent event) { GESTURES.Pinch.distance = rgVector2Distance(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB); - GESTURES.Touch.downPositionA = GESTURES.Touch.moveDownPositionA; - GESTURES.Touch.downPositionB = GESTURES.Touch.moveDownPositionB; - GESTURES.Touch.moveDownPositionA = event.position[0]; GESTURES.Touch.moveDownPositionB = event.position[1]; GESTURES.Pinch.vector.x = GESTURES.Touch.moveDownPositionB.x - GESTURES.Touch.moveDownPositionA.x; GESTURES.Pinch.vector.y = GESTURES.Touch.moveDownPositionB.y - GESTURES.Touch.moveDownPositionA.y; - if ((rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.moveDownPositionA) >= MINIMUM_PINCH) || (rgVector2Distance(GESTURES.Touch.downPositionB, GESTURES.Touch.moveDownPositionB) >= MINIMUM_PINCH)) + if ((rgVector2Distance(GESTURES.Touch.previousPositionA, GESTURES.Touch.moveDownPositionA) >= MINIMUM_PINCH) || (rgVector2Distance(GESTURES.Touch.previousPositionB, GESTURES.Touch.moveDownPositionB) >= MINIMUM_PINCH)) { - if ((rgVector2Distance(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB) - GESTURES.Pinch.distance) < 0) GESTURES.current = GESTURE_PINCH_IN; + if ( rgVector2Distance(GESTURES.Touch.previousPositionA, GESTURES.Touch.previousPositionB) > rgVector2Distance(GESTURES.Touch.moveDownPositionA, GESTURES.Touch.moveDownPositionB) ) GESTURES.current = GESTURE_PINCH_IN; else GESTURES.current = GESTURE_PINCH_OUT; } else From 225b4fb3e2503d97a53d3aa746ddac3648cfebd8 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 4 Jul 2023 16:58:43 +0200 Subject: [PATCH 0137/1350] REVIEWED: `Vector2Angle()` --- examples/others/raymath_vector_angle.c | 4 +--- src/raymath.h | 12 +++--------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/examples/others/raymath_vector_angle.c b/examples/others/raymath_vector_angle.c index 5de6116f2..b193648fe 100644 --- a/examples/others/raymath_vector_angle.c +++ b/examples/others/raymath_vector_angle.c @@ -78,9 +78,7 @@ int main(void) DrawLineEx(v0, v2, 2.0f, RED); float startangle = 90 - Vector2LineAngle(v0, v1)*RAD2DEG; - DrawCircleSector(v0, 40.0f, startangle, angle + startangle, 32, Fade(GREEN, 0.6f)); - - //DrawCircleSector(v0, 40.0f, 90 - Vector2LineAngle(v0, v1)*RAD2DEG, angle, 32, Fade(GREEN, 0.6f)); + DrawCircleSector(v0, 40.0f, startangle, startangle + angle - 360.0f*(angle > 180.0f), 32, Fade(GREEN, 0.6f)); } else if (angleMode == 1) { diff --git a/src/raymath.h b/src/raymath.h index 087410ef3..d7ec1d252 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -316,15 +316,9 @@ RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) { float result = 0.0f; - float dot = v1.x*v2.x + v1.y*v2.y; // Dot product - float dotClamp = (dot < -1.0f)? -1.0f : dot; // Clamp - if (dotClamp > 1.0f) dotClamp = 1.0f; - result = acosf(dotClamp); - - // Alternative implementation, more costly - //float v1Length = sqrtf((v1.x*v1.x) + (v1.y*v1.y)); - //float v2Length = sqrtf((v2.x*v2.x) + (v2.y*v2.y)); - //result = -acosf((v1.x*v2.x + v1.y*v2.y)/(v1Length*v2Length)); + float dot = v1.x*v2.x + v1.y*v2.y; + float det = v1.x*v2.y - v1.y*v2.x; + result = -atan2f(det, dot); return result; } From 3c4ce9c99b74475cfc651ddc4db482a4ee86d57f Mon Sep 17 00:00:00 2001 From: Wytek01 <101928745+Wytekol@users.noreply.github.com> Date: Wed, 5 Jul 2023 16:39:38 +0200 Subject: [PATCH 0138/1350] Update examples_template.c to raylib 4.5 (#3156) --- examples/examples_template.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/examples_template.c b/examples/examples_template.c index 3d8332e67..2b962d319 100644 --- a/examples/examples_template.c +++ b/examples/examples_template.c @@ -41,7 +41,7 @@ * * raylib [core] example - Basic window * -* Example originally created with raylib 4.2, last time updated with raylib 4.2 +* Example originally created with raylib 4.5, last time updated with raylib 4.5 * * Example contributed by (@) and reviewed by Ramon Santamaria (@raysan5) * From 13a26a0fa238b50621091ff686a6edf7014ab75f Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 5 Jul 2023 11:43:00 -0300 Subject: [PATCH 0139/1350] Fix degrees of swipe gestures for web (#3155) --- src/rgestures.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rgestures.h b/src/rgestures.h index 1461a1114..35cd7a602 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -311,10 +311,10 @@ void ProcessGestureEvent(GestureEvent event) // NOTE: Angle should be inverted in Y GESTURES.Drag.angle = 360.0f - rgVector2Angle(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition); - if ((GESTURES.Drag.angle < 30) || (GESTURES.Drag.angle > 330)) GESTURES.current = GESTURE_SWIPE_RIGHT; // Right - else if ((GESTURES.Drag.angle > 30) && (GESTURES.Drag.angle < 120)) GESTURES.current = GESTURE_SWIPE_UP; // Up - else if ((GESTURES.Drag.angle > 120) && (GESTURES.Drag.angle < 210)) GESTURES.current = GESTURE_SWIPE_LEFT; // Left - else if ((GESTURES.Drag.angle > 210) && (GESTURES.Drag.angle < 300)) GESTURES.current = GESTURE_SWIPE_DOWN; // Down + if ((GESTURES.Drag.angle < 30) || (GESTURES.Drag.angle > 330)) GESTURES.current = GESTURE_SWIPE_RIGHT; // Right + else if ((GESTURES.Drag.angle >= 30) && (GESTURES.Drag.angle <= 150)) GESTURES.current = GESTURE_SWIPE_UP; // Up + else if ((GESTURES.Drag.angle > 150) && (GESTURES.Drag.angle < 210)) GESTURES.current = GESTURE_SWIPE_LEFT; // Left + else if ((GESTURES.Drag.angle >= 210) && (GESTURES.Drag.angle <= 330)) GESTURES.current = GESTURE_SWIPE_DOWN; // Down else GESTURES.current = GESTURE_NONE; } else From 58bd10edb27e0e5dd8d835e2222239390c804d5e Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 5 Jul 2023 18:36:54 +0200 Subject: [PATCH 0140/1350] Update rtext.c --- src/rtext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtext.c b/src/rtext.c index 0cdac6d10..396275a8d 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -112,8 +112,8 @@ static Font defaultFont = { 0 }; //---------------------------------------------------------------------------------- #if defined(SUPPORT_FILEFORMAT_FNT) static Font LoadBMFont(const char *fileName); // Load a BMFont file (AngelCode font file) -static int textLineSpacing = 15; // Text vertical line spacing in pixels #endif +static int textLineSpacing = 15; // Text vertical line spacing in pixels #if defined(SUPPORT_DEFAULT_FONT) extern void LoadFontDefault(void); From 668b37e1113c653c38ce50757984b8fc074ea9b3 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 6 Jul 2023 12:29:28 +0200 Subject: [PATCH 0141/1350] REVIEWED: `GenImageFontAtlas()`, make atlas size less conservative --- src/rtext.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/rtext.c b/src/rtext.c index 396275a8d..eb3ad5514 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -699,7 +699,7 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC for (int i = 0; i < glyphCount; i++) { if (chars[i].image.width > maxGlyphWidth) maxGlyphWidth = chars[i].image.width; - totalWidth += chars[i].image.width + 2*padding; + totalWidth += chars[i].image.width + 4*padding; } //#define SUPPORT_FONT_ATLAS_SIZE_CONSERVATIVE @@ -713,15 +713,28 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC imageSize *= 2; // Double the size of image (to keep POT) rowCount = imageSize/(fontSize + 2*padding); // Calculate new row count for the new image size } -#else - // No need for a so-conservative atlas generation - float totalArea = totalWidth*fontSize*1.3f; - float imageMinSize = sqrtf(totalArea); - int imageSize = (int)powf(2, ceilf(logf(imageMinSize)/logf(2))); -#endif atlas.width = imageSize; // Atlas bitmap width atlas.height = imageSize; // Atlas bitmap height +#else + // No need for a so-conservative atlas generation + float totalArea = totalWidth*fontSize*1.2f; + float imageMinSize = sqrtf(totalArea); + int imageSize = (int)powf(2, ceilf(logf(imageMinSize)/logf(2))); + + if (totalArea < ((imageSize*imageSize)/2)) + { + atlas.width = imageSize; // Atlas bitmap width + atlas.height = imageSize/2; // Atlas bitmap height + } + else + { + atlas.width = imageSize; // Atlas bitmap width + atlas.height = imageSize; // Atlas bitmap height + } +#endif + + atlas.data = (unsigned char *)RL_CALLOC(1, atlas.width*atlas.height); // Create a bitmap to store characters (8 bpp) atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; atlas.mipmaps = 1; From 685d47938bf97ef5cb2ec094bfebd7de9bd9bef8 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 6 Jul 2023 12:31:01 +0200 Subject: [PATCH 0142/1350] Reverted `MeasureTextEx()` change #3105 --- src/rtext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtext.c b/src/rtext.c index eb3ad5514..e7bea9a00 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1254,7 +1254,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing if (tempTextWidth < textWidth) tempTextWidth = textWidth; - textSize.x = (tempTextWidth + (float)((tempByteCounter - 1)*spacing))*scaleFactor; + textSize.x = tempTextWidth*scaleFactor + (float)((tempByteCounter - 1)*spacing); textSize.y = textHeight*scaleFactor; return textSize; From 334e96d470c5cb09d154e1c849d04adb721a7a8c Mon Sep 17 00:00:00 2001 From: o3o Date: Thu, 6 Jul 2023 12:32:02 +0200 Subject: [PATCH 0143/1350] update bindbd-raylib3 to raylib 4.5 (#3157) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index f040599ca..bb73d73aa 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -17,7 +17,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | ray-cyber | 4.5 | [Cyber](https://cyberscript.dev) | MIT | https://github.com/fubark/ray-cyber | | raylib-c3 | **4.5** | [C3](https://c3-lang.org/) | Zlib | https://github.com/Its-Kenta/Raylib-C3 | | dart-raylib | 4.0 | [Dart](https://dart.dev/) | MIT | https://gitlab.com/wolfenrain/dart-raylib | -| bindbc-raylib3 | 4.0 | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | +| bindbc-raylib3 | **4.5** | [D](https://dlang.org/) | BSL-1.0 | https://github.com/o3o/bindbc-raylib3 | | dray | 4.2 | [D](https://dlang.org/) | Apache-2.0 | https://github.com/redthing1/dray | | raylib-d | **4.5** | [D](https://dlang.org/) | Zlib | https://github.com/schveiguy/raylib-d | | dlang_raylib | 4.0 | [D](https://dlang.org) | MPL-2.0 |https://github.com/rc-05/dlang_raylib | From 91e4eea52d1b92e1408fd129ca7b638f9cd4531d Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Mon, 10 Jul 2023 12:58:10 -0400 Subject: [PATCH 0144/1350] Add raylib-ffi to bindings list (#3164) --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index bb73d73aa..8c5c8396b 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -59,6 +59,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-php | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/joseph-montanez/raylib-php | | raylib-phpcpp | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/oraoto/raylib-phpcpp | | raylibr | 4.0 | [R](https://www.r-project.org) | MIT | https://github.com/jeroenjanssens/raylibr | +| raylib-ffi | 4.5 | [Rust](https://www.rust-lang.org/) | GPLv3 | https://github.com/ewpratten/raylib-ffi | | raylib-rs | 3.5 | [Rust](https://www.rust-lang.org/) | Zlib | https://github.com/deltaphc/raylib-rs | | Relib | 3.5 | [ReCT](https://github.com/RedCubeDev-ByteSpace/ReCT) | ? | https://github.com/RedCubeDev-ByteSpace/Relib | | racket-raylib | 4.0 | [Racket](https://racket-lang.org/) | MIT/Apache-2.0 | https://github.com/eutro/racket-raylib | From 4b6cbd234040312091e24085ee929a7cec60faac Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Mon, 10 Jul 2023 13:58:56 -0300 Subject: [PATCH 0145/1350] Fix Touch pointCount for web (#3163) --- src/rcore.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rcore.c b/src/rcore.c index 6ef25c0bf..833400fcf 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -6139,6 +6139,9 @@ static EM_BOOL EmscriptenTouchCallback(int eventType, const EmscriptenTouchEvent // Gesture data is sent to gestures system for processing ProcessGestureEvent(gestureEvent); + + // Reset the pointCount for web, if it was the last Touch End event + if (eventType == EMSCRIPTEN_EVENT_TOUCHEND && CORE.Input.Touch.pointCount == 1) CORE.Input.Touch.pointCount = 0; #endif return 1; // The event was consumed by the callback handler From 0b9fae3c539161e850db9092ac7294f889583e82 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 10 Jul 2023 19:13:44 +0200 Subject: [PATCH 0146/1350] Reviewed `rcamera`/`rgestures` file-macros for consistency #3161 --- src/rcamera.h | 20 ++++++++++---------- src/rcore.c | 4 ++-- src/rgestures.h | 22 +++++++++++----------- src/rtext.c | 2 +- 4 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/rcamera.h b/src/rcamera.h index 2a20d5814..4e99c3e8c 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -3,12 +3,12 @@ * rcamera - Basic camera system with support for multiple camera modes * * CONFIGURATION: -* #define CAMERA_IMPLEMENTATION +* #define RCAMERA_IMPLEMENTATION * Generates the implementation of the library into the included file. * If not defined, the library is in header only mode and can be included in other headers * or source files without problems. But only ONE file should hold the implementation. * -* #define CAMERA_STANDALONE +* #define RCAMERA_STANDALONE * If defined, the library can be used as standalone as a camera system but some * functions must be redefined to manage inputs accordingly. * @@ -50,7 +50,7 @@ #define RLAPI // Functions defined as 'extern' by default (implicit specifiers) #endif -#if defined(CAMERA_STANDALONE) +#if defined(RCAMERA_STANDALONE) #define CAMERA_CULL_DISTANCE_NEAR 0.01 #define CAMERA_CULL_DISTANCE_FAR 1000.0 #else @@ -60,9 +60,9 @@ //---------------------------------------------------------------------------------- // Types and Structures Definition -// NOTE: Below types are required for CAMERA_STANDALONE usage +// NOTE: Below types are required for standalone usage //---------------------------------------------------------------------------------- -#if defined(CAMERA_STANDALONE) +#if defined(RCAMERA_STANDALONE) // Vector2, 2 components typedef struct Vector2 { float x; // Vector x component @@ -138,7 +138,7 @@ RLAPI Matrix GetCameraProjectionMatrix(Camera* camera, float aspect); } #endif -#endif // CAMERA_H +#endif // RCAMERA_H /*********************************************************************************** @@ -147,7 +147,7 @@ RLAPI Matrix GetCameraProjectionMatrix(Camera* camera, float aspect); * ************************************************************************************/ -#if defined(CAMERA_IMPLEMENTATION) +#if defined(RCAMERA_IMPLEMENTATION) #include "raymath.h" // Required for vector maths: // Vector3Add() @@ -417,7 +417,7 @@ Matrix GetCameraProjectionMatrix(Camera *camera, float aspect) return MatrixIdentity(); } -#ifndef CAMERA_STANDALONE +#if !defined(RCAMERA_STANDALONE) // Update camera position for selected mode // Camera mode: CAMERA_FREE, CAMERA_FIRST_PERSON, CAMERA_THIRD_PERSON, CAMERA_ORBITAL or CUSTOM void UpdateCamera(Camera *camera, int mode) @@ -483,7 +483,7 @@ void UpdateCamera(Camera *camera, int mode) if (IsKeyPressed(KEY_KP_ADD)) CameraMoveToTarget(camera, -2.0f); } } -#endif // !CAMERA_STANDALONE +#endif // !RCAMERA_STANDALONE // Update camera movement, movement/rotation values should be provided by user void UpdateCameraPro(Camera *camera, Vector3 movement, Vector3 rotation, float zoom) @@ -516,4 +516,4 @@ void UpdateCameraPro(Camera *camera, Vector3 movement, Vector3 rotation, float z CameraMoveToTarget(camera, zoom); } -#endif // CAMERA_IMPLEMENTATION +#endif // RCAMERA_IMPLEMENTATION diff --git a/src/rcore.c b/src/rcore.c index 6ef25c0bf..e98734ada 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -124,12 +124,12 @@ #include "raymath.h" // Vector3, Quaternion and Matrix functionality #if defined(SUPPORT_GESTURES_SYSTEM) - #define GESTURES_IMPLEMENTATION + #define RGESTURES_IMPLEMENTATION #include "rgestures.h" // Gestures detection functionality #endif #if defined(SUPPORT_CAMERA_SYSTEM) - #define CAMERA_IMPLEMENTATION + #define RCAMERA_IMPLEMENTATION #include "rcamera.h" // Camera system functionality #endif diff --git a/src/rgestures.h b/src/rgestures.h index 35cd7a602..749f440aa 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -3,12 +3,12 @@ * rgestures - Gestures system, gestures processing based on input events (touch/mouse) * * CONFIGURATION: -* #define GESTURES_IMPLEMENTATION +* #define RGESTURES_IMPLEMENTATION * Generates the implementation of the library into the included file. * If not defined, the library is in header only mode and can be included in other headers * or source files without problems. But only ONE file should hold the implementation. * -* #define GESTURES_STANDALONE +* #define RGESTURES_STANDALONE * If defined, the library can be used as standalone to process gesture events with * no external dependencies. * @@ -56,7 +56,7 @@ //---------------------------------------------------------------------------------- // Types and Structures Definition -// NOTE: Below types are required for GESTURES_STANDALONE usage +// NOTE: Below types are required for standalone usage //---------------------------------------------------------------------------------- // Boolean type #if (defined(__STDC__) && __STDC_VERSION__ >= 199901L) || (defined(_MSC_VER) && _MSC_VER >= 1800) @@ -73,7 +73,7 @@ typedef struct Vector2 { } Vector2; #endif -#if defined(GESTURES_STANDALONE) +#if defined(RGESTURES_STANDALONE) // Gestures type // NOTE: It could be used as flags to enable only some gestures typedef enum { @@ -122,7 +122,7 @@ extern "C" { // Prevents name mangling of functions void ProcessGestureEvent(GestureEvent event); // Process gesture event and translate it into gestures void UpdateGestures(void); // Update gestures detected (must be called every frame) -#if defined(GESTURES_STANDALONE) +#if defined(RGESTURES_STANDALONE) void SetGesturesEnabled(unsigned int flags); // Enable a set of gestures using flags bool IsGestureDetected(int gesture); // Check if a gesture have been detected int GetGestureDetected(void); // Get latest detected gesture @@ -138,17 +138,17 @@ float GetGesturePinchAngle(void); // Get gesture pinch ang } #endif -#endif // GESTURES_H +#endif // RGESTURES_H /*********************************************************************************** * -* GESTURES IMPLEMENTATION +* RGESTURES IMPLEMENTATION * ************************************************************************************/ -#if defined(GESTURES_IMPLEMENTATION) +#if defined(RGESTURES_IMPLEMENTATION) -#if defined(GESTURES_STANDALONE) +#if defined(RGESTURES_STANDALONE) #if defined(_WIN32) #if defined(__cplusplus) extern "C" { // Prevents name mangling of functions @@ -527,7 +527,7 @@ static double rgGetCurrentTime(void) { double time = 0; -#if !defined(GESTURES_STANDALONE) +#if !defined(RGESTURES_STANDALONE) time = GetTime(); #else #if defined(_WIN32) @@ -568,4 +568,4 @@ static double rgGetCurrentTime(void) return time; } -#endif // GESTURES_IMPLEMENTATION +#endif // RGESTURES_IMPLEMENTATION diff --git a/src/rtext.c b/src/rtext.c index e7bea9a00..3b21a93c9 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1244,7 +1244,7 @@ Vector2 MeasureTextEx(Font font, const char *text, float fontSize, float spacing if (tempTextWidth < textWidth) tempTextWidth = textWidth; byteCounter = 0; textWidth = 0; - + // NOTE: Line spacing is a global variable, use SetTextLineSpacing() to setup textHeight += (float)textLineSpacing; } From bc40012ca3d719ece17f8ba28865ccb6cdf8bbcf Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 10 Jul 2023 19:15:55 +0200 Subject: [PATCH 0147/1350] Added missing structure on standalone mode #3160 --- src/rcamera.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/rcamera.h b/src/rcamera.h index 4e99c3e8c..c5382684e 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -76,6 +76,14 @@ float z; // Vector z component } Vector3; + // Matrix, 4x4 components, column major, OpenGL style, right-handed + typedef struct Matrix { + float m0, m4, m8, m12; // Matrix first row (4 components) + float m1, m5, m9, m13; // Matrix second row (4 components) + float m2, m6, m10, m14; // Matrix third row (4 components) + float m3, m7, m11, m15; // Matrix fourth row (4 components) + } Matrix; + // Camera type, defines a camera position/orientation in 3d space typedef struct Camera3D { Vector3 position; // Camera position From 2061bfc5e814dd4e2b5ff629cb02bef4c972e9f8 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 10 Jul 2023 19:29:10 +0200 Subject: [PATCH 0148/1350] Reviewed parameter names to avoid issues #3159 --- src/raymath.h | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/raymath.h b/src/raymath.h index d7ec1d252..9281691fa 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -1509,11 +1509,11 @@ RMAPI Matrix MatrixFrustum(double left, double right, double bottom, double top, // Get perspective projection matrix // NOTE: Fovy angle must be provided in radians -RMAPI Matrix MatrixPerspective(double fovy, double aspect, double near, double far) +RMAPI Matrix MatrixPerspective(double fovY, double aspect, double nearPlane, double farPlane) { Matrix result = { 0 }; - double top = near*tan(fovy*0.5); + double top = nearPlane*tan(fovY*0.5); double bottom = -top; double right = top*aspect; double left = -right; @@ -1521,27 +1521,27 @@ RMAPI Matrix MatrixPerspective(double fovy, double aspect, double near, double f // MatrixFrustum(-right, right, -top, top, near, far); float rl = (float)(right - left); float tb = (float)(top - bottom); - float fn = (float)(far - near); + float fn = (float)(farPlane - nearPlane); - result.m0 = ((float)near*2.0f)/rl; - result.m5 = ((float)near*2.0f)/tb; + result.m0 = ((float)nearPlane*2.0f)/rl; + result.m5 = ((float)nearPlane*2.0f)/tb; result.m8 = ((float)right + (float)left)/rl; result.m9 = ((float)top + (float)bottom)/tb; - result.m10 = -((float)far + (float)near)/fn; + result.m10 = -((float)farPlane + (float)nearPlane)/fn; result.m11 = -1.0f; - result.m14 = -((float)far*(float)near*2.0f)/fn; + result.m14 = -((float)farPlane*(float)nearPlane*2.0f)/fn; return result; } // Get orthographic projection matrix -RMAPI Matrix MatrixOrtho(double left, double right, double bottom, double top, double near, double far) +RMAPI Matrix MatrixOrtho(double left, double right, double bottom, double top, double nearPlane, double farPlane) { Matrix result = { 0 }; float rl = (float)(right - left); float tb = (float)(top - bottom); - float fn = (float)(far - near); + float fn = (float)(farPlane - nearPlane); result.m0 = 2.0f/rl; result.m1 = 0.0f; @@ -1557,7 +1557,7 @@ RMAPI Matrix MatrixOrtho(double left, double right, double bottom, double top, d result.m11 = 0.0f; result.m12 = -((float)left + (float)right)/rl; result.m13 = -((float)top + (float)bottom)/tb; - result.m14 = -((float)far + (float)near)/fn; + result.m14 = -((float)farPlane + (float)nearPlane)/fn; result.m15 = 1.0f; return result; From e8181a5ddf22cb6c4b3dd530b5b60e5c4bd24c86 Mon Sep 17 00:00:00 2001 From: Anand Swaroop <72886192+Anut-py@users.noreply.github.com> Date: Tue, 11 Jul 2023 09:21:49 -0400 Subject: [PATCH 0149/1350] Update latest h-raylib version (#3166) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 8c5c8396b..7fc88113e 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -28,7 +28,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylib-go | 4.5 | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | | raylib-guile | **auto** | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | | gforth-raylib | 3.5 | [Gforth](https://gforth.org/) | MIT | https://github.com/ArnautDaniel/gforth-raylib | -| h-raylib | **4.5** | [Haskell](https://haskell.org/) | Apache-2.0 | https://github.com/Anut-py/h-raylib | +| h-raylib | **4.6-dev** | [Haskell](https://haskell.org/) | Apache-2.0 | https://github.com/Anut-py/h-raylib | | raylib-hx | 4.2 | [Haxe](https://haxe.org/) | Zlib | https://github.com/foreignsasquatch/raylib-hx | | hb-raylib | 3.5 | [Harbour](https://harbour.github.io) | MIT | https://github.com/MarcosLeonardoMendezGerencir/hb-raylib | | jaylib | 4.5 | [Janet](https://janet-lang.org/) | MIT | https://github.com/janet-lang/jaylib | From 7f21cf1dcf17225949cbfb0bdeb4677182e5564b Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 12 Jul 2023 10:37:10 +0200 Subject: [PATCH 0150/1350] Revert "UPDATED: `sdefl` and `sinfl` compression libraries" This reverts commit e190b7eee9199b681a8c50fb69f2fce07e92c7af. --- src/external/sdefl.h | 195 +++++++++++++------------------------------ src/external/sinfl.h | 28 +++---- 2 files changed, 68 insertions(+), 155 deletions(-) diff --git a/src/external/sdefl.h b/src/external/sdefl.h index 36015b95b..56539a56e 100644 --- a/src/external/sdefl.h +++ b/src/external/sdefl.h @@ -71,7 +71,7 @@ Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License -Copyright (c) 2020-2023 Micha Mettke +Copyright (c) 2020 Micha Mettke Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to @@ -125,7 +125,7 @@ extern "C" { #define SDEFL_MIN_MATCH 4 #define SDEFL_BLK_MAX (256*1024) -#define SDEFL_SEQ_SIZ ((SDEFL_BLK_MAX+2)/3) +#define SDEFL_SEQ_SIZ ((SDEFL_BLK_MAX + SDEFL_MIN_MATCH)/SDEFL_MIN_MATCH) #define SDEFL_SYM_MAX (288) #define SDEFL_OFF_MAX (32) @@ -185,7 +185,6 @@ extern int zsdeflate(struct sdefl *s, void *o, const void *i, int n, int lvl); #define SDEFL_MAX_CODE_LEN (15) #define SDEFL_SYM_BITS (10u) #define SDEFL_SYM_MSK ((1u << SDEFL_SYM_BITS)-1u) -#define SDEFL_RAW_BLK_SIZE (65535) #define SDEFL_LIT_LEN_CODES (14) #define SDEFL_OFF_CODES (15) #define SDEFL_PRE_CODES (7) @@ -193,7 +192,6 @@ extern int zsdeflate(struct sdefl *s, void *o, const void *i, int n, int lvl); #define SDEFL_EOB (256) #define sdefl_npow2(n) (1 << (sdefl_ilog2((n)-1) + 1)) -#define sdefl_div_round_up(n,d) (((n)+((d)-1))/(d)) static int sdefl_ilog2(int n) { @@ -440,12 +438,12 @@ sdefl_precode(struct sdefl_symcnt *cnt, unsigned *freqs, unsigned *items, } while (run_start != total); cnt->items = (int)(at - items); } -struct sdefl_match_codest { +struct sdefl_match_codes { int ls, lc; int dc, dx; }; static void -sdefl_match_codes(struct sdefl_match_codest *cod, int dist, int len) { +sdefl_match_codes(struct sdefl_match_codes *cod, int dist, int len) { static const short dxmax[] = {0,6,12,24,48,96,192,384,768,1536,3072,6144,12288,24576}; static const unsigned char lslot[258+1] = { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, @@ -473,44 +471,6 @@ sdefl_match_codes(struct sdefl_match_codest *cod, int dist, int len) { cod->dx = sdefl_ilog2(sdefl_npow2(dist) >> 2); cod->dc = cod->dx ? ((cod->dx + 1) << 1) + (dist > dxmax[cod->dx]) : dist-1; } -enum sdefl_blk_type { - SDEFL_BLK_UCOMPR, - SDEFL_BLK_DYN -}; -static enum sdefl_blk_type -sdefl_blk_type(const struct sdefl *s, int blk_len, int pre_item_len, - const unsigned *pre_freq, const unsigned char *pre_len) { - static const unsigned char x_pre_bits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; - static const unsigned char x_len_bits[] = {0,0,0,0,0,0,0,0, 1,1,1,1,2,2,2,2, - 3,3,3,3,4,4,4,4, 5,5,5,5,0}; - static const unsigned char x_off_bits[] = {0,0,0,0,1,1,2,2, 3,3,4,4,5,5,6,6, - 7,7,8,8,9,9,10,10, 11,11,12,12,13,13}; - - int dyn_cost = 0; - int fix_cost = 0; - int sym = 0; - - dyn_cost += 5 + 5 + 4 + (3 * pre_item_len); - for (sym = 0; sym < SDEFL_PRE_MAX; sym++) - dyn_cost += pre_freq[sym] * (x_pre_bits[sym] + pre_len[sym]); - for (sym = 0; sym < 256; sym++) - dyn_cost += s->freq.lit[sym] * s->cod.len.lit[sym]; - dyn_cost += s->cod.len.lit[SDEFL_EOB]; - for (sym = 257; sym < 286; sym++) - dyn_cost += s->freq.lit[sym] * (x_len_bits[sym - 257] + s->cod.len.lit[sym]); - for (sym = 0; sym < 30; sym++) - dyn_cost += s->freq.off[sym] * (x_off_bits[sym] + s->cod.len.off[sym]); - - fix_cost += 8*(5 * sdefl_div_round_up(blk_len, SDEFL_RAW_BLK_SIZE) + blk_len + 1 + 2); - return (dyn_cost < fix_cost) ? SDEFL_BLK_DYN : SDEFL_BLK_UCOMPR; -} -static void -sdefl_put16(unsigned char **dst, unsigned short x) { - unsigned char *val = *dst; - val[0] = (unsigned char)(x & 0xff); - val[1] = (unsigned char)(x >> 8); - *dst = val + 2; -} static void sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) { static const char lxn[] = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; @@ -519,7 +479,7 @@ sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) { static const short dmin[] = {1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257, 385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577}; - struct sdefl_match_codest cod; + struct sdefl_match_codes cod; sdefl_match_codes(&cod, dist, len); sdefl_put(dst, s, (int)s->cod.word.lit[cod.lc], s->cod.len.lit[cod.lc]); sdefl_put(dst, s, len - lmin[cod.ls], lxn[cod.ls]); @@ -528,8 +488,7 @@ sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) { } static void sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, - const unsigned char *in, int blk_begin, int blk_end) { - int blk_len = blk_end - blk_begin; + const unsigned char *in) { int j, i = 0, item_cnt = 0; struct sdefl_symcnt symcnt = {0}; unsigned codes[SDEFL_PRE_MAX]; @@ -539,7 +498,7 @@ sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, static const unsigned char perm[SDEFL_PRE_MAX] = {16,17,18,0,8,7,9,6,10,5,11, 4,12,3,13,2,14,1,15}; - /* calculate huffman codes */ + /* huffman codes */ s->freq.lit[SDEFL_EOB]++; sdefl_huff(s->cod.len.lit, s->cod.word.lit, s->freq.lit, SDEFL_SYM_MAX, SDEFL_LIT_LEN_CODES); sdefl_huff(s->cod.len.off, s->cod.word.off, s->freq.off, SDEFL_OFF_MAX, SDEFL_OFF_CODES); @@ -550,58 +509,35 @@ sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, break; } } - /* write block */ - switch (sdefl_blk_type(s, blk_len, item_cnt, freqs, lens)) { - case SDEFL_BLK_UCOMPR: { - /* uncompressed blocks */ - int n = sdefl_div_round_up(blk_len, SDEFL_RAW_BLK_SIZE); - for (i = 0; i < n; ++i) { - int fin = is_last && (i + 1 == n); - int amount = blk_len < SDEFL_RAW_BLK_SIZE ? blk_len : SDEFL_RAW_BLK_SIZE; - sdefl_put(dst, s, !!fin, 1); /* block */ - sdefl_put(dst, s, 0x00, 2); /* stored block */ - if (s->bitcnt) { - sdefl_put(dst, s, 0x00, 8 - s->bitcnt); + /* block header */ + sdefl_put(dst, s, is_last ? 0x01 : 0x00, 1); /* block */ + sdefl_put(dst, s, 0x02, 2); /* dynamic huffman */ + sdefl_put(dst, s, symcnt.lit - 257, 5); + sdefl_put(dst, s, symcnt.off - 1, 5); + sdefl_put(dst, s, item_cnt - 4, 4); + for (i = 0; i < item_cnt; ++i) { + sdefl_put(dst, s, lens[perm[i]], 3); + } + for (i = 0; i < symcnt.items; ++i) { + unsigned sym = items[i] & 0x1F; + sdefl_put(dst, s, (int)codes[sym], lens[sym]); + if (sym < 16) continue; + if (sym == 16) sdefl_put(dst, s, items[i] >> 5, 2); + else if(sym == 17) sdefl_put(dst, s, items[i] >> 5, 3); + else sdefl_put(dst, s, items[i] >> 5, 7); + } + /* block sequences */ + for (i = 0; i < s->seq_cnt; ++i) { + if (s->seq[i].off >= 0) { + for (j = 0; j < s->seq[i].len; ++j) { + int c = in[s->seq[i].off + j]; + sdefl_put(dst, s, (int)s->cod.word.lit[c], s->cod.len.lit[c]); } - assert(s->bitcnt == 0); - sdefl_put16(dst, (unsigned short)amount); - sdefl_put16(dst, ~(unsigned short)amount); - memcpy(*dst, in + blk_begin + i * SDEFL_RAW_BLK_SIZE, amount); - *dst = *dst + amount; - blk_len -= amount; + } else { + sdefl_match(dst, s, -s->seq[i].off, s->seq[i].len); } - } break; - case SDEFL_BLK_DYN: { - /* dynamic huffman block */ - sdefl_put(dst, s, !!is_last, 1); /* block */ - sdefl_put(dst, s, 0x02, 2); /* dynamic huffman */ - sdefl_put(dst, s, symcnt.lit - 257, 5); - sdefl_put(dst, s, symcnt.off - 1, 5); - sdefl_put(dst, s, item_cnt - 4, 4); - for (i = 0; i < item_cnt; ++i) { - sdefl_put(dst, s, lens[perm[i]], 3); - } - for (i = 0; i < symcnt.items; ++i) { - unsigned sym = items[i] & 0x1F; - sdefl_put(dst, s, (int)codes[sym], lens[sym]); - if (sym < 16) continue; - if (sym == 16) sdefl_put(dst, s, items[i] >> 5, 2); - else if(sym == 17) sdefl_put(dst, s, items[i] >> 5, 3); - else sdefl_put(dst, s, items[i] >> 5, 7); - } - /* block sequences */ - for (i = 0; i < s->seq_cnt; ++i) { - if (s->seq[i].off >= 0) { - for (j = 0; j < s->seq[i].len; ++j) { - int c = in[s->seq[i].off + j]; - sdefl_put(dst, s, (int)s->cod.word.lit[c], s->cod.len.lit[c]); - } - } else { - sdefl_match(dst, s, -s->seq[i].off, s->seq[i].len); - } - } - sdefl_put(dst, s, (int)(s)->cod.word.lit[SDEFL_EOB], (s)->cod.len.lit[SDEFL_EOB]); - } break;} + } + sdefl_put(dst, s, (int)(s)->cod.word.lit[SDEFL_EOB], (s)->cod.len.lit[SDEFL_EOB]); memset(&s->freq, 0, sizeof(s->freq)); s->seq_cnt = 0; } @@ -614,12 +550,8 @@ sdefl_seq(struct sdefl *s, int off, int len) { } static void sdefl_reg_match(struct sdefl *s, int off, int len) { - struct sdefl_match_codest cod; + struct sdefl_match_codes cod; sdefl_match_codes(&cod, off, len); - - assert(cod.lc < SDEFL_SYM_MAX); - assert(cod.dc < SDEFL_OFF_MAX); - s->freq.lit[cod.lc]++; s->freq.off[cod.dc]++; } @@ -628,35 +560,22 @@ struct sdefl_match { int len; }; static void -sdefl_fnd(struct sdefl_match *m, const struct sdefl *s, int chain_len, - int max_match, const unsigned char *in, int p, int e) { - int i = s->tbl[sdefl_hash32(in + p)]; - int limit = ((p - SDEFL_WIN_SIZ) < SDEFL_NIL) ? SDEFL_NIL : (p-SDEFL_WIN_SIZ); - - assert(p < e); - assert(p + max_match <= e); +sdefl_fnd(struct sdefl_match *m, const struct sdefl *s, + int chain_len, int max_match, const unsigned char *in, int p) { + int i = s->tbl[sdefl_hash32(&in[p])]; + int limit = ((p-SDEFL_WIN_SIZ) limit) { - assert(i + m->len < e); - assert(p + m->len < e); - assert(i + SDEFL_MIN_MATCH < e); - assert(p + SDEFL_MIN_MATCH < e); - - if (in[i + m->len] == in[p + m->len] && - (sdefl_uload32(&in[i]) == sdefl_uload32(&in[p]))) { + if (in[i+m->len] == in[p+m->len] && + (sdefl_uload32(&in[i]) == sdefl_uload32(&in[p]))){ int n = SDEFL_MIN_MATCH; - while (n < max_match && in[i + n] == in[p + n]) { - assert(i + n < e); - assert(p + n < e); - n++; - } + while (n < max_match && in[i+n] == in[p+n]) n++; if (n > m->len) { m->len = n, m->off = p - i; - if (n == max_match) - break; + if (n == max_match) break; } } if (!(--chain_len)) break; - i = s->prv[i & SDEFL_WIN_MSK]; + i = s->prv[i&SDEFL_WIN_MSK]; } } static int @@ -669,20 +588,19 @@ sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in, for (n = 0; n < SDEFL_HASH_SIZ; ++n) { s->tbl[n] = SDEFL_NIL; } - do {int blk_begin = i; - int blk_end = ((i + SDEFL_BLK_MAX) < in_len) ? (i + SDEFL_BLK_MAX) : in_len; + do {int blk_end = ((i + SDEFL_BLK_MAX) < in_len) ? (i + SDEFL_BLK_MAX) : in_len; while (i < blk_end) { struct sdefl_match m = {0}; int left = blk_end - i; - int max_match = (left > SDEFL_MAX_MATCH) ? SDEFL_MAX_MATCH : left; + int max_match = (left >= SDEFL_MAX_MATCH) ? SDEFL_MAX_MATCH : left; int nice_match = pref[lvl] < max_match ? pref[lvl] : max_match; int run = 1, inc = 1, run_inc = 0; if (max_match > SDEFL_MIN_MATCH) { - sdefl_fnd(&m, s, max_chain, max_match, in, i, in_len); + sdefl_fnd(&m, s, max_chain, max_match, in, i); } - if (lvl >= 5 && m.len >= SDEFL_MIN_MATCH && m.len + 1 < nice_match){ + if (lvl >= 5 && m.len >= SDEFL_MIN_MATCH && m.len < nice_match){ struct sdefl_match m2 = {0}; - sdefl_fnd(&m2, s, max_chain, m.len + 1, in, i + 1, in_len); + sdefl_fnd(&m2, s, max_chain, m.len+1, in, i+1); m.len = (m2.len > m.len) ? 0 : m.len; } if (m.len >= SDEFL_MIN_MATCH) { @@ -718,12 +636,12 @@ sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in, sdefl_seq(s, i - litlen, litlen); litlen = 0; } - sdefl_flush(&q, s, blk_end == in_len, in, blk_begin, blk_end); + sdefl_flush(&q, s, blk_end == in_len, in); } while (i < in_len); - if (s->bitcnt) { + + if (s->bitcnt > 0) sdefl_put(&q, s, 0x00, 8 - s->bitcnt); - } - assert(s->bitcnt == 0); + return (int)(q - out); } extern int @@ -783,8 +701,9 @@ zsdeflate(struct sdefl *s, void *out, const void *in, int n, int lvl) { } extern int sdefl_bound(int len) { - int max_blocks = 1 + sdefl_div_round_up(len, SDEFL_RAW_BLK_SIZE); - int bound = 5 * max_blocks + len + 1 + 4 + 8; - return bound; + int a = 128 + (len * 110) / 100; + int b = 128 + len + ((len / (31 * 1024)) + 1) * 5; + return (a > b) ? a : b; } #endif /* SDEFL_IMPLEMENTATION */ + diff --git a/src/external/sinfl.h b/src/external/sinfl.h index 8979fcd7f..915da9d23 100644 --- a/src/external/sinfl.h +++ b/src/external/sinfl.h @@ -72,7 +72,7 @@ Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License -Copyright (c) 2020-2023 Micha Mettke +Copyright (c) 2020 Micha Mettke Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to @@ -400,21 +400,17 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) } break; case stored: { /* uncompressed block */ - unsigned len, nlen; + int len, nlen; + sinfl_refill(&s); sinfl__get(&s,s.bitcnt & 7); - len = (unsigned short)sinfl__get(&s,16); - nlen = (unsigned short)sinfl__get(&s,16); - s.bitptr -= s.bitcnt / 8; - s.bitbuf = s.bitcnt = 0; + len = sinfl__get(&s,16); + nlen = sinfl__get(&s,16); + in -= 2; s.bitcnt = 0; - if ((unsigned short)len != (unsigned short)~nlen) + if (len > (e-in) || !len) return (int)(out-o); - if (len > (e - s.bitptr) || !len) - return (int)(out-o); - - memcpy(out, s.bitptr, (size_t)len); - s.bitptr += len, out += len; - if (last) return (int)(out-o); + memcpy(out, in, (size_t)len); + in += len, out += len; state = hdr; } break; case fixed: { @@ -447,9 +443,8 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) /* decode code lengths */ for (n = 0; n < nlit + ndist;) { - int sym = 0; sinfl_refill(&s); - sym = sinfl_decode(&s, hlens, 7); + int sym = sinfl_decode(&s, hlens, 7); switch (sym) {default: lens[n++] = (unsigned char)sym; break; case 16: for (i=3+sinfl_get(&s,2);i;i--,n++) lens[n]=lens[n-1]; break; case 17: for (i=3+sinfl_get(&s,3);i;i--,n++) lens[n]=0; break; @@ -463,9 +458,8 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) case blk: { /* decompress block */ while (1) { - int sym; sinfl_refill(&s); - sym = sinfl_decode(&s, s.lits, 10); + int sym = sinfl_decode(&s, s.lits, 10); if (sym < 256) { /* literal */ if (sinfl_unlikely(out >= oe)) { From 8096f142ecc9607df6b37a924b56202f8eb31657 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 12 Jul 2023 07:02:33 -0300 Subject: [PATCH 0151/1350] Add a new task the issue template about checking the wiki (#3169) --- .github/ISSUE_TEMPLATE/new-issue-template.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/ISSUE_TEMPLATE/new-issue-template.md b/.github/ISSUE_TEMPLATE/new-issue-template.md index 096eb28cb..4bac86e82 100644 --- a/.github/ISSUE_TEMPLATE/new-issue-template.md +++ b/.github/ISSUE_TEMPLATE/new-issue-template.md @@ -21,6 +21,7 @@ Please, before submitting a new issue verify and check: - [ ] I tested it on latest raylib version from master branch - [ ] I checked there is no similar issue already reported + - [ ] I checked the documentation on the [wiki](https://github.com/raysan5/raylib/wiki) - [ ] My code has no errors or misuse of raylib ### Issue description From bc9c06325481c0b4b5a2db3b2a8281465569ba3e Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 12 Jul 2023 15:49:38 +0200 Subject: [PATCH 0152/1350] Update external sdefl and sinfl --- src/external/sdefl.h | 195 ++++++++++++++++++++++++++++++------------- src/external/sinfl.h | 28 ++++--- 2 files changed, 155 insertions(+), 68 deletions(-) diff --git a/src/external/sdefl.h b/src/external/sdefl.h index 56539a56e..36015b95b 100644 --- a/src/external/sdefl.h +++ b/src/external/sdefl.h @@ -71,7 +71,7 @@ Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License -Copyright (c) 2020 Micha Mettke +Copyright (c) 2020-2023 Micha Mettke Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to @@ -125,7 +125,7 @@ extern "C" { #define SDEFL_MIN_MATCH 4 #define SDEFL_BLK_MAX (256*1024) -#define SDEFL_SEQ_SIZ ((SDEFL_BLK_MAX + SDEFL_MIN_MATCH)/SDEFL_MIN_MATCH) +#define SDEFL_SEQ_SIZ ((SDEFL_BLK_MAX+2)/3) #define SDEFL_SYM_MAX (288) #define SDEFL_OFF_MAX (32) @@ -185,6 +185,7 @@ extern int zsdeflate(struct sdefl *s, void *o, const void *i, int n, int lvl); #define SDEFL_MAX_CODE_LEN (15) #define SDEFL_SYM_BITS (10u) #define SDEFL_SYM_MSK ((1u << SDEFL_SYM_BITS)-1u) +#define SDEFL_RAW_BLK_SIZE (65535) #define SDEFL_LIT_LEN_CODES (14) #define SDEFL_OFF_CODES (15) #define SDEFL_PRE_CODES (7) @@ -192,6 +193,7 @@ extern int zsdeflate(struct sdefl *s, void *o, const void *i, int n, int lvl); #define SDEFL_EOB (256) #define sdefl_npow2(n) (1 << (sdefl_ilog2((n)-1) + 1)) +#define sdefl_div_round_up(n,d) (((n)+((d)-1))/(d)) static int sdefl_ilog2(int n) { @@ -438,12 +440,12 @@ sdefl_precode(struct sdefl_symcnt *cnt, unsigned *freqs, unsigned *items, } while (run_start != total); cnt->items = (int)(at - items); } -struct sdefl_match_codes { +struct sdefl_match_codest { int ls, lc; int dc, dx; }; static void -sdefl_match_codes(struct sdefl_match_codes *cod, int dist, int len) { +sdefl_match_codes(struct sdefl_match_codest *cod, int dist, int len) { static const short dxmax[] = {0,6,12,24,48,96,192,384,768,1536,3072,6144,12288,24576}; static const unsigned char lslot[258+1] = { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, @@ -471,6 +473,44 @@ sdefl_match_codes(struct sdefl_match_codes *cod, int dist, int len) { cod->dx = sdefl_ilog2(sdefl_npow2(dist) >> 2); cod->dc = cod->dx ? ((cod->dx + 1) << 1) + (dist > dxmax[cod->dx]) : dist-1; } +enum sdefl_blk_type { + SDEFL_BLK_UCOMPR, + SDEFL_BLK_DYN +}; +static enum sdefl_blk_type +sdefl_blk_type(const struct sdefl *s, int blk_len, int pre_item_len, + const unsigned *pre_freq, const unsigned char *pre_len) { + static const unsigned char x_pre_bits[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + static const unsigned char x_len_bits[] = {0,0,0,0,0,0,0,0, 1,1,1,1,2,2,2,2, + 3,3,3,3,4,4,4,4, 5,5,5,5,0}; + static const unsigned char x_off_bits[] = {0,0,0,0,1,1,2,2, 3,3,4,4,5,5,6,6, + 7,7,8,8,9,9,10,10, 11,11,12,12,13,13}; + + int dyn_cost = 0; + int fix_cost = 0; + int sym = 0; + + dyn_cost += 5 + 5 + 4 + (3 * pre_item_len); + for (sym = 0; sym < SDEFL_PRE_MAX; sym++) + dyn_cost += pre_freq[sym] * (x_pre_bits[sym] + pre_len[sym]); + for (sym = 0; sym < 256; sym++) + dyn_cost += s->freq.lit[sym] * s->cod.len.lit[sym]; + dyn_cost += s->cod.len.lit[SDEFL_EOB]; + for (sym = 257; sym < 286; sym++) + dyn_cost += s->freq.lit[sym] * (x_len_bits[sym - 257] + s->cod.len.lit[sym]); + for (sym = 0; sym < 30; sym++) + dyn_cost += s->freq.off[sym] * (x_off_bits[sym] + s->cod.len.off[sym]); + + fix_cost += 8*(5 * sdefl_div_round_up(blk_len, SDEFL_RAW_BLK_SIZE) + blk_len + 1 + 2); + return (dyn_cost < fix_cost) ? SDEFL_BLK_DYN : SDEFL_BLK_UCOMPR; +} +static void +sdefl_put16(unsigned char **dst, unsigned short x) { + unsigned char *val = *dst; + val[0] = (unsigned char)(x & 0xff); + val[1] = (unsigned char)(x >> 8); + *dst = val + 2; +} static void sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) { static const char lxn[] = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; @@ -479,7 +519,7 @@ sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) { static const short dmin[] = {1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257, 385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577}; - struct sdefl_match_codes cod; + struct sdefl_match_codest cod; sdefl_match_codes(&cod, dist, len); sdefl_put(dst, s, (int)s->cod.word.lit[cod.lc], s->cod.len.lit[cod.lc]); sdefl_put(dst, s, len - lmin[cod.ls], lxn[cod.ls]); @@ -488,7 +528,8 @@ sdefl_match(unsigned char **dst, struct sdefl *s, int dist, int len) { } static void sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, - const unsigned char *in) { + const unsigned char *in, int blk_begin, int blk_end) { + int blk_len = blk_end - blk_begin; int j, i = 0, item_cnt = 0; struct sdefl_symcnt symcnt = {0}; unsigned codes[SDEFL_PRE_MAX]; @@ -498,7 +539,7 @@ sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, static const unsigned char perm[SDEFL_PRE_MAX] = {16,17,18,0,8,7,9,6,10,5,11, 4,12,3,13,2,14,1,15}; - /* huffman codes */ + /* calculate huffman codes */ s->freq.lit[SDEFL_EOB]++; sdefl_huff(s->cod.len.lit, s->cod.word.lit, s->freq.lit, SDEFL_SYM_MAX, SDEFL_LIT_LEN_CODES); sdefl_huff(s->cod.len.off, s->cod.word.off, s->freq.off, SDEFL_OFF_MAX, SDEFL_OFF_CODES); @@ -509,35 +550,58 @@ sdefl_flush(unsigned char **dst, struct sdefl *s, int is_last, break; } } - /* block header */ - sdefl_put(dst, s, is_last ? 0x01 : 0x00, 1); /* block */ - sdefl_put(dst, s, 0x02, 2); /* dynamic huffman */ - sdefl_put(dst, s, symcnt.lit - 257, 5); - sdefl_put(dst, s, symcnt.off - 1, 5); - sdefl_put(dst, s, item_cnt - 4, 4); - for (i = 0; i < item_cnt; ++i) { - sdefl_put(dst, s, lens[perm[i]], 3); - } - for (i = 0; i < symcnt.items; ++i) { - unsigned sym = items[i] & 0x1F; - sdefl_put(dst, s, (int)codes[sym], lens[sym]); - if (sym < 16) continue; - if (sym == 16) sdefl_put(dst, s, items[i] >> 5, 2); - else if(sym == 17) sdefl_put(dst, s, items[i] >> 5, 3); - else sdefl_put(dst, s, items[i] >> 5, 7); - } - /* block sequences */ - for (i = 0; i < s->seq_cnt; ++i) { - if (s->seq[i].off >= 0) { - for (j = 0; j < s->seq[i].len; ++j) { - int c = in[s->seq[i].off + j]; - sdefl_put(dst, s, (int)s->cod.word.lit[c], s->cod.len.lit[c]); + /* write block */ + switch (sdefl_blk_type(s, blk_len, item_cnt, freqs, lens)) { + case SDEFL_BLK_UCOMPR: { + /* uncompressed blocks */ + int n = sdefl_div_round_up(blk_len, SDEFL_RAW_BLK_SIZE); + for (i = 0; i < n; ++i) { + int fin = is_last && (i + 1 == n); + int amount = blk_len < SDEFL_RAW_BLK_SIZE ? blk_len : SDEFL_RAW_BLK_SIZE; + sdefl_put(dst, s, !!fin, 1); /* block */ + sdefl_put(dst, s, 0x00, 2); /* stored block */ + if (s->bitcnt) { + sdefl_put(dst, s, 0x00, 8 - s->bitcnt); } - } else { - sdefl_match(dst, s, -s->seq[i].off, s->seq[i].len); + assert(s->bitcnt == 0); + sdefl_put16(dst, (unsigned short)amount); + sdefl_put16(dst, ~(unsigned short)amount); + memcpy(*dst, in + blk_begin + i * SDEFL_RAW_BLK_SIZE, amount); + *dst = *dst + amount; + blk_len -= amount; } - } - sdefl_put(dst, s, (int)(s)->cod.word.lit[SDEFL_EOB], (s)->cod.len.lit[SDEFL_EOB]); + } break; + case SDEFL_BLK_DYN: { + /* dynamic huffman block */ + sdefl_put(dst, s, !!is_last, 1); /* block */ + sdefl_put(dst, s, 0x02, 2); /* dynamic huffman */ + sdefl_put(dst, s, symcnt.lit - 257, 5); + sdefl_put(dst, s, symcnt.off - 1, 5); + sdefl_put(dst, s, item_cnt - 4, 4); + for (i = 0; i < item_cnt; ++i) { + sdefl_put(dst, s, lens[perm[i]], 3); + } + for (i = 0; i < symcnt.items; ++i) { + unsigned sym = items[i] & 0x1F; + sdefl_put(dst, s, (int)codes[sym], lens[sym]); + if (sym < 16) continue; + if (sym == 16) sdefl_put(dst, s, items[i] >> 5, 2); + else if(sym == 17) sdefl_put(dst, s, items[i] >> 5, 3); + else sdefl_put(dst, s, items[i] >> 5, 7); + } + /* block sequences */ + for (i = 0; i < s->seq_cnt; ++i) { + if (s->seq[i].off >= 0) { + for (j = 0; j < s->seq[i].len; ++j) { + int c = in[s->seq[i].off + j]; + sdefl_put(dst, s, (int)s->cod.word.lit[c], s->cod.len.lit[c]); + } + } else { + sdefl_match(dst, s, -s->seq[i].off, s->seq[i].len); + } + } + sdefl_put(dst, s, (int)(s)->cod.word.lit[SDEFL_EOB], (s)->cod.len.lit[SDEFL_EOB]); + } break;} memset(&s->freq, 0, sizeof(s->freq)); s->seq_cnt = 0; } @@ -550,8 +614,12 @@ sdefl_seq(struct sdefl *s, int off, int len) { } static void sdefl_reg_match(struct sdefl *s, int off, int len) { - struct sdefl_match_codes cod; + struct sdefl_match_codest cod; sdefl_match_codes(&cod, off, len); + + assert(cod.lc < SDEFL_SYM_MAX); + assert(cod.dc < SDEFL_OFF_MAX); + s->freq.lit[cod.lc]++; s->freq.off[cod.dc]++; } @@ -560,22 +628,35 @@ struct sdefl_match { int len; }; static void -sdefl_fnd(struct sdefl_match *m, const struct sdefl *s, - int chain_len, int max_match, const unsigned char *in, int p) { - int i = s->tbl[sdefl_hash32(&in[p])]; - int limit = ((p-SDEFL_WIN_SIZ)tbl[sdefl_hash32(in + p)]; + int limit = ((p - SDEFL_WIN_SIZ) < SDEFL_NIL) ? SDEFL_NIL : (p-SDEFL_WIN_SIZ); + + assert(p < e); + assert(p + max_match <= e); while (i > limit) { - if (in[i+m->len] == in[p+m->len] && - (sdefl_uload32(&in[i]) == sdefl_uload32(&in[p]))){ + assert(i + m->len < e); + assert(p + m->len < e); + assert(i + SDEFL_MIN_MATCH < e); + assert(p + SDEFL_MIN_MATCH < e); + + if (in[i + m->len] == in[p + m->len] && + (sdefl_uload32(&in[i]) == sdefl_uload32(&in[p]))) { int n = SDEFL_MIN_MATCH; - while (n < max_match && in[i+n] == in[p+n]) n++; + while (n < max_match && in[i + n] == in[p + n]) { + assert(i + n < e); + assert(p + n < e); + n++; + } if (n > m->len) { m->len = n, m->off = p - i; - if (n == max_match) break; + if (n == max_match) + break; } } if (!(--chain_len)) break; - i = s->prv[i&SDEFL_WIN_MSK]; + i = s->prv[i & SDEFL_WIN_MSK]; } } static int @@ -588,19 +669,20 @@ sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in, for (n = 0; n < SDEFL_HASH_SIZ; ++n) { s->tbl[n] = SDEFL_NIL; } - do {int blk_end = ((i + SDEFL_BLK_MAX) < in_len) ? (i + SDEFL_BLK_MAX) : in_len; + do {int blk_begin = i; + int blk_end = ((i + SDEFL_BLK_MAX) < in_len) ? (i + SDEFL_BLK_MAX) : in_len; while (i < blk_end) { struct sdefl_match m = {0}; int left = blk_end - i; - int max_match = (left >= SDEFL_MAX_MATCH) ? SDEFL_MAX_MATCH : left; + int max_match = (left > SDEFL_MAX_MATCH) ? SDEFL_MAX_MATCH : left; int nice_match = pref[lvl] < max_match ? pref[lvl] : max_match; int run = 1, inc = 1, run_inc = 0; if (max_match > SDEFL_MIN_MATCH) { - sdefl_fnd(&m, s, max_chain, max_match, in, i); + sdefl_fnd(&m, s, max_chain, max_match, in, i, in_len); } - if (lvl >= 5 && m.len >= SDEFL_MIN_MATCH && m.len < nice_match){ + if (lvl >= 5 && m.len >= SDEFL_MIN_MATCH && m.len + 1 < nice_match){ struct sdefl_match m2 = {0}; - sdefl_fnd(&m2, s, max_chain, m.len+1, in, i+1); + sdefl_fnd(&m2, s, max_chain, m.len + 1, in, i + 1, in_len); m.len = (m2.len > m.len) ? 0 : m.len; } if (m.len >= SDEFL_MIN_MATCH) { @@ -636,12 +718,12 @@ sdefl_compr(struct sdefl *s, unsigned char *out, const unsigned char *in, sdefl_seq(s, i - litlen, litlen); litlen = 0; } - sdefl_flush(&q, s, blk_end == in_len, in); + sdefl_flush(&q, s, blk_end == in_len, in, blk_begin, blk_end); } while (i < in_len); - - if (s->bitcnt > 0) + if (s->bitcnt) { sdefl_put(&q, s, 0x00, 8 - s->bitcnt); - + } + assert(s->bitcnt == 0); return (int)(q - out); } extern int @@ -701,9 +783,8 @@ zsdeflate(struct sdefl *s, void *out, const void *in, int n, int lvl) { } extern int sdefl_bound(int len) { - int a = 128 + (len * 110) / 100; - int b = 128 + len + ((len / (31 * 1024)) + 1) * 5; - return (a > b) ? a : b; + int max_blocks = 1 + sdefl_div_round_up(len, SDEFL_RAW_BLK_SIZE); + int bound = 5 * max_blocks + len + 1 + 4 + 8; + return bound; } #endif /* SDEFL_IMPLEMENTATION */ - diff --git a/src/external/sinfl.h b/src/external/sinfl.h index 915da9d23..8979fcd7f 100644 --- a/src/external/sinfl.h +++ b/src/external/sinfl.h @@ -72,7 +72,7 @@ Results on the [Silesia compression corpus](http://sun.aei.polsl.pl/~sdeor/index This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------ ALTERNATIVE A - MIT License -Copyright (c) 2020 Micha Mettke +Copyright (c) 2020-2023 Micha Mettke Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to @@ -400,17 +400,21 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) } break; case stored: { /* uncompressed block */ - int len, nlen; - sinfl_refill(&s); + unsigned len, nlen; sinfl__get(&s,s.bitcnt & 7); - len = sinfl__get(&s,16); - nlen = sinfl__get(&s,16); - in -= 2; s.bitcnt = 0; + len = (unsigned short)sinfl__get(&s,16); + nlen = (unsigned short)sinfl__get(&s,16); + s.bitptr -= s.bitcnt / 8; + s.bitbuf = s.bitcnt = 0; - if (len > (e-in) || !len) + if ((unsigned short)len != (unsigned short)~nlen) return (int)(out-o); - memcpy(out, in, (size_t)len); - in += len, out += len; + if (len > (e - s.bitptr) || !len) + return (int)(out-o); + + memcpy(out, s.bitptr, (size_t)len); + s.bitptr += len, out += len; + if (last) return (int)(out-o); state = hdr; } break; case fixed: { @@ -443,8 +447,9 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) /* decode code lengths */ for (n = 0; n < nlit + ndist;) { + int sym = 0; sinfl_refill(&s); - int sym = sinfl_decode(&s, hlens, 7); + sym = sinfl_decode(&s, hlens, 7); switch (sym) {default: lens[n++] = (unsigned char)sym; break; case 16: for (i=3+sinfl_get(&s,2);i;i--,n++) lens[n]=lens[n-1]; break; case 17: for (i=3+sinfl_get(&s,3);i;i--,n++) lens[n]=0; break; @@ -458,8 +463,9 @@ sinfl_decompress(unsigned char *out, int cap, const unsigned char *in, int size) case blk: { /* decompress block */ while (1) { + int sym; sinfl_refill(&s); - int sym = sinfl_decode(&s, s.lits, 10); + sym = sinfl_decode(&s, s.lits, 10); if (sym < 256) { /* literal */ if (sinfl_unlikely(out >= oe)) { From 22895ba14fab982ca74ec1526b48b924d1174e60 Mon Sep 17 00:00:00 2001 From: Roy Qu Date: Fri, 14 Jul 2023 03:20:06 +0800 Subject: [PATCH 0153/1350] fix: cmake option "OPENGL_VERSION" doesn't work (#3170) --- cmake/LibraryConfigurations.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/LibraryConfigurations.cmake b/cmake/LibraryConfigurations.cmake index e9eee630a..d12df3a00 100644 --- a/cmake/LibraryConfigurations.cmake +++ b/cmake/LibraryConfigurations.cmake @@ -106,7 +106,7 @@ elseif ("${PLATFORM}" MATCHES "DRM") endif () -if (NOT ${OPENGL_VERSION}) +if (NOT ${OPENGL_VERSION} MATCHES "OFF") set(${SUGGESTED_GRAPHICS} "${GRAPHICS}") if (${OPENGL_VERSION} MATCHES "4.3") set(GRAPHICS "GRAPHICS_API_OPENGL_43") From b980268ba7fd1fb5083546ca3f0b20f3390fe128 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sun, 16 Jul 2023 08:07:29 -0300 Subject: [PATCH 0154/1350] [example] Core Input Gestures for Web (#3172) * [example] Core Input Gestures for Web * Fix Doubletap for web * Changes TAP_TIMEOUT and rgGetCurrentTime to seconds --- examples/Makefile | 1 + examples/Makefile.Web | 26 +- examples/core/core_input_gestures_web.c | 330 ++++++++++++++++++++++ examples/core/core_input_gestures_web.png | Bin 0 -> 8937 bytes src/rcore.c | 6 + src/rgestures.h | 16 +- 6 files changed, 360 insertions(+), 19 deletions(-) create mode 100644 examples/core/core_input_gestures_web.c create mode 100644 examples/core/core_input_gestures_web.png diff --git a/examples/Makefile b/examples/Makefile index 15edf1b36..1a4d5f06c 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -405,6 +405,7 @@ CORE = \ core/core_input_gamepad \ core/core_input_multitouch \ core/core_input_gestures \ + core/core_input_gestures_web \ core/core_2d_camera \ core/core_2d_camera_platformer \ core/core_2d_camera_mouse_zoom \ diff --git a/examples/Makefile.Web b/examples/Makefile.Web index 499eb3eaa..e65351e07 100644 --- a/examples/Makefile.Web +++ b/examples/Makefile.Web @@ -386,6 +386,7 @@ CORE = \ core/core_input_gamepad \ core/core_input_multitouch \ core/core_input_gestures \ + core/core_input_gestures_web \ core/core_2d_camera \ core/core_2d_camera_platformer \ core/core_2d_camera_mouse_zoom \ @@ -558,12 +559,15 @@ core/core_input_multitouch: core/core_input_multitouch.c core/core_input_gestures: core/core_input_gestures.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) +core/core_input_gestures_web: core/core_input_gestures_web.c + $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) + core/core_2d_camera: core/core_2d_camera.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) core/core_2d_camera_platformer: core/core_2d_camera_platformer.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - + core/core_2d_camera_mouse_zoom: core/core_2d_camera_mouse_zoom.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) @@ -618,10 +622,10 @@ core/core_custom_frame_control: core/core_custom_frame_control.c core/core_window_should_close: core/core_window_should_close.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) - + # NOTE: To use multi-threading raylib must be compiled with multi-theading support (-s USE_PTHREADS=1) # WARNING: For security reasons multi-threading is not supported on browsers, it requires cross-origin isolation (Oct.2021) -# WARNING: It requires raylib to be compiled using -pthread, so atomic operations and thread-local data (if any) +# WARNING: It requires raylib to be compiled using -pthread, so atomic operations and thread-local data (if any) # in its source were transformed to non-atomic operations and non-thread-local data core/core_loading_thread: core/core_loading_thread.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s USE_PTHREADS=1 @@ -745,7 +749,7 @@ textures/textures_sprite_explosion: textures/textures_sprite_explosion.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file textures/resources/explosion.png@resources/explosion.png \ --preload-file textures/resources/boom.wav@resources/boom.wav - + textures/textures_textured_curve: textures/textures_textured_curve.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file textures/resources/road.png@resources/road.png @@ -857,8 +861,8 @@ models/models_cubicmap: models/models_cubicmap.c models/models_draw_cube_texture: models/models_draw_cube_texture.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ - --preload-file models/resources/cubicmap_atlas.png@resources/cubicmap_atlas.png - + --preload-file models/resources/cubicmap_atlas.png@resources/cubicmap_atlas.png + models/models_first_person_maze: models/models_first_person_maze.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file models/resources/cubicmap.png@resources/cubicmap.png \ @@ -889,7 +893,7 @@ models/models_loading_vox: models/models_loading_vox.c models/models_loading_gltf: models/models_loading_gltf.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s TOTAL_MEMORY=67108864 \ --preload-file models/resources/models/gltf/robot.glb@resources/models/gltf/robot.glb - + models/models_loading_m3d: models/models_loading_m3d.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s TOTAL_MEMORY=67108864 \ --preload-file models/resources/models/m3d/cesium_man.m3d@resources/models/m3d/cesium_man.m3d @@ -1010,16 +1014,16 @@ shaders/shaders_texture_outline: shaders/shaders_texture_outline.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/shaders/glsl100/outline.fs@resources/shaders/glsl100/outline.fs \ --preload-file shaders/resources/fudesumi.png@resources/fudesumi.png - + shaders/shaders_write_depth: shaders/shaders_write_depth.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/shaders/glsl100/write_depth.fs@resources/shaders/glsl100/write_depth.fs - + shaders/shaders_hybrid_render: shaders/shaders_hybrid_render.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ --preload-file shaders/resources/shaders/glsl100/hybrid_raymarch.fs@resources/shaders/glsl100/hybrid_raymarch.fs \ --preload-file shaders/resources/shaders/glsl100/hybrid_raster.fs@resources/shaders/glsl100/hybrid_raster.fs - + # Compile AUDIO examples audio/audio_module_playing: audio/audio_module_playing.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) \ @@ -1040,7 +1044,7 @@ audio/audio_sound_loading: audio/audio_sound_loading.c audio/audio_stream_effects: audio/audio_stream_effects.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s TOTAL_MEMORY=67108864 \ --preload-file audio/resources/country.mp3@resources/country.mp3 - + audio/audio_mixed_processor: audio/audio_mixed_processor.c $(CC) -o $@$(EXT) $< $(CFLAGS) $(INCLUDE_PATHS) $(LDFLAGS) $(LDLIBS) -D$(PLATFORM) -s TOTAL_MEMORY=67108864 \ --preload-file audio/resources/country.mp3@resources/country.mp3 \ diff --git a/examples/core/core_input_gestures_web.c b/examples/core/core_input_gestures_web.c new file mode 100644 index 000000000..0ee3d1c5e --- /dev/null +++ b/examples/core/core_input_gestures_web.c @@ -0,0 +1,330 @@ +/******************************************************************************************* +* +* raylib [core] example - Input Gestures for Web +* +* Example originally created with raylib 4.6-dev, last time updated with raylib 4.6-dev +* +* Example contributed by ubkp (@ubkp) and reviewed by Ramon Santamaria (@raysan5) +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2023 ubkp (@ubkp) +* +********************************************************************************************/ + +#include "raylib.h" +#include "math.h" // Required for the protractor angle graphic drawing + +#if defined(PLATFORM_WEB) + #include // Required for the Web/HTML5 +#endif + +//-------------------------------------------------------------------------------------- +// Global definitions and declarations +//-------------------------------------------------------------------------------------- + +// Common variables definitions +//-------------------------------------------------------------------------------------- +int screenWidth = 800; +const int screenHeight = 450; +Vector2 messagePosition = {160, 7}; + +// Last gesture variables definitions +//-------------------------------------------------------------------------------------- +int lastGesture = 0; +Vector2 lastGesturePosition = {165, 130}; + +// Gesture log variables definitions and functions declarations +//-------------------------------------------------------------------------------------- +#define GESTURE_LOG_SIZE 20 +char gestureLog[GESTURE_LOG_SIZE][12] = { "" }; // The gesture log uses an array (as an inverted circular queue) to store the performed gestures +int gestureLogIndex = GESTURE_LOG_SIZE; // The index for the inverted circular queue (moving from last to first direction, then looping around) +int previousGesture = 0; +char const *GetGestureName(int i) +{ + switch (i) { + case 0: return "None"; break; + case 1: return "Tap"; break; + case 2: return "Double Tap"; break; + case 4: return "Hold"; break; + case 8: return "Drag"; break; + case 16: return "Swipe Right"; break; + case 32: return "Swipe Left"; break; + case 64: return "Swipe Up"; break; + case 128: return "Swipe Down"; break; + case 256: return "Pinch In"; break; + case 512: return "Pinch Out"; break; + default: return "Unknown"; break; + } +} +Color GetGestureColor(int i) +{ + switch (i) { + case 0: return BLACK; break; + case 1: return BLUE; break; + case 2: return SKYBLUE; break; + case 4: return BLACK; break; + case 8: return LIME; break; + case 16: return RED; break; + case 32: return RED; break; + case 64: return RED; break; + case 128: return RED; break; + case 256: return VIOLET; break; + case 512: return ORANGE; break; + default: return BLACK; break; + } +} +Color gestureColor = BLACK; +int logMode = 1; // Log mode values: 0 shows repeated events; 1 hides repeated events; 2 shows repeated events but hide hold events; 3 hides repeated events and hide hold events +Rectangle logButton1 = {53, 7, 48, 26}; +Rectangle logButton2 = {108, 7, 36, 26}; +Vector2 gestureLogPosition = {10, 10}; + +// Protractor variables definitions +//-------------------------------------------------------------------------------------- +float angleLength = 90.0f; +float currentAngleDegrees = 0.0f; +Vector2 finalVector = {0.0f, 0.0f}; +char currentAngleStr[7] = ""; +Vector2 protractorPosition = {266.0f, 315.0f}; + +// Update +//-------------------------------------------------------------------------------------- +void Update(void) +{ + // Handle common + //-------------------------------------------------------------------------------------- + int i, ii; // Iterators that will be reused by all for loops + const int currentGesture = GetGestureDetected(); + const float currentDragDegrees = GetGestureDragAngle(); + const float currentPitchDegrees = GetGesturePinchAngle(); + const int touchCount = GetTouchPointCount(); + + // Handle last gesture + //-------------------------------------------------------------------------------------- + if ( currentGesture != 0 && currentGesture != 4 && currentGesture != previousGesture ) lastGesture = currentGesture; // Filter the meaningful gestures (1, 2, 8 to 512) for the display + + // Handle gesture log + //-------------------------------------------------------------------------------------- + if ( IsMouseButtonReleased(MOUSE_BUTTON_LEFT) ) + { + if ( CheckCollisionPointRec(GetMousePosition(), logButton1 ) ) + { + switch (logMode) + { + case 3: logMode=2; break; + case 2: logMode=3; break; + case 1: logMode=0; break; + default: logMode=1; break; + } + } + else if ( CheckCollisionPointRec(GetMousePosition(), logButton2) ) + { + switch (logMode) + { + case 3: logMode=1; break; + case 2: logMode=0; break; + case 1: logMode=3; break; + default: logMode=2; break; + } + } + } + int fillLog = 0; // Gate variable to be used to allow or not the gesture log to be filled + if (currentGesture !=0) + { + if (logMode == 3) // 3 hides repeated events and hide hold events + { + if ( ( currentGesture != 4 && currentGesture != previousGesture ) || currentGesture < 3 ) fillLog = 1; + } + else if (logMode == 2) // 2 shows repeated events but hide hold events + { + if (currentGesture != 4) fillLog = 1; + } + else if (logMode == 1) // 1 hides repeated events + { + if (currentGesture != previousGesture) fillLog = 1; + } + else // 0 shows repeated events + { + fillLog = 1; + } + } + if (fillLog) // If one of the conditions from logMode was met, fill the gesture log + { + previousGesture = currentGesture; + gestureColor = GetGestureColor(currentGesture); + if (gestureLogIndex <= 0) gestureLogIndex = GESTURE_LOG_SIZE; + gestureLogIndex--; + TextCopy( gestureLog[gestureLogIndex], GetGestureName(currentGesture) ); // Copy the gesture respective name to the gesture log array + } + + // Handle protractor + //-------------------------------------------------------------------------------------- + if (currentGesture > 255) // aka Pinch In and Pinch Out + { + currentAngleDegrees = currentPitchDegrees; + } + else if (currentGesture > 15) // aka Swipe Right, Swipe Left, Swipe Up and Swipe Down + { + currentAngleDegrees = currentDragDegrees; + } + else if (currentGesture > 0) // aka Tap, Doubletap, Hold and Grab + { + currentAngleDegrees = 0.0f; + } + float currentAngleRadians = ( (currentAngleDegrees +90.0f)*PI/180 ); // Convert the current angle to Radians + finalVector = (Vector2){ ( angleLength*sinf(currentAngleRadians) ) + protractorPosition.x, ( angleLength*cosf(currentAngleRadians) ) + protractorPosition.y }; // Calculate the final vector for display + + // Handle touch and mouse pointer points + //-------------------------------------------------------------------------------------- + Vector2 touchPosition[touchCount]; + Vector2 mousePosition = {0, 0}; + if (currentGesture != GESTURE_NONE) + { + if (touchCount != 0) + { + for (i = 0; i < touchCount; i++) touchPosition[i] = GetTouchPosition(i); // Fill the touch positions + } + else + { + mousePosition = GetMousePosition(); + } + } + + // Draw + //-------------------------------------------------------------------------------------- + BeginDrawing(); + ClearBackground(RAYWHITE); + + // Draw common + //-------------------------------------------------------------------------------------- + DrawText("*", messagePosition.x + 5, messagePosition.y + 5, 10, BLACK); + DrawText("Example optimized for Web/HTML5\non Smartphones with Touch Screen.", messagePosition.x + 15, messagePosition.y + 5, 10, BLACK); + DrawText("*", messagePosition.x + 5, messagePosition.y + 35, 10, BLACK); + DrawText("While running on Desktop Web Browsers,\ninspect and turn on Touch Emulation.", messagePosition.x + 15, messagePosition.y + 35, 10, BLACK); + + // Draw last gesture + //-------------------------------------------------------------------------------------- + DrawText("Last gesture", lastGesturePosition.x + 33, lastGesturePosition.y - 47, 20, BLACK); + DrawText("Swipe Tap Pinch Touch", lastGesturePosition.x + 17, lastGesturePosition.y - 18, 10, BLACK); + DrawRectangle(lastGesturePosition.x + 20, lastGesturePosition.y, 20, 20, lastGesture == GESTURE_SWIPE_UP ? RED : LIGHTGRAY); + DrawRectangle(lastGesturePosition.x, lastGesturePosition.y + 20, 20, 20, lastGesture == GESTURE_SWIPE_LEFT ? RED : LIGHTGRAY); + DrawRectangle(lastGesturePosition.x + 40, lastGesturePosition.y + 20, 20, 20, lastGesture == GESTURE_SWIPE_RIGHT ? RED : LIGHTGRAY); + DrawRectangle(lastGesturePosition.x + 20, lastGesturePosition.y + 40, 20, 20, lastGesture == GESTURE_SWIPE_DOWN ? RED : LIGHTGRAY); + DrawCircle(lastGesturePosition.x + 80, lastGesturePosition.y + 16, 10, lastGesture == GESTURE_TAP ? BLUE : LIGHTGRAY); + DrawRing( (Vector2){lastGesturePosition.x + 103, lastGesturePosition.y + 16}, 6.0f, 11.0f, 0.0f, 360.0f, 0, lastGesture == GESTURE_DRAG ? LIME : LIGHTGRAY); + DrawCircle(lastGesturePosition.x + 80, lastGesturePosition.y + 43, 10, lastGesture == GESTURE_DOUBLETAP ? SKYBLUE : LIGHTGRAY); + DrawCircle(lastGesturePosition.x + 103, lastGesturePosition.y + 43, 10, lastGesture == GESTURE_DOUBLETAP ? SKYBLUE : LIGHTGRAY); + DrawTriangle( (Vector2){lastGesturePosition.x + 122, lastGesturePosition.y + 16}, (Vector2){lastGesturePosition.x + 137, lastGesturePosition.y + 26}, (Vector2){lastGesturePosition.x + 137, lastGesturePosition.y + 6}, lastGesture == GESTURE_PINCH_OUT ? ORANGE : LIGHTGRAY); + DrawTriangle( (Vector2){lastGesturePosition.x + 147, lastGesturePosition.y + 6}, (Vector2){lastGesturePosition.x + 147, lastGesturePosition.y + 26}, (Vector2){lastGesturePosition.x + 162, lastGesturePosition.y + 16}, lastGesture == GESTURE_PINCH_OUT ? ORANGE : LIGHTGRAY); + DrawTriangle( (Vector2){lastGesturePosition.x + 125, lastGesturePosition.y + 33}, (Vector2){lastGesturePosition.x + 125, lastGesturePosition.y + 53}, (Vector2){lastGesturePosition.x + 140, lastGesturePosition.y + 43}, lastGesture == GESTURE_PINCH_IN ? VIOLET : LIGHTGRAY); + DrawTriangle( (Vector2){lastGesturePosition.x + 144, lastGesturePosition.y + 43}, (Vector2){lastGesturePosition.x + 159, lastGesturePosition.y + 53}, (Vector2){lastGesturePosition.x + 159, lastGesturePosition.y + 33}, lastGesture == GESTURE_PINCH_IN ? VIOLET : LIGHTGRAY); + for ( i = 0; i < 4; i++ ) DrawCircle(lastGesturePosition.x + 180, lastGesturePosition.y + 7 + i*15, 5, touchCount <= i ? LIGHTGRAY : gestureColor); + + // Draw gesture log + //-------------------------------------------------------------------------------------- + DrawText("Log", gestureLogPosition.x, gestureLogPosition.y, 20, BLACK); + // Loop in both directions to print the gesture log array in the inverted order (and looping around if the index started somewhere in the middle) + for (i = 0, ii = gestureLogIndex; i < GESTURE_LOG_SIZE; i++, ii = (ii + 1) % GESTURE_LOG_SIZE) DrawText(gestureLog[ii], gestureLogPosition.x, gestureLogPosition.y + 410 - i*20, 20, (i == 0 ? gestureColor : LIGHTGRAY)); + Color logButton1Color, logButton2Color; + switch (logMode) + { + case 3: logButton1Color=MAROON; logButton2Color=MAROON; break; + case 2: logButton1Color=GRAY; logButton2Color=MAROON; break; + case 1: logButton1Color=MAROON; logButton2Color=GRAY; break; + default: logButton1Color=GRAY; logButton2Color=GRAY; break; + } + DrawRectangleRec( logButton1, logButton1Color); + DrawText("Hide", logButton1.x + 7, logButton1.y + 3, 10, WHITE); + DrawText("Repeat", logButton1.x + 7, logButton1.y + 13, 10, WHITE); + DrawRectangleRec( logButton2, logButton2Color); + DrawText("Hide", logButton1.x + 62, logButton1.y + 3, 10, WHITE); + DrawText("Hold", logButton1.x + 62, logButton1.y + 13, 10, WHITE); + + // Draw protractor + //-------------------------------------------------------------------------------------- + DrawText("Angle", protractorPosition.x + 55, protractorPosition.y + 76, 10, BLACK); + const char *angleString = TextFormat("%f", currentAngleDegrees); + const int angleStringDot = TextFindIndex(angleString, "."); + const char *angleStringTrim = TextSubtext(angleString, 0, angleStringDot + 3); + DrawText( angleStringTrim, protractorPosition.x + 55, protractorPosition.y + 92, 20, gestureColor); + DrawCircle(protractorPosition.x, protractorPosition.y, 80.0f, WHITE); + DrawLineEx( (Vector2){protractorPosition.x - 90, protractorPosition.y}, (Vector2){protractorPosition.x + 90, protractorPosition.y}, 3.0f, LIGHTGRAY); + DrawLineEx( (Vector2){protractorPosition.x, protractorPosition.y - 90}, (Vector2){protractorPosition.x, protractorPosition.y + 90}, 3.0f, LIGHTGRAY); + DrawLineEx( (Vector2){protractorPosition.x - 80, protractorPosition.y - 45}, (Vector2){protractorPosition.x + 80, protractorPosition.y + 45}, 3.0f, GREEN); + DrawLineEx( (Vector2){protractorPosition.x - 80, protractorPosition.y + 45}, (Vector2){protractorPosition.x + 80, protractorPosition.y - 45}, 3.0f, GREEN); + DrawText("0", protractorPosition.x + 96, protractorPosition.y - 9, 20, BLACK); + DrawText("30", protractorPosition.x + 74, protractorPosition.y - 68, 20, BLACK); + DrawText("90", protractorPosition.x - 11, protractorPosition.y - 110, 20, BLACK); + DrawText("150", protractorPosition.x - 100, protractorPosition.y - 68, 20, BLACK); + DrawText("180", protractorPosition.x - 124, protractorPosition.y - 9, 20, BLACK); + DrawText("210", protractorPosition.x - 100, protractorPosition.y + 50, 20, BLACK); + DrawText("270", protractorPosition.x - 18, protractorPosition.y + 92, 20, BLACK); + DrawText("330", protractorPosition.x + 72, protractorPosition.y + 50, 20, BLACK); + if ( currentAngleDegrees != 0.0f ) DrawLineEx( protractorPosition, finalVector, 3.0f, gestureColor); + + // Draw touch and mouse pointer points + //-------------------------------------------------------------------------------------- + if (currentGesture != GESTURE_NONE) + { + if ( touchCount != 0 ) + { + for (i = 0; i < touchCount; i++) + { + DrawCircleV(touchPosition[i], 50.0f, Fade(gestureColor, 0.5f)); + DrawCircleV(touchPosition[i], 5.0f, gestureColor); + } + if (touchCount == 2) DrawLineEx( touchPosition[0], touchPosition[1], (currentGesture == 512 ? 8 : 12), gestureColor); + } + else + { + DrawCircleV(mousePosition, 35.0f, Fade(gestureColor, 0.5f)); + DrawCircleV(mousePosition, 5.0f, gestureColor); + } + } + + EndDrawing(); + //-------------------------------------------------------------------------------------- + +} + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + #if defined( PLATFORM_WEB ) + const int canvasWidth = EM_ASM_INT( return document.getElementById('canvas').getBoundingClientRect().width; ); // Using Emscripten EM_ASM_INT macro, get the page canvas width + if (canvasWidth > 400) + { + screenWidth = canvasWidth; + } + else + { + screenWidth = 400; // Set a minimum width for the screen + } + #endif + + InitWindow(screenWidth, screenHeight, "raylib [core] example - input gestures web"); + //-------------------------------------------------------------------------------------- + + // Main game loop + //-------------------------------------------------------------------------------------- + #if defined(PLATFORM_WEB) + emscripten_set_main_loop(Update, 0, 1); + #else + SetTargetFPS(60); + while (!WindowShouldClose()) Update(); // Detect window close button or ESC key + #endif + //-------------------------------------------------------------------------------------- + + // De-Initialization + //-------------------------------------------------------------------------------------- + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} diff --git a/examples/core/core_input_gestures_web.png b/examples/core/core_input_gestures_web.png new file mode 100644 index 0000000000000000000000000000000000000000..dd604e84b9c831d32229a8bdefc81d4ae161189a GIT binary patch literal 8937 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 literal 0 HcmV?d00001 diff --git a/src/rcore.c b/src/rcore.c index 6fd7a4fe1..45495bb15 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -5593,8 +5593,14 @@ static void MouseButtonCallback(GLFWwindow *window, int button, int action, int gestureEvent.position[0].y /= (float)GetScreenHeight(); // Gesture data is sent to gestures-system for processing +#if defined(PLATFORM_WEB) + // Prevent calling ProcessGestureEvent() when Emscripten is present and there's a touch gesture, so EmscriptenTouchCallback() can handle it itself + if (GetMouseX() != 0 || GetMouseY() != 0) ProcessGestureEvent(gestureEvent); +#else ProcessGestureEvent(gestureEvent); #endif + +#endif } // GLFW3 Cursor Position Callback, runs on mouse move diff --git a/src/rgestures.h b/src/rgestures.h index 749f440aa..78dde76ee 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -127,7 +127,7 @@ void SetGesturesEnabled(unsigned int flags); // Enable a set of gestu bool IsGestureDetected(int gesture); // Check if a gesture have been detected int GetGestureDetected(void); // Get latest detected gesture -float GetGestureHoldDuration(void); // Get gesture hold time in milliseconds +float GetGestureHoldDuration(void); // Get gesture hold time in seconds Vector2 GetGestureDragVector(void); // Get gesture drag vector float GetGestureDragAngle(void); // Get gesture drag angle Vector2 GetGesturePinchVector(void); // Get gesture pinch delta @@ -181,8 +181,8 @@ float GetGesturePinchAngle(void); // Get gesture pinch ang #define FORCE_TO_SWIPE 0.0005f // Swipe force, measured in normalized screen units/time #define MINIMUM_DRAG 0.015f // Drag minimum force, measured in normalized screen units (0.0f to 1.0f) #define MINIMUM_PINCH 0.005f // Pinch minimum force, measured in normalized screen units (0.0f to 1.0f) -#define TAP_TIMEOUT 300 // Tap minimum time, measured in milliseconds -#define PINCH_TIMEOUT 300 // Pinch minimum time, measured in milliseconds +#define TAP_TIMEOUT 0.3f // Tap minimum time, measured in seconds +#define PINCH_TIMEOUT 0.3f // Pinch minimum time, measured in seconds #define DOUBLETAP_RANGE 0.03f // DoubleTap range, measured in normalized screen units (0.0f to 1.0f) //---------------------------------------------------------------------------------- @@ -209,7 +209,7 @@ typedef struct { } Touch; struct { bool resetRequired; // HOLD reset to get first touch point again - double timeDuration; // HOLD duration in milliseconds + double timeDuration; // HOLD duration in seconds } Hold; struct { Vector2 vector; // DRAG vector (between initial and current position) @@ -522,7 +522,7 @@ static float rgVector2Distance(Vector2 v1, Vector2 v2) return result; } -// Time measure returned are milliseconds +// Time measure returned are seconds static double rgGetCurrentTime(void) { double time = 0; @@ -536,7 +536,7 @@ static double rgGetCurrentTime(void) QueryPerformanceFrequency(&clockFrequency); // BE CAREFUL: Costly operation! QueryPerformanceCounter(¤tTime); - time = (double)currentTime/clockFrequency*1000.0f; // Time in miliseconds + time = (double)currentTime/clockFrequency; // Time in seconds #endif #if defined(__linux__) @@ -545,7 +545,7 @@ static double rgGetCurrentTime(void) clock_gettime(CLOCK_MONOTONIC, &now); unsigned long long int nowTime = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec; // Time in nanoseconds - time = ((double)nowTime/1000000.0); // Time in miliseconds + time = ((double)nowTime*1e-9); // Time in seconds #endif #if defined(__APPLE__) @@ -561,7 +561,7 @@ static double rgGetCurrentTime(void) mach_port_deallocate(mach_task_self(), cclock); unsigned long long int nowTime = (unsigned long long int)now.tv_sec*1000000000LLU + (unsigned long long int)now.tv_nsec; // Time in nanoseconds - time = ((double)nowTime/1000000.0); // Time in miliseconds + time = ((double)nowTime*1e-9); // Time in seconds #endif #endif From 70286c7cdc6d972c63704ad957c18065f6a44cfe Mon Sep 17 00:00:00 2001 From: Michael Anghelone Date: Sun, 16 Jul 2023 07:08:55 -0400 Subject: [PATCH 0155/1350] Makefile change for cross compiling. (#3176) Working from wsl and compiling for windows this change makes it much easier to compile a static library for windows on arm. To compile a static library for windows on arm: ``` make PLATFORM=PLATFORM_DESKTOP TARGET_OS=WINDOWS CROSS_CC=/llvm/bin/aarch64-w64-mingw32-gcc CROSS_AR=/llvm/bin/aarch64-w64-mingw32-ar ``` This does not work to compile a shared library yet, only static. --- src/Makefile | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Makefile b/src/Makefile index fc4c5b0c7..30ac232f8 100644 --- a/src/Makefile +++ b/src/Makefile @@ -308,6 +308,17 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID) AR = $(ANDROID_TOOLCHAIN)/bin/llvm-ar endif +# This section is here to help handle cross compilation. eg build on Linux for Windows. If you are doing this, you better know what you are doing! +ifdef TARGET_OS + ifdef CROSS_CC + CC = ${CROSS_CC} + endif + ifdef CROSS_AR + AR = ${CROSS_AR} + endif + PLATFORM_OS = ${TARGET_OS} +endif + # Define compiler flags: CFLAGS #------------------------------------------------------------------------------------------------ # -O1 defines optimization level From 86f95d71502bb6099dca0490aa8216b1684acf0f Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 16 Jul 2023 13:24:49 +0200 Subject: [PATCH 0156/1350] Reviewed C compilation issues and formatting --- examples/core/core_input_gestures_web.c | 124 +++++++++++++----------- 1 file changed, 66 insertions(+), 58 deletions(-) diff --git a/examples/core/core_input_gestures_web.c b/examples/core/core_input_gestures_web.c index 0ee3d1c5e..0129a983b 100644 --- a/examples/core/core_input_gestures_web.c +++ b/examples/core/core_input_gestures_web.c @@ -14,7 +14,8 @@ ********************************************************************************************/ #include "raylib.h" -#include "math.h" // Required for the protractor angle graphic drawing + +#include "math.h" // Required for the protractor angle graphic drawing #if defined(PLATFORM_WEB) #include // Required for the Web/HTML5 @@ -26,14 +27,14 @@ // Common variables definitions //-------------------------------------------------------------------------------------- -int screenWidth = 800; +int screenWidth = 800; // Update depending on web canvas const int screenHeight = 450; -Vector2 messagePosition = {160, 7}; +Vector2 messagePosition = { 160, 7 }; // Last gesture variables definitions //-------------------------------------------------------------------------------------- int lastGesture = 0; -Vector2 lastGesturePosition = {165, 130}; +Vector2 lastGesturePosition = { 165, 130 }; // Gesture log variables definitions and functions declarations //-------------------------------------------------------------------------------------- @@ -41,6 +42,7 @@ Vector2 lastGesturePosition = {165, 130}; char gestureLog[GESTURE_LOG_SIZE][12] = { "" }; // The gesture log uses an array (as an inverted circular queue) to store the performed gestures int gestureLogIndex = GESTURE_LOG_SIZE; // The index for the inverted circular queue (moving from last to first direction, then looping around) int previousGesture = 0; + char const *GetGestureName(int i) { switch (i) { @@ -58,6 +60,7 @@ char const *GetGestureName(int i) default: return "Unknown"; break; } } + Color GetGestureColor(int i) { switch (i) { @@ -75,19 +78,21 @@ Color GetGestureColor(int i) default: return BLACK; break; } } -Color gestureColor = BLACK; + int logMode = 1; // Log mode values: 0 shows repeated events; 1 hides repeated events; 2 shows repeated events but hide hold events; 3 hides repeated events and hide hold events -Rectangle logButton1 = {53, 7, 48, 26}; -Rectangle logButton2 = {108, 7, 36, 26}; -Vector2 gestureLogPosition = {10, 10}; + +Color gestureColor = { 0, 0, 0, 255 }; +Rectangle logButton1 = { 53, 7, 48, 26 }; +Rectangle logButton2 = { 108, 7, 36, 26 }; +Vector2 gestureLogPosition = { 10, 10 }; // Protractor variables definitions //-------------------------------------------------------------------------------------- float angleLength = 90.0f; float currentAngleDegrees = 0.0f; -Vector2 finalVector = {0.0f, 0.0f}; +Vector2 finalVector = { 0.0f, 0.0f }; char currentAngleStr[7] = ""; -Vector2 protractorPosition = {266.0f, 315.0f}; +Vector2 protractorPosition = { 266.0f, 315.0f }; // Update //-------------------------------------------------------------------------------------- @@ -103,39 +108,40 @@ void Update(void) // Handle last gesture //-------------------------------------------------------------------------------------- - if ( currentGesture != 0 && currentGesture != 4 && currentGesture != previousGesture ) lastGesture = currentGesture; // Filter the meaningful gestures (1, 2, 8 to 512) for the display + if ((currentGesture != 0) && (currentGesture != 4) && (currentGesture != previousGesture)) lastGesture = currentGesture; // Filter the meaningful gestures (1, 2, 8 to 512) for the display // Handle gesture log //-------------------------------------------------------------------------------------- - if ( IsMouseButtonReleased(MOUSE_BUTTON_LEFT) ) + if (IsMouseButtonReleased(MOUSE_BUTTON_LEFT)) { - if ( CheckCollisionPointRec(GetMousePosition(), logButton1 ) ) + if (CheckCollisionPointRec(GetMousePosition(), logButton1)) { switch (logMode) { - case 3: logMode=2; break; - case 2: logMode=3; break; - case 1: logMode=0; break; - default: logMode=1; break; + case 3: logMode=2; break; + case 2: logMode=3; break; + case 1: logMode=0; break; + default: logMode=1; break; } } - else if ( CheckCollisionPointRec(GetMousePosition(), logButton2) ) + else if (CheckCollisionPointRec(GetMousePosition(), logButton2)) { switch (logMode) { - case 3: logMode=1; break; - case 2: logMode=0; break; - case 1: logMode=3; break; - default: logMode=2; break; + case 3: logMode=1; break; + case 2: logMode=0; break; + case 1: logMode=3; break; + default: logMode=2; break; } } } + int fillLog = 0; // Gate variable to be used to allow or not the gesture log to be filled if (currentGesture !=0) { if (logMode == 3) // 3 hides repeated events and hide hold events { - if ( ( currentGesture != 4 && currentGesture != previousGesture ) || currentGesture < 3 ) fillLog = 1; + if (((currentGesture != 4) && (currentGesture != previousGesture)) || (currentGesture < 3)) fillLog = 1; } else if (logMode == 2) // 2 shows repeated events but hide hold events { @@ -150,13 +156,16 @@ void Update(void) fillLog = 1; } } + if (fillLog) // If one of the conditions from logMode was met, fill the gesture log { previousGesture = currentGesture; gestureColor = GetGestureColor(currentGesture); if (gestureLogIndex <= 0) gestureLogIndex = GESTURE_LOG_SIZE; gestureLogIndex--; - TextCopy( gestureLog[gestureLogIndex], GetGestureName(currentGesture) ); // Copy the gesture respective name to the gesture log array + + // Copy the gesture respective name to the gesture log array + TextCopy(gestureLog[gestureLogIndex], GetGestureName(currentGesture)); } // Handle protractor @@ -173,12 +182,15 @@ void Update(void) { currentAngleDegrees = 0.0f; } - float currentAngleRadians = ( (currentAngleDegrees +90.0f)*PI/180 ); // Convert the current angle to Radians - finalVector = (Vector2){ ( angleLength*sinf(currentAngleRadians) ) + protractorPosition.x, ( angleLength*cosf(currentAngleRadians) ) + protractorPosition.y }; // Calculate the final vector for display + + float currentAngleRadians = ((currentAngleDegrees +90.0f)*PI/180); // Convert the current angle to Radians + finalVector = (Vector2){ (angleLength*sinf(currentAngleRadians)) + protractorPosition.x, (angleLength*cosf(currentAngleRadians)) + protractorPosition.y }; // Calculate the final vector for display // Handle touch and mouse pointer points //-------------------------------------------------------------------------------------- - Vector2 touchPosition[touchCount]; + #define MAX_TOUCH_COUNT 32 + + Vector2 touchPosition[MAX_TOUCH_COUNT] = { 0 }; Vector2 mousePosition = {0, 0}; if (currentGesture != GESTURE_NONE) { @@ -186,16 +198,14 @@ void Update(void) { for (i = 0; i < touchCount; i++) touchPosition[i] = GetTouchPosition(i); // Fill the touch positions } - else - { - mousePosition = GetMousePosition(); - } + else mousePosition = GetMousePosition(); } // Draw //-------------------------------------------------------------------------------------- BeginDrawing(); - ClearBackground(RAYWHITE); + + ClearBackground(RAYWHITE); // Draw common //-------------------------------------------------------------------------------------- @@ -216,29 +226,30 @@ void Update(void) DrawRing( (Vector2){lastGesturePosition.x + 103, lastGesturePosition.y + 16}, 6.0f, 11.0f, 0.0f, 360.0f, 0, lastGesture == GESTURE_DRAG ? LIME : LIGHTGRAY); DrawCircle(lastGesturePosition.x + 80, lastGesturePosition.y + 43, 10, lastGesture == GESTURE_DOUBLETAP ? SKYBLUE : LIGHTGRAY); DrawCircle(lastGesturePosition.x + 103, lastGesturePosition.y + 43, 10, lastGesture == GESTURE_DOUBLETAP ? SKYBLUE : LIGHTGRAY); - DrawTriangle( (Vector2){lastGesturePosition.x + 122, lastGesturePosition.y + 16}, (Vector2){lastGesturePosition.x + 137, lastGesturePosition.y + 26}, (Vector2){lastGesturePosition.x + 137, lastGesturePosition.y + 6}, lastGesture == GESTURE_PINCH_OUT ? ORANGE : LIGHTGRAY); - DrawTriangle( (Vector2){lastGesturePosition.x + 147, lastGesturePosition.y + 6}, (Vector2){lastGesturePosition.x + 147, lastGesturePosition.y + 26}, (Vector2){lastGesturePosition.x + 162, lastGesturePosition.y + 16}, lastGesture == GESTURE_PINCH_OUT ? ORANGE : LIGHTGRAY); - DrawTriangle( (Vector2){lastGesturePosition.x + 125, lastGesturePosition.y + 33}, (Vector2){lastGesturePosition.x + 125, lastGesturePosition.y + 53}, (Vector2){lastGesturePosition.x + 140, lastGesturePosition.y + 43}, lastGesture == GESTURE_PINCH_IN ? VIOLET : LIGHTGRAY); - DrawTriangle( (Vector2){lastGesturePosition.x + 144, lastGesturePosition.y + 43}, (Vector2){lastGesturePosition.x + 159, lastGesturePosition.y + 53}, (Vector2){lastGesturePosition.x + 159, lastGesturePosition.y + 33}, lastGesture == GESTURE_PINCH_IN ? VIOLET : LIGHTGRAY); - for ( i = 0; i < 4; i++ ) DrawCircle(lastGesturePosition.x + 180, lastGesturePosition.y + 7 + i*15, 5, touchCount <= i ? LIGHTGRAY : gestureColor); + DrawTriangle((Vector2){ lastGesturePosition.x + 122, lastGesturePosition.y + 16 }, (Vector2){ lastGesturePosition.x + 137, lastGesturePosition.y + 26 }, (Vector2){ lastGesturePosition.x + 137, lastGesturePosition.y + 6 }, lastGesture == GESTURE_PINCH_OUT? ORANGE : LIGHTGRAY); + DrawTriangle((Vector2){ lastGesturePosition.x + 147, lastGesturePosition.y + 6 }, (Vector2){ lastGesturePosition.x + 147, lastGesturePosition.y + 26 }, (Vector2){ lastGesturePosition.x + 162, lastGesturePosition.y + 16 }, lastGesture == GESTURE_PINCH_OUT? ORANGE : LIGHTGRAY); + DrawTriangle((Vector2){ lastGesturePosition.x + 125, lastGesturePosition.y + 33 }, (Vector2){ lastGesturePosition.x + 125, lastGesturePosition.y + 53 }, (Vector2){ lastGesturePosition.x + 140, lastGesturePosition.y + 43 }, lastGesture == GESTURE_PINCH_IN? VIOLET : LIGHTGRAY); + DrawTriangle((Vector2){ lastGesturePosition.x + 144, lastGesturePosition.y + 43 }, (Vector2){ lastGesturePosition.x + 159, lastGesturePosition.y + 53 }, (Vector2){ lastGesturePosition.x + 159, lastGesturePosition.y + 33 }, lastGesture == GESTURE_PINCH_IN? VIOLET : LIGHTGRAY); + for (i = 0; i < 4; i++) DrawCircle(lastGesturePosition.x + 180, lastGesturePosition.y + 7 + i*15, 5, touchCount <= i? LIGHTGRAY : gestureColor); // Draw gesture log //-------------------------------------------------------------------------------------- DrawText("Log", gestureLogPosition.x, gestureLogPosition.y, 20, BLACK); + // Loop in both directions to print the gesture log array in the inverted order (and looping around if the index started somewhere in the middle) for (i = 0, ii = gestureLogIndex; i < GESTURE_LOG_SIZE; i++, ii = (ii + 1) % GESTURE_LOG_SIZE) DrawText(gestureLog[ii], gestureLogPosition.x, gestureLogPosition.y + 410 - i*20, 20, (i == 0 ? gestureColor : LIGHTGRAY)); Color logButton1Color, logButton2Color; switch (logMode) { - case 3: logButton1Color=MAROON; logButton2Color=MAROON; break; - case 2: logButton1Color=GRAY; logButton2Color=MAROON; break; - case 1: logButton1Color=MAROON; logButton2Color=GRAY; break; - default: logButton1Color=GRAY; logButton2Color=GRAY; break; + case 3: logButton1Color=MAROON; logButton2Color=MAROON; break; + case 2: logButton1Color=GRAY; logButton2Color=MAROON; break; + case 1: logButton1Color=MAROON; logButton2Color=GRAY; break; + default: logButton1Color=GRAY; logButton2Color=GRAY; break; } - DrawRectangleRec( logButton1, logButton1Color); + DrawRectangleRec(logButton1, logButton1Color); DrawText("Hide", logButton1.x + 7, logButton1.y + 3, 10, WHITE); DrawText("Repeat", logButton1.x + 7, logButton1.y + 13, 10, WHITE); - DrawRectangleRec( logButton2, logButton2Color); + DrawRectangleRec(logButton2, logButton2Color); DrawText("Hide", logButton1.x + 62, logButton1.y + 3, 10, WHITE); DrawText("Hold", logButton1.x + 62, logButton1.y + 13, 10, WHITE); @@ -250,10 +261,10 @@ void Update(void) const char *angleStringTrim = TextSubtext(angleString, 0, angleStringDot + 3); DrawText( angleStringTrim, protractorPosition.x + 55, protractorPosition.y + 92, 20, gestureColor); DrawCircle(protractorPosition.x, protractorPosition.y, 80.0f, WHITE); - DrawLineEx( (Vector2){protractorPosition.x - 90, protractorPosition.y}, (Vector2){protractorPosition.x + 90, protractorPosition.y}, 3.0f, LIGHTGRAY); - DrawLineEx( (Vector2){protractorPosition.x, protractorPosition.y - 90}, (Vector2){protractorPosition.x, protractorPosition.y + 90}, 3.0f, LIGHTGRAY); - DrawLineEx( (Vector2){protractorPosition.x - 80, protractorPosition.y - 45}, (Vector2){protractorPosition.x + 80, protractorPosition.y + 45}, 3.0f, GREEN); - DrawLineEx( (Vector2){protractorPosition.x - 80, protractorPosition.y + 45}, (Vector2){protractorPosition.x + 80, protractorPosition.y - 45}, 3.0f, GREEN); + DrawLineEx((Vector2){ protractorPosition.x - 90, protractorPosition.y }, (Vector2){ protractorPosition.x + 90, protractorPosition.y }, 3.0f, LIGHTGRAY); + DrawLineEx((Vector2){ protractorPosition.x, protractorPosition.y - 90 }, (Vector2){ protractorPosition.x, protractorPosition.y + 90 }, 3.0f, LIGHTGRAY); + DrawLineEx((Vector2){ protractorPosition.x - 80, protractorPosition.y - 45 }, (Vector2){ protractorPosition.x + 80, protractorPosition.y + 45 }, 3.0f, GREEN); + DrawLineEx((Vector2){ protractorPosition.x - 80, protractorPosition.y + 45 }, (Vector2){ protractorPosition.x + 80, protractorPosition.y - 45 }, 3.0f, GREEN); DrawText("0", protractorPosition.x + 96, protractorPosition.y - 9, 20, BLACK); DrawText("30", protractorPosition.x + 74, protractorPosition.y - 68, 20, BLACK); DrawText("90", protractorPosition.x - 11, protractorPosition.y - 110, 20, BLACK); @@ -262,7 +273,7 @@ void Update(void) DrawText("210", protractorPosition.x - 100, protractorPosition.y + 50, 20, BLACK); DrawText("270", protractorPosition.x - 18, protractorPosition.y + 92, 20, BLACK); DrawText("330", protractorPosition.x + 72, protractorPosition.y + 50, 20, BLACK); - if ( currentAngleDegrees != 0.0f ) DrawLineEx( protractorPosition, finalVector, 3.0f, gestureColor); + if (currentAngleDegrees != 0.0f) DrawLineEx(protractorPosition, finalVector, 3.0f, gestureColor); // Draw touch and mouse pointer points //-------------------------------------------------------------------------------------- @@ -275,7 +286,8 @@ void Update(void) DrawCircleV(touchPosition[i], 50.0f, Fade(gestureColor, 0.5f)); DrawCircleV(touchPosition[i], 5.0f, gestureColor); } - if (touchCount == 2) DrawLineEx( touchPosition[0], touchPosition[1], (currentGesture == 512 ? 8 : 12), gestureColor); + + if (touchCount == 2) DrawLineEx(touchPosition[0], touchPosition[1], ((currentGesture == 512)? 8 : 12), gestureColor); } else { @@ -297,15 +309,11 @@ int main(void) // Initialization //-------------------------------------------------------------------------------------- #if defined( PLATFORM_WEB ) - const int canvasWidth = EM_ASM_INT( return document.getElementById('canvas').getBoundingClientRect().width; ); // Using Emscripten EM_ASM_INT macro, get the page canvas width - if (canvasWidth > 400) - { - screenWidth = canvasWidth; - } - else - { - screenWidth = 400; // Set a minimum width for the screen - } + // Using Emscripten EM_ASM_INT macro, get the page canvas width + const int canvasWidth = EM_ASM_INT( return document.getElementById('canvas').getBoundingClientRect().width; ); + + if (canvasWidth > 400) screenWidth = canvasWidth; + else screenWidth = 400; // Set a minimum width for the screen #endif InitWindow(screenWidth, screenHeight, "raylib [core] example - input gestures web"); From e0c80f5ddd9543a6f086f3871f886e275c331a53 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 16 Jul 2023 20:18:38 +0200 Subject: [PATCH 0157/1350] Revert "Makefile change for cross compiling. (#3176)" This reverts commit 70286c7cdc6d972c63704ad957c18065f6a44cfe. --- src/Makefile | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/Makefile b/src/Makefile index 30ac232f8..fc4c5b0c7 100644 --- a/src/Makefile +++ b/src/Makefile @@ -308,17 +308,6 @@ ifeq ($(PLATFORM),PLATFORM_ANDROID) AR = $(ANDROID_TOOLCHAIN)/bin/llvm-ar endif -# This section is here to help handle cross compilation. eg build on Linux for Windows. If you are doing this, you better know what you are doing! -ifdef TARGET_OS - ifdef CROSS_CC - CC = ${CROSS_CC} - endif - ifdef CROSS_AR - AR = ${CROSS_AR} - endif - PLATFORM_OS = ${TARGET_OS} -endif - # Define compiler flags: CFLAGS #------------------------------------------------------------------------------------------------ # -O1 defines optimization level From 52541b4a1fb04d66de30c2a1e5f44358b2c1aee7 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 18 Jul 2023 17:57:10 +0200 Subject: [PATCH 0158/1350] ADDED: `SUPPORT_FONT_ATLAS_WHITE_REC` Support creating a 3x3 pixels white rectangle at the bottom-right corner of the generated font atlas image, useful for shapes+text drawing in a single draw call! --- CMakeOptions.txt | 1 + src/config.h | 5 +++++ src/rtext.c | 27 ++++++++++++++++++++++----- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/CMakeOptions.txt b/CMakeOptions.txt index 326ed44e6..a239f1143 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -73,6 +73,7 @@ cmake_dependent_option(SUPPORT_FILEFORMAT_PVR "Support loading PVR as textures" cmake_dependent_option(SUPPORT_FILEFORMAT_FNT "Support loading fonts in FNT format" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_TTF "Support loading font in TTF/OTF format" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_TEXT_MANIPULATION "Support text manipulation functions" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(#define SUPPORT_FONT_ATLAS_WHITE_REC "Support white rec on font atlas bottom-right corner" ON CUSTOMIZE_BUILD ON) # rmodels.c cmake_dependent_option(SUPPORT_MESH_GENERATION "Support procedural mesh generation functions, uses external par_shapes.h library. NOTE: Some generated meshes DO NOT include generated texture coordinates" ON CUSTOMIZE_BUILD ON) diff --git a/src/config.h b/src/config.h index fbc7a5b47..67c6060b8 100644 --- a/src/config.h +++ b/src/config.h @@ -181,6 +181,11 @@ // If not defined, still some functions are supported: TextLength(), TextFormat() #define SUPPORT_TEXT_MANIPULATION 1 +// On font atlas image generation [GenImageFontAtlas()], add a 3x3 pixels white rectangle +// at the bottom-right corner of the atlas. It can be useful to for shapes drawing, to allow +// drawing text and shapes with a single draw call [SetShapesTexture()]. +#define SUPPORT_FONT_ATLAS_WHITE_REC 1 + // rtext: Configuration values //------------------------------------------------------------------------------------ #define MAX_TEXT_BUFFER_LENGTH 1024 // Size of internal static buffers used on some functions: diff --git a/src/rtext.c b/src/rtext.c index 3b21a93c9..a7d9903a8 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -6,14 +6,19 @@ * #define SUPPORT_MODULE_RTEXT * rtext module is included in the build * +* #define SUPPORT_DEFAULT_FONT +* Load default raylib font on initialization to be used by DrawText() and MeasureText(). +* If no default font loaded, DrawTextEx() and MeasureTextEx() are required. +* * #define SUPPORT_FILEFORMAT_FNT * #define SUPPORT_FILEFORMAT_TTF * Selected desired fileformats to be supported for loading. Some of those formats are * supported by default, to remove support, just comment unrequired #define in this module * -* #define SUPPORT_DEFAULT_FONT -* Load default raylib font on initialization to be used by DrawText() and MeasureText(). -* If no default font loaded, DrawTextEx() and MeasureTextEx() are required. +* #define SUPPORT_FONT_ATLAS_WHITE_REC +* On font atlas image generation [GenImageFontAtlas()], add a 3x3 pixels white rectangle +* at the bottom-right corner of the atlas. It can be useful to for shapes drawing, to allow +* drawing text and shapes with a single draw call [SetShapesTexture()]. * * #define TEXTSPLIT_MAX_TEXT_BUFFER_LENGTH * TextSplit() function static buffer max size @@ -734,7 +739,6 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC } #endif - atlas.data = (unsigned char *)RL_CALLOC(1, atlas.width*atlas.height); // Create a bitmap to store characters (8 bpp) atlas.format = PIXELFORMAT_UNCOMPRESSED_GRAYSCALE; atlas.mipmaps = 1; @@ -839,7 +843,20 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC RL_FREE(nodes); RL_FREE(context); } - + +#if defined(SUPPORT_FONT_ATLAS_WHITE_REC) + // Add a 3x3 white rectangle at the bottom-right corner of the generated atlas, + // useful to use as the white texture to draw shapes with raylib, using this rectangle + // shapes and text can be backed into a single draw call: SetShapesTexture() + for (int i = 0, k = atlas.width*atlas.height - 1; i < 3; i++) + { + ((unsigned char *)atlas.data)[k - 0] = 255; + ((unsigned char *)atlas.data)[k - 1] = 255; + ((unsigned char *)atlas.data)[k - 2] = 255; + k -= atlas.width; + } +#endif + // Convert image data from GRAYSCALE to GRAY_ALPHA unsigned char *dataGrayAlpha = (unsigned char *)RL_MALLOC(atlas.width*atlas.height*sizeof(unsigned char)*2); // Two channels From a9ff13a367b7bd166436b12b741b8c8199f1c85f Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 18 Jul 2023 18:07:49 +0200 Subject: [PATCH 0159/1350] Update CMakeOptions.txt --- CMakeOptions.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeOptions.txt b/CMakeOptions.txt index a239f1143..c643427c6 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -73,7 +73,7 @@ cmake_dependent_option(SUPPORT_FILEFORMAT_PVR "Support loading PVR as textures" cmake_dependent_option(SUPPORT_FILEFORMAT_FNT "Support loading fonts in FNT format" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_TTF "Support loading font in TTF/OTF format" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_TEXT_MANIPULATION "Support text manipulation functions" ON CUSTOMIZE_BUILD ON) -cmake_dependent_option(#define SUPPORT_FONT_ATLAS_WHITE_REC "Support white rec on font atlas bottom-right corner" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FONT_ATLAS_WHITE_REC "Support white rec on font atlas bottom-right corner" ON CUSTOMIZE_BUILD ON) # rmodels.c cmake_dependent_option(SUPPORT_MESH_GENERATION "Support procedural mesh generation functions, uses external par_shapes.h library. NOTE: Some generated meshes DO NOT include generated texture coordinates" ON CUSTOMIZE_BUILD ON) From 954c60100f2411dcfb6b57a3a39e96c71e4d2e52 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 19 Jul 2023 06:33:10 -0300 Subject: [PATCH 0160/1350] Fix GESTURE_DRAG and GESTURE_SWIPE_* issues (mostly) for web (#3183) --- src/rgestures.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/rgestures.h b/src/rgestures.h index 78dde76ee..d8bb3b1d9 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -178,8 +178,9 @@ float GetGesturePinchAngle(void); // Get gesture pinch ang //---------------------------------------------------------------------------------- // Defines and Macros //---------------------------------------------------------------------------------- -#define FORCE_TO_SWIPE 0.0005f // Swipe force, measured in normalized screen units/time +#define FORCE_TO_SWIPE 0.2f // Swipe force, measured in normalized screen units/time #define MINIMUM_DRAG 0.015f // Drag minimum force, measured in normalized screen units (0.0f to 1.0f) +#define DRAG_TIMEOUT 0.2f // Drag minimum time for web, measured in seconds #define MINIMUM_PINCH 0.005f // Pinch minimum force, measured in normalized screen units (0.0f to 1.0f) #define TAP_TIMEOUT 0.3f // Tap minimum time, measured in seconds #define PINCH_TIMEOUT 0.3f // Pinch minimum time, measured in seconds @@ -297,7 +298,8 @@ void ProcessGestureEvent(GestureEvent event) } else if (event.touchAction == TOUCH_ACTION_UP) { - if (GESTURES.current == GESTURE_DRAG) GESTURES.Touch.upPosition = event.position[0]; + // A swipe can happen while the current gesture is drag, but (specially for web) also hold, so set upPosition for both cases + if (GESTURES.current == GESTURE_DRAG || GESTURES.current == GESTURE_HOLD) GESTURES.Touch.upPosition = event.position[0]; // NOTE: GESTURES.Drag.intensity dependent on the resolution of the screen GESTURES.Drag.distance = rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition); @@ -348,7 +350,12 @@ void ProcessGestureEvent(GestureEvent event) GESTURES.Hold.resetRequired = false; // Detect GESTURE_DRAG +#if defined(PLATFORM_WEB) + // An alternative check to detect gesture drag is necessary since moveDownPositionA on touch for web is always zero, causing the distance calculation to be inaccurate + if ((rgGetCurrentTime() - GESTURES.Touch.eventTime) > DRAG_TIMEOUT) +#else if (rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.moveDownPositionA) >= MINIMUM_DRAG) +#endif { GESTURES.Touch.eventTime = rgGetCurrentTime(); GESTURES.current = GESTURE_DRAG; From d6f16b76649944f65491633a75ad648a6ddfdacf Mon Sep 17 00:00:00 2001 From: Rokas Puzonas Date: Wed, 19 Jul 2023 14:46:14 +0300 Subject: [PATCH 0161/1350] Update usage of 'sinf()' and 'cosf()' to be correct (#3181) * Update usage of 'sinf()' and 'cosf()' to be correct * Update formatting of arithmetic operations --- src/rshapes.c | 216 +++++++++++++++++++++++++++----------------------- 1 file changed, 117 insertions(+), 99 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index d726ff0e8..86e998a6d 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -393,14 +393,14 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); rlVertex2f(center.x, center.y); - rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength*2))*radius, center.y + sinf(DEG2RAD*(angle + stepLength*2))*radius); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); - rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength*2))*radius, center.y + cosf(DEG2RAD*(angle + stepLength*2))*radius); + rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); angle += (stepLength*2); } @@ -413,11 +413,11 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); rlVertex2f(center.x, center.y); - rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); - rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); + + rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); rlVertex2f(center.x, center.y); @@ -432,8 +432,8 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(center.x, center.y); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); angle += stepLength; } @@ -475,15 +475,15 @@ void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float { rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(center.x, center.y); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); } for (int i = 0; i < segments; i++) { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); angle += stepLength; } @@ -492,7 +492,7 @@ void DrawCircleSectorLines(Vector2 center, float radius, float startAngle, float { rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(center.x, center.y); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); } rlEnd(); } @@ -507,9 +507,9 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co rlColor4ub(color1.r, color1.g, color1.b, color1.a); rlVertex2f((float)centerX, (float)centerY); rlColor4ub(color2.r, color2.g, color2.b, color2.a); - rlVertex2f((float)centerX + sinf(DEG2RAD*i)*radius, (float)centerY + cosf(DEG2RAD*i)*radius); + rlVertex2f((float)centerX + cosf(DEG2RAD*(i + 10))*radius, (float)centerY + sinf(DEG2RAD*(i + 10))*radius); rlColor4ub(color2.r, color2.g, color2.b, color2.a); - rlVertex2f((float)centerX + sinf(DEG2RAD*(i + 10))*radius, (float)centerY + cosf(DEG2RAD*(i + 10))*radius); + rlVertex2f((float)centerX + cosf(DEG2RAD*i)*radius, (float)centerY + sinf(DEG2RAD*i)*radius); } rlEnd(); } @@ -530,8 +530,8 @@ void DrawCircleLines(int centerX, int centerY, float radius, Color color) // NOTE: Circle outline is drawn pixel by pixel every degree (0 to 360) for (int i = 0; i < 360; i += 10) { - rlVertex2f(centerX + sinf(DEG2RAD*i)*radius, centerY + cosf(DEG2RAD*i)*radius); - rlVertex2f(centerX + sinf(DEG2RAD*(i + 10))*radius, centerY + cosf(DEG2RAD*(i + 10))*radius); + rlVertex2f(centerX + cosf(DEG2RAD*i)*radius, centerY + sinf(DEG2RAD*i)*radius); + rlVertex2f(centerX + cosf(DEG2RAD*(i + 10))*radius, centerY + sinf(DEG2RAD*(i + 10))*radius); } rlEnd(); } @@ -544,8 +544,8 @@ void DrawEllipse(int centerX, int centerY, float radiusH, float radiusV, Color c { rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f((float)centerX, (float)centerY); - rlVertex2f((float)centerX + sinf(DEG2RAD*i)*radiusH, (float)centerY + cosf(DEG2RAD*i)*radiusV); - rlVertex2f((float)centerX + sinf(DEG2RAD*(i + 10))*radiusH, (float)centerY + cosf(DEG2RAD*(i + 10))*radiusV); + rlVertex2f((float)centerX + cosf(DEG2RAD*(i + 10))*radiusH, (float)centerY + sinf(DEG2RAD*(i + 10))*radiusV); + rlVertex2f((float)centerX + cosf(DEG2RAD*i)*radiusH, (float)centerY + sinf(DEG2RAD*i)*radiusV); } rlEnd(); } @@ -557,8 +557,8 @@ void DrawEllipseLines(int centerX, int centerY, float radiusH, float radiusV, Co for (int i = 0; i < 360; i += 10) { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(centerX + sinf(DEG2RAD*i)*radiusH, centerY + cosf(DEG2RAD*i)*radiusV); - rlVertex2f(centerX + sinf(DEG2RAD*(i + 10))*radiusH, centerY + cosf(DEG2RAD*(i + 10))*radiusV); + rlVertex2f(centerX + cosf(DEG2RAD*(i + 10))*radiusH, centerY + sinf(DEG2RAD*(i + 10))*radiusV); + rlVertex2f(centerX + cosf(DEG2RAD*i)*radiusH, centerY + sinf(DEG2RAD*i)*radiusV); } rlEnd(); } @@ -616,17 +616,17 @@ void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startA { rlColor4ub(color.r, color.g, color.b, color.a); - rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); - rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); - rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius); angle += stepLength; } @@ -639,13 +639,13 @@ void DrawRing(Vector2 center, float innerRadius, float outerRadius, float startA { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); angle += stepLength; } @@ -702,19 +702,19 @@ void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float s if (showCapLines) { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); } for (int i = 0; i < segments; i++) { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); angle += stepLength; } @@ -722,8 +722,8 @@ void DrawRingLines(Vector2 center, float innerRadius, float outerRadius, float s if (showCapLines) { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); } rlEnd(); } @@ -982,7 +982,7 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co }; const Vector2 centers[4] = { point[8], point[9], point[10], point[11] }; - const float angles[4] = { 180.0f, 90.0f, 0.0f, 270.0f }; + const float angles[4] = { 180.0f, 270.0f, 0.0f, 90.0f }; #if defined(SUPPORT_QUADS_DRAW_MODE) rlSetTexture(texShapes.id); @@ -1000,12 +1000,16 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co rlColor4ub(color.r, color.g, color.b, color.a); rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); rlVertex2f(center.x, center.y); - rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); - rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength*2))*radius, center.y + cosf(DEG2RAD*(angle + stepLength*2))*radius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength*2))*radius, center.y + sinf(DEG2RAD*(angle + stepLength*2))*radius); + + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); + + rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); + angle += (stepLength*2); } @@ -1015,10 +1019,13 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co rlColor4ub(color.r, color.g, color.b, color.a); rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); rlVertex2f(center.x, center.y); - rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); + + rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); rlVertex2f(center.x, center.y); } @@ -1093,8 +1100,8 @@ void DrawRectangleRounded(Rectangle rec, float roundness, int segments, Color co { rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(center.x, center.y); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*radius, center.y + cosf(DEG2RAD*angle)*radius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*radius, center.y + cosf(DEG2RAD*(angle + stepLength))*radius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); angle += stepLength; } } @@ -1208,7 +1215,7 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo {(float)(rec.x + rec.width) - innerRadius, (float)(rec.y + rec.height) - innerRadius}, {(float)rec.x + innerRadius, (float)(rec.y + rec.height) - innerRadius} // P18, P19 }; - const float angles[4] = { 180.0f, 90.0f, 0.0f, 270.0f }; + const float angles[4] = { 180.0f, 270.0f, 0.0f, 90.0f }; if (lineThick > 1) { @@ -1225,14 +1232,18 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo for (int i = 0; i < segments; i++) { rlColor4ub(color.r, color.g, color.b, color.a); + rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); - rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); - rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius); + + rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); angle += stepLength; } @@ -1297,13 +1308,13 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*innerRadius, center.y + cosf(DEG2RAD*angle)*innerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*innerRadius, center.y + sinf(DEG2RAD*angle)*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*innerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*innerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*innerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); angle += stepLength; } @@ -1361,8 +1372,8 @@ void DrawRectangleRoundedLines(Rectangle rec, float roundness, int segments, flo for (int i = 0; i < segments; i++) { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x + sinf(DEG2RAD*angle)*outerRadius, center.y + cosf(DEG2RAD*angle)*outerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + cosf(DEG2RAD*(angle + stepLength))*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*angle)*outerRadius, center.y + sinf(DEG2RAD*angle)*outerRadius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*outerRadius, center.y + sinf(DEG2RAD*(angle + stepLength))*outerRadius); angle += stepLength; } } @@ -1492,7 +1503,8 @@ void DrawTriangleStrip(Vector2 *points, int pointCount, Color color) void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color color) { if (sides < 3) sides = 3; - float centralAngle = rotation; + float centralAngle = rotation*DEG2RAD; + float angleStep = 360.0f/(float)sides*DEG2RAD; #if defined(SUPPORT_QUADS_DRAW_MODE) rlSetTexture(texShapes.id); @@ -1501,19 +1513,21 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col for (int i = 0; i < sides; i++) { rlColor4ub(color.r, color.g, color.b, color.a); + float nextAngle = centralAngle + angleStep; rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); rlVertex2f(center.x, center.y); rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); + rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius); + + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); + rlVertex2f(center.x + cosf(nextAngle)*radius, center.y + sinf(nextAngle)*radius); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); + rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius); - centralAngle += 360.0f/(float)sides; - rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); + centralAngle = nextAngle; } rlEnd(); rlSetTexture(0); @@ -1524,10 +1538,10 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col rlColor4ub(color.r, color.g, color.b, color.a); rlVertex2f(center.x, center.y); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); + rlVertex2f(center.x + cosf(centralAngle + angleStep)*radius, center.y + sinf(centralAngle + angleStep)*radius); + rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius); - centralAngle += 360.0f/(float)sides; - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); + centralAngle += angleStep; } rlEnd(); #endif @@ -1537,16 +1551,18 @@ void DrawPoly(Vector2 center, int sides, float radius, float rotation, Color col void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Color color) { if (sides < 3) sides = 3; - float centralAngle = rotation; + float centralAngle = rotation*DEG2RAD; + float angleStep = 360.0f/(float)sides*DEG2RAD; rlBegin(RL_LINES); for (int i = 0; i < sides; i++) { rlColor4ub(color.r, color.g, color.b, color.a); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); - centralAngle += 360.0f/(float)sides; - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); + rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius); + rlVertex2f(center.x + cosf(centralAngle + angleStep)*radius, center.y + sinf(centralAngle + angleStep)*radius); + + centralAngle += angleStep; } rlEnd(); } @@ -1554,8 +1570,8 @@ void DrawPolyLines(Vector2 center, int sides, float radius, float rotation, Colo void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, float lineThick, Color color) { if (sides < 3) sides = 3; - float centralAngle = rotation; - float exteriorAngle = 360.0f/(float)sides; + float centralAngle = rotation*DEG2RAD; + float exteriorAngle = 360.0f/(float)sides*DEG2RAD; float innerRadius = radius - (lineThick*cosf(DEG2RAD*exteriorAngle/2.0f)); #if defined(SUPPORT_QUADS_DRAW_MODE) @@ -1565,19 +1581,21 @@ void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, fl for (int i = 0; i < sides; i++) { rlColor4ub(color.r, color.g, color.b, color.a); - - rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*innerRadius, center.y + cosf(DEG2RAD*centralAngle)*innerRadius); + float nextAngle = centralAngle + exteriorAngle; rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); + rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius); - centralAngle += exteriorAngle; - rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); + rlTexCoord2f(texShapesRec.x/texShapes.width, texShapesRec.y/texShapes.height); + rlVertex2f(center.x + cosf(centralAngle)*innerRadius, center.y + sinf(centralAngle)*innerRadius); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*innerRadius, center.y + cosf(DEG2RAD*centralAngle)*innerRadius); + rlVertex2f(center.x + cosf(nextAngle)*innerRadius, center.y + sinf(nextAngle)*innerRadius); + + rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); + rlVertex2f(center.x + cosf(nextAngle)*radius, center.y + sinf(nextAngle)*radius); + + centralAngle = nextAngle; } rlEnd(); rlSetTexture(0); @@ -1588,13 +1606,13 @@ void DrawPolyLinesEx(Vector2 center, int sides, float radius, float rotation, fl rlColor4ub(color.r, color.g, color.b, color.a); float nextAngle = centralAngle + exteriorAngle; - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*radius, center.y + cosf(DEG2RAD*centralAngle)*radius); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*innerRadius, center.y + cosf(DEG2RAD*centralAngle)*innerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*nextAngle)*radius, center.y + cosf(DEG2RAD*nextAngle)*radius); + rlVertex2f(center.x + cosf(nextAngle)*radius, center.y + sinf(nextAngle)*radius); + rlVertex2f(center.x + cosf(centralAngle)*radius, center.y + sinf(centralAngle)*radius); + rlVertex2f(center.x + cosf(centralAngle)*innerRadius, center.y + sinf(centralAngle)*innerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*centralAngle)*innerRadius, center.y + cosf(DEG2RAD*centralAngle)*innerRadius); - rlVertex2f(center.x + sinf(DEG2RAD*nextAngle)*radius, center.y + cosf(DEG2RAD*nextAngle)*radius); - rlVertex2f(center.x + sinf(DEG2RAD*nextAngle)*innerRadius, center.y + cosf(DEG2RAD*nextAngle)*innerRadius); + rlVertex2f(center.x + cosf(centralAngle)*innerRadius, center.y + sinf(centralAngle)*innerRadius); + rlVertex2f(center.x + cosf(nextAngle)*innerRadius, center.y + sinf(nextAngle)*innerRadius); + rlVertex2f(center.x + cosf(nextAngle)*radius, center.y + sinf(nextAngle)*radius); centralAngle = nextAngle; } From 7124a14a60a4bf7e4357030395750e03a1b976b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Gonz=C3=A1lez=20Palomo?= Date: Wed, 19 Jul 2023 23:11:29 +0200 Subject: [PATCH 0162/1350] Document buffer format for audio processors. (#3186) --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index f9f36626e..c20a69d2b 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1591,7 +1591,7 @@ RLAPI void SetAudioStreamCallback(AudioStream stream, AudioCallback callback); RLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream RLAPI void DetachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Detach audio stream processor from stream -RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline +RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline, receives the samples as s RLAPI void DetachAudioMixedProcessor(AudioCallback processor); // Detach audio stream processor from the entire audio pipeline #if defined(__cplusplus) From 055fd752c273f246515375d79fca511eef8ecf1b Mon Sep 17 00:00:00 2001 From: Danil <61111955+localwhale20@users.noreply.github.com> Date: Thu, 20 Jul 2023 11:56:35 +0300 Subject: [PATCH 0163/1350] Fixed GetMonitorName description (#3184) (#3189) --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index c20a69d2b..e99d674e2 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -974,7 +974,7 @@ RLAPI int GetMonitorPhysicalHeight(int monitor); // Get specifi RLAPI int GetMonitorRefreshRate(int monitor); // Get specified monitor refresh rate RLAPI Vector2 GetWindowPosition(void); // Get window position XY on monitor RLAPI Vector2 GetWindowScaleDPI(void); // Get window scale DPI factor -RLAPI const char *GetMonitorName(int monitor); // Get the human-readable, UTF-8 encoded name of the primary monitor +RLAPI const char *GetMonitorName(int monitor); // Get the human-readable, UTF-8 encoded name of the specified monitor RLAPI void SetClipboardText(const char *text); // Set clipboard text content RLAPI const char *GetClipboardText(void); // Get clipboard text content RLAPI void EnableEventWaiting(void); // Enable waiting for events on EndDrawing(), no automatic event polling From 5635e4214cb31b0c83e2d65f32af1cc3d40ffdf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alberto=20Gonz=C3=A1lez=20Palomo?= Date: Thu, 20 Jul 2023 13:41:35 +0200 Subject: [PATCH 0164/1350] Add note about sample format to AttachAudioStreamProcessor() (#3188) --- src/raylib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/raylib.h b/src/raylib.h index e99d674e2..108ab0245 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1588,7 +1588,7 @@ RLAPI void SetAudioStreamPan(AudioStream stream, float pan); // Set pan RLAPI void SetAudioStreamBufferSizeDefault(int size); // Default size for new audio streams RLAPI void SetAudioStreamCallback(AudioStream stream, AudioCallback callback); // Audio thread callback to request new data -RLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream +RLAPI void AttachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Attach audio stream processor to stream, receives the samples as s RLAPI void DetachAudioStreamProcessor(AudioStream stream, AudioCallback processor); // Detach audio stream processor from stream RLAPI void AttachAudioMixedProcessor(AudioCallback processor); // Attach audio stream processor to the entire audio pipeline, receives the samples as s From 1310617a922c12256259eca69da0d746f5d10b71 Mon Sep 17 00:00:00 2001 From: smalltimewizard <83366732+smalltimewizard@users.noreply.github.com> Date: Thu, 20 Jul 2023 07:50:56 -0400 Subject: [PATCH 0165/1350] Optimization of ImageDrawRectangleRec() (#3185) A significant performance increase can be had by copying the first row to all other rows. --- src/rtextures.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index bc53e1cec..ca341bc81 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3135,26 +3135,28 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color) if (rec.height < 0) rec.height = 0; int sy = (int)rec.y; - int ey = sy + (int)rec.height; - int sx = (int)rec.x; int bytesPerPixel = GetPixelDataSize(1, 1, dst->format); - for (int y = sy; y < ey; y++) + // Fill in the first pixel of the first row based on image format + ImageDrawPixel(dst, sx, sy, color); + + int bytesOffset = ((sy*dst->width) + sx)*bytesPerPixel; + unsigned char *pSrcPixel = (unsigned char *)dst->data + bytesOffset; + + // Repeat the first pixel data throughout the row + for (int x = 1; x < (int)rec.width; x++) { - // Fill in the first pixel of the row based on image format - ImageDrawPixel(dst, sx, y, color); - - int bytesOffset = ((y*dst->width) + sx)*bytesPerPixel; - unsigned char *pSrcPixel = (unsigned char *)dst->data + bytesOffset; - - // Repeat the first pixel data throughout the row - for (int x = 1; x < (int)rec.width; x++) - { - memcpy(pSrcPixel + x*bytesPerPixel, pSrcPixel, bytesPerPixel); - } + memcpy(pSrcPixel + x*bytesPerPixel, pSrcPixel, bytesPerPixel); } + + // Repeat the first row data for all other rows + int bytesPerRow = bytesPerPixel * (int)rec.width; + for (int y = 1; y < (int)rec.height; y++) + { + memcpy(pSrcPixel + (y*dst->width)*bytesPerPixel, pSrcPixel, bytesPerRow); + } } // Draw rectangle lines within an image From ad2338b994785c887528c91a843d6720cac2c2b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20V=C3=A1clav=20Flasar?= <109527915+jakubvf@users.noreply.github.com> Date: Fri, 21 Jul 2023 14:08:35 +0200 Subject: [PATCH 0166/1350] build.zig: Support for building with PLAFORM_DRM (#3191) - Adds an option -Dplatform_drm when using zig build - When building for linux, checks whether -Dplatform_drm is present and configures the build accordingly. --- src/build.zig | 43 +++++++++++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 10 deletions(-) diff --git a/src/build.zig b/src/build.zig index 8485322d5..64480a4f1 100644 --- a/src/build.zig +++ b/src/build.zig @@ -16,7 +16,10 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built }); raylib.linkLibC(); - raylib.addIncludePath(srcdir ++ "/external/glfw/include"); + // No GLFW required on PLATFORM_DRM + if (!options.platform_drm) { + raylib.addIncludePath(srcdir ++ "/external/glfw/include"); + } raylib.addCSourceFiles(&.{ srcdir ++ "/raudio.c", @@ -49,15 +52,32 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built raylib.defineCMacro("PLATFORM_DESKTOP", null); }, .linux => { - raylib.addCSourceFiles(&.{srcdir ++ "/rglfw.c"}, raylib_flags); - raylib.linkSystemLibrary("GL"); - raylib.linkSystemLibrary("rt"); - raylib.linkSystemLibrary("dl"); - raylib.linkSystemLibrary("m"); - raylib.linkSystemLibrary("X11"); - raylib.addIncludePath("/usr/include"); + if (!options.platform_drm) { + raylib.addCSourceFiles(&.{srcdir ++ "/rglfw.c"}, raylib_flags); + raylib.linkSystemLibrary("GL"); + raylib.linkSystemLibrary("rt"); + raylib.linkSystemLibrary("dl"); + raylib.linkSystemLibrary("m"); + raylib.linkSystemLibrary("X11"); + raylib.addIncludePath("/usr/include"); - raylib.defineCMacro("PLATFORM_DESKTOP", null); + raylib.defineCMacro("PLATFORM_DESKTOP", null); + } else { + raylib.linkSystemLibrary("GLESv2"); + raylib.linkSystemLibrary("EGL"); + raylib.linkSystemLibrary("drm"); + raylib.linkSystemLibrary("gbm"); + raylib.linkSystemLibrary("pthread"); + raylib.linkSystemLibrary("rt"); + raylib.linkSystemLibrary("m"); + raylib.linkSystemLibrary("dl"); + raylib.addIncludePath("/usr/include/libdrm"); + + raylib.defineCMacro("PLATFORM_DRM", null); + raylib.defineCMacro("GRAPHICS_API_OPENGL_ES2", null); + raylib.defineCMacro("EGL_NO_X11", null); + raylib.defineCMacro("DEFAULT_BATCH_BUFFER_ELEMENT", "2048"); + } }, .freebsd, .openbsd, .netbsd, .dragonfly => { raylib.addCSourceFiles(&.{srcdir ++ "/rglfw.c"}, raylib_flags); @@ -115,8 +135,9 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built return raylib; } -const Options = struct { +pub const Options = struct { raygui: bool = false, + platform_drm: bool = false, }; pub fn build(b: *std.Build) void { @@ -131,9 +152,11 @@ pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); const raygui = b.option(bool, "raygui", "Compile with raygui support"); + const platform_drm = b.option(bool, "platform_drm", "Compile raylib in native mode (no X11)"); const lib = addRaylib(b, target, optimize, .{ .raygui = raygui orelse false, + .platform_drm = platform_drm orelse false, }); lib.installHeader("src/raylib.h", "raylib.h"); From 295e8c2a2faa7b0c80a840695e37f3ed1740eb88 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sat, 22 Jul 2023 15:50:04 -0300 Subject: [PATCH 0167/1350] Optimize and simplify the gesture system (#3190) * Optimize and simplify the gesture system * Decouples GESTURE_SWIPE_* from GESTURE_DRAG --- src/rgestures.h | 33 +++++---------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/src/rgestures.h b/src/rgestures.h index d8bb3b1d9..779dbed56 100644 --- a/src/rgestures.h +++ b/src/rgestures.h @@ -180,7 +180,7 @@ float GetGesturePinchAngle(void); // Get gesture pinch ang //---------------------------------------------------------------------------------- #define FORCE_TO_SWIPE 0.2f // Swipe force, measured in normalized screen units/time #define MINIMUM_DRAG 0.015f // Drag minimum force, measured in normalized screen units (0.0f to 1.0f) -#define DRAG_TIMEOUT 0.2f // Drag minimum time for web, measured in seconds +#define DRAG_TIMEOUT 0.3f // Drag minimum time for web, measured in seconds #define MINIMUM_PINCH 0.005f // Pinch minimum force, measured in normalized screen units (0.0f to 1.0f) #define TAP_TIMEOUT 0.3f // Tap minimum time, measured in seconds #define PINCH_TIMEOUT 0.3f // Pinch minimum time, measured in seconds @@ -219,8 +219,7 @@ typedef struct { float intensity; // DRAG intensity, how far why did the DRAG (pixels per frame) } Drag; struct { - bool start; // SWIPE used to define when start measuring GESTURES.Swipe.timeDuration - double timeDuration; // SWIPE time to calculate drag intensity + double startTime; // SWIPE start time to calculate drag intensity } Swipe; struct { Vector2 vector; // PINCH vector (between first and second touch points) @@ -292,7 +291,7 @@ void ProcessGestureEvent(GestureEvent event) GESTURES.Touch.upPosition = GESTURES.Touch.downPositionA; GESTURES.Touch.eventTime = rgGetCurrentTime(); - GESTURES.Touch.firstId = event.pointId[0]; + GESTURES.Swipe.startTime = rgGetCurrentTime(); GESTURES.Drag.vector = (Vector2){ 0.0f, 0.0f }; } @@ -303,12 +302,10 @@ void ProcessGestureEvent(GestureEvent event) // NOTE: GESTURES.Drag.intensity dependent on the resolution of the screen GESTURES.Drag.distance = rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition); - GESTURES.Drag.intensity = GESTURES.Drag.distance/(float)((rgGetCurrentTime() - GESTURES.Swipe.timeDuration)); - - GESTURES.Swipe.start = false; + GESTURES.Drag.intensity = GESTURES.Drag.distance/(float)((rgGetCurrentTime() - GESTURES.Swipe.startTime)); // Detect GESTURE_SWIPE - if ((GESTURES.Drag.intensity > FORCE_TO_SWIPE) && (GESTURES.Touch.firstId == event.pointId[0])) + if ((GESTURES.Drag.intensity > FORCE_TO_SWIPE) && (GESTURES.current != GESTURE_DRAG)) { // NOTE: Angle should be inverted in Y GESTURES.Drag.angle = 360.0f - rgVector2Angle(GESTURES.Touch.downPositionA, GESTURES.Touch.upPosition); @@ -333,14 +330,6 @@ void ProcessGestureEvent(GestureEvent event) } else if (event.touchAction == TOUCH_ACTION_MOVE) { - if (GESTURES.current == GESTURE_DRAG) GESTURES.Touch.eventTime = rgGetCurrentTime(); - - if (!GESTURES.Swipe.start) - { - GESTURES.Swipe.timeDuration = rgGetCurrentTime(); - GESTURES.Swipe.start = true; - } - GESTURES.Touch.moveDownPositionA = event.position[0]; if (GESTURES.current == GESTURE_HOLD) @@ -350,12 +339,7 @@ void ProcessGestureEvent(GestureEvent event) GESTURES.Hold.resetRequired = false; // Detect GESTURE_DRAG -#if defined(PLATFORM_WEB) - // An alternative check to detect gesture drag is necessary since moveDownPositionA on touch for web is always zero, causing the distance calculation to be inaccurate if ((rgGetCurrentTime() - GESTURES.Touch.eventTime) > DRAG_TIMEOUT) -#else - if (rgVector2Distance(GESTURES.Touch.downPositionA, GESTURES.Touch.moveDownPositionA) >= MINIMUM_DRAG) -#endif { GESTURES.Touch.eventTime = rgGetCurrentTime(); GESTURES.current = GESTURE_DRAG; @@ -436,13 +420,6 @@ void UpdateGestures(void) GESTURES.Hold.timeDuration = rgGetCurrentTime(); } - if (((rgGetCurrentTime() - GESTURES.Touch.eventTime) > TAP_TIMEOUT) && (GESTURES.current == GESTURE_DRAG) && (GESTURES.Touch.pointCount < 2)) - { - GESTURES.current = GESTURE_HOLD; - GESTURES.Hold.timeDuration = rgGetCurrentTime(); - GESTURES.Hold.resetRequired = true; - } - // Detect GESTURE_NONE if ((GESTURES.current == GESTURE_SWIPE_RIGHT) || (GESTURES.current == GESTURE_SWIPE_UP) || (GESTURES.current == GESTURE_SWIPE_LEFT) || (GESTURES.current == GESTURE_SWIPE_DOWN)) { From 090b857912c338f0021987d0a4d5b42f9844e2c4 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sun, 23 Jul 2023 15:35:41 -0300 Subject: [PATCH 0168/1350] Fix mouse wheel not working in PLATFORM_RPI or PLATFORM_DRM (#3193) --- src/rcore.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 45495bb15..d6aeaec96 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -468,6 +468,7 @@ typedef struct CoreData { Vector2 currentWheelMove; // Registers current mouse wheel variation Vector2 previousWheelMove; // Registers previous mouse wheel variation #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) + Vector2 eventWheelMove; // Registers the event mouse wheel variation // NOTE: currentButtonState[] can't be written directly due to multithreading, app could miss the update char currentButtonStateEvdev[MAX_MOUSE_BUTTONS]; // Holds the new mouse state for the next polling event to grab #endif @@ -5062,7 +5063,8 @@ void PollInputEvents(void) // Register previous mouse states CORE.Input.Mouse.previousWheelMove = CORE.Input.Mouse.currentWheelMove; - CORE.Input.Mouse.currentWheelMove = (Vector2){ 0.0f, 0.0f }; + CORE.Input.Mouse.currentWheelMove = CORE.Input.Mouse.eventWheelMove; + CORE.Input.Mouse.eventWheelMove = (Vector2){ 0.0f, 0.0f }; for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) { CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; @@ -6677,7 +6679,7 @@ static void *EventThread(void *arg) gestureUpdate = true; } - if (event.code == REL_WHEEL) CORE.Input.Mouse.currentWheelMove.y += event.value; + if (event.code == REL_WHEEL) CORE.Input.Mouse.eventWheelMove.y += event.value; } // Absolute movement parsing From 32b54be5cbd9eb77fc36a4c044dce69c5640a26a Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 24 Jul 2023 12:32:22 +0200 Subject: [PATCH 0169/1350] Update FAQ.md --- FAQ.md | 1 + 1 file changed, 1 insertion(+) diff --git a/FAQ.md b/FAQ.md index 274236d47..be06657b9 100644 --- a/FAQ.md +++ b/FAQ.md @@ -20,6 +20,7 @@ - [Does raylib support the Vulkan API?](#does-raylib-support-the-vulkan-api) - [What could I expect to see in raylib in the future?](#what-could-i-expect-to-see-in-raylib-in-the-future) - [Who are the raylib developers?](#who-are-the-raylib-developers) +- [MORE QUESTIONS...](https://github.com/raysan5/raylib/wiki/Frequently-Asked-Questions) ### What is raylib? From 298f93ef50cbf976ed497714e7cf5fbc0c1193e4 Mon Sep 17 00:00:00 2001 From: bohonghuang <1281299809@qq.com> Date: Wed, 26 Jul 2023 23:46:57 +0800 Subject: [PATCH 0170/1350] Fix `DrawBillboardPro` to allow `source` of negative size (#3197) (#3203) --- src/rmodels.c | 45 ++++++++++++++++++++++++++++++++------------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 49f922695..8c7e088cb 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -3492,7 +3492,7 @@ void DrawBillboardRec(Camera camera, Texture2D texture, Rectangle source, Vector void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector3 position, Vector3 up, Vector2 size, Vector2 origin, float rotation, Color tint) { // NOTE: Billboard size will maintain source rectangle aspect ratio, size will represent billboard width - Vector2 sizeRatio = { size.x*(float)source.width/source.height, size.y }; + Vector2 sizeRatio = { size.x*fabsf((float)source.width/source.height), size.y }; Matrix matView = MatrixLookAt(camera.position, camera.target, camera.up); @@ -3558,21 +3558,40 @@ void DrawBillboardPro(Camera camera, Texture2D texture, Rectangle source, Vector rlBegin(RL_QUADS); rlColor4ub(tint.r, tint.g, tint.b, tint.a); - // Bottom-left corner for texture and quad - rlTexCoord2f((float)source.x/texture.width, (float)source.y/texture.height); - rlVertex3f(topLeft.x, topLeft.y, topLeft.z); + if (sizeRatio.x * sizeRatio.y >= 0.0f) + { + // Bottom-left corner for texture and quad + rlTexCoord2f((float)source.x/texture.width, (float)source.y/texture.height); + rlVertex3f(topLeft.x, topLeft.y, topLeft.z); - // Top-left corner for texture and quad - rlTexCoord2f((float)source.x/texture.width, (float)(source.y + source.height)/texture.height); - rlVertex3f(bottomLeft.x, bottomLeft.y, bottomLeft.z); + // Top-left corner for texture and quad + rlTexCoord2f((float)source.x/texture.width, (float)(source.y + source.height)/texture.height); + rlVertex3f(bottomLeft.x, bottomLeft.y, bottomLeft.z); - // Top-right corner for texture and quad - rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)(source.y + source.height)/texture.height); - rlVertex3f(bottomRight.x, bottomRight.y, bottomRight.z); + // Top-right corner for texture and quad + rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)(source.y + source.height)/texture.height); + rlVertex3f(bottomRight.x, bottomRight.y, bottomRight.z); + + // Bottom-right corner for texture and quad + rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)source.y/texture.height); + rlVertex3f(topRight.x, topRight.y, topRight.z); + } + else + { + // Reverse vertex order if the size has only one negative dimension + rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)source.y/texture.height); + rlVertex3f(topRight.x, topRight.y, topRight.z); + + rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)(source.y + source.height)/texture.height); + rlVertex3f(bottomRight.x, bottomRight.y, bottomRight.z); + + rlTexCoord2f((float)source.x/texture.width, (float)(source.y + source.height)/texture.height); + rlVertex3f(bottomLeft.x, bottomLeft.y, bottomLeft.z); + + rlTexCoord2f((float)source.x/texture.width, (float)source.y/texture.height); + rlVertex3f(topLeft.x, topLeft.y, topLeft.z); + } - // Bottom-right corner for texture and quad - rlTexCoord2f((float)(source.x + source.width)/texture.width, (float)source.y/texture.height); - rlVertex3f(topRight.x, topRight.y, topRight.z); rlEnd(); rlSetTexture(0); From ac6f889dfcaf4c195c13abed09522ecebd03b0f9 Mon Sep 17 00:00:00 2001 From: ashn <60763262+ashn-dot-dev@users.noreply.github.com> Date: Wed, 26 Jul 2023 11:50:07 -0400 Subject: [PATCH 0171/1350] Fix misleading indentation in src/Makefile (#3202) --- src/Makefile | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Makefile b/src/Makefile index fc4c5b0c7..4f742a259 100644 --- a/src/Makefile +++ b/src/Makefile @@ -801,19 +801,19 @@ ifeq ($(ROOT),root) # and $(RAYLIB_H_INSTALL_PATH). Please confirm each item. ifeq ($(PLATFORM_OS),LINUX) ifeq ($(RAYLIB_LIBTYPE),SHARED) - rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so - rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so.$(RAYLIB_API_VERSION) - rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so.$(RAYLIB_VERSION) - # Uncomment to clean up the runtime linker cache. See install target. - ldconfig + rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so + rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so.$(RAYLIB_API_VERSION) + rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.so.$(RAYLIB_VERSION) + # Uncomment to clean up the runtime linker cache. See install target. + ldconfig else - rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.a + rm --force --interactive --verbose $(RAYLIB_INSTALL_PATH)/libraylib.a endif rm --force --interactive --verbose $(RAYLIB_H_INSTALL_PATH)/raylib.h rm --force --interactive --verbose $(RAYLIB_H_INSTALL_PATH)/raymath.h rm --force --interactive --verbose $(RAYLIB_H_INSTALL_PATH)/rlgl.h @echo "raylib development files removed!" - else + else @echo "This function currently works on GNU/Linux systems. Add yours today (^;" endif else From 5d28bad0adf2a85a48f6ce4644ce728ec9868208 Mon Sep 17 00:00:00 2001 From: Nikolas Date: Thu, 27 Jul 2023 22:41:43 +0200 Subject: [PATCH 0172/1350] Fix LoadTextureCubemap for manual layouts (#3204) --- src/rtextures.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/rtextures.c b/src/rtextures.c index ca341bc81..c13e331cd 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3369,10 +3369,16 @@ TextureCubemap LoadTextureCubemap(Image image, int layout) if ((image.height/6) == image.width) { layout = CUBEMAP_LAYOUT_LINE_VERTICAL; cubemap.width = image.height/6; } else if ((image.width/3) == (image.height/4)) { layout = CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR; cubemap.width = image.width/3; } } - - cubemap.height = cubemap.width; + } else { + if (layout == CUBEMAP_LAYOUT_LINE_VERTICAL) cubemap.width = image.height/6; + if (layout == CUBEMAP_LAYOUT_LINE_HORIZONTAL) cubemap.width = image.width/6; + if (layout == CUBEMAP_LAYOUT_CROSS_THREE_BY_FOUR) cubemap.width = image.width/3; + if (layout == CUBEMAP_LAYOUT_CROSS_FOUR_BY_THREE) cubemap.width = image.width/4; + if (layout == CUBEMAP_LAYOUT_PANORAMA) cubemap.width = image.width/4; } + cubemap.height = cubemap.width; + // Layout provided or already auto-detected if (layout != CUBEMAP_LAYOUT_AUTO_DETECT) { From 962030e70ad3dfcce5d4642ad65890fde93ad117 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Fri, 28 Jul 2023 15:08:41 -0300 Subject: [PATCH 0173/1350] Changes SetWindowMonitor() to no longer force fullscreen (#3209) * Changes SetWindowMonitor() to no longer force fullscreen * Readds fullscreen support --- src/raylib.h | 2 +- src/rcore.c | 32 ++++++++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 108ab0245..b3f17bd53 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -954,7 +954,7 @@ RLAPI void SetWindowIcon(Image image); // Set icon fo RLAPI void SetWindowIcons(Image *images, int count); // Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP) RLAPI void SetWindowTitle(const char *title); // Set title for window (only PLATFORM_DESKTOP) RLAPI void SetWindowPosition(int x, int y); // Set window position on screen (only PLATFORM_DESKTOP) -RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window (fullscreen mode) +RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window RLAPI void SetWindowMinSize(int width, int height); // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE) RLAPI void SetWindowSize(int width, int height); // Set window dimensions RLAPI void SetWindowOpacity(float opacity); // Set window opacity [0.0f..1.0f] (only PLATFORM_DESKTOP) diff --git a/src/rcore.c b/src/rcore.c index d6aeaec96..66a914ee7 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1661,7 +1661,7 @@ void SetWindowPosition(int x, int y) #endif } -// Set monitor for the current window (fullscreen mode) +// Set monitor for the current window void SetWindowMonitor(int monitor) { #if defined(PLATFORM_DESKTOP) @@ -1670,10 +1670,34 @@ void SetWindowMonitor(int monitor) if ((monitor >= 0) && (monitor < monitorCount)) { - TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); + if (CORE.Window.fullscreen) + { + TRACELOG(LOG_INFO, "GLFW: Selected fullscreen monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); - const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); - glfwSetWindowMonitor(CORE.Window.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate); + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + glfwSetWindowMonitor(CORE.Window.handle, monitors[monitor], 0, 0, mode->width, mode->height, mode->refreshRate); + } + else + { + TRACELOG(LOG_INFO, "GLFW: Selected monitor: [%i] %s", monitor, glfwGetMonitorName(monitors[monitor])); + + const int screenWidth = CORE.Window.screen.width; + const int screenHeight = CORE.Window.screen.height; + int monitorWorkareaX = 0; + int monitorWorkareaY = 0; + int monitorWorkareaWidth = 0; + int monitorWorkareaHeight = 0; + glfwGetMonitorWorkarea(monitors[monitor], &monitorWorkareaX, &monitorWorkareaY, &monitorWorkareaWidth, &monitorWorkareaHeight); + + // If the screen size is larger than the monitor workarea, anchor it on the top left corner, otherwise, center it + if ((screenWidth >= monitorWorkareaWidth) || (screenHeight >= monitorWorkareaHeight)) glfwSetWindowPos(CORE.Window.handle, monitorWorkareaX, monitorWorkareaY); + else + { + const int x = monitorWorkareaX + (monitorWorkareaWidth*0.5f) - (screenWidth*0.5f); + const int y = monitorWorkareaY + (monitorWorkareaHeight*0.5f) - (screenHeight*0.5f); + glfwSetWindowPos(CORE.Window.handle, x, y); + } + } } else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); #endif From 44659b7ba8bd6d517d75fac8675ecd026f713240 Mon Sep 17 00:00:00 2001 From: ndytts Date: Sat, 29 Jul 2023 07:46:04 -0400 Subject: [PATCH 0174/1350] Fix android soname in src/Makefile (#3211) --- src/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Makefile b/src/Makefile index 4f742a259..e9b68cd93 100644 --- a/src/Makefile +++ b/src/Makefile @@ -516,7 +516,7 @@ ifeq ($(PLATFORM),PLATFORM_DRM) endif endif ifeq ($(PLATFORM),PLATFORM_ANDROID) - LDFLAGS += -Wl,-soname,libraylib.$(API_VERSION).so -Wl,--exclude-libs,libatomic.a + LDFLAGS += -Wl,-soname,libraylib.$(RAYLIB_API_VERSION).so -Wl,--exclude-libs,libatomic.a LDFLAGS += -Wl,--build-id -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now -Wl,--warn-shared-textrel -Wl,--fatal-warnings # Force linking of library module to define symbol LDFLAGS += -u ANativeActivity_onCreate From d3ea64983212f7451a9cfbf644da8a5c43dbc706 Mon Sep 17 00:00:00 2001 From: Alexander Klingenbeck Date: Sun, 30 Jul 2023 19:08:59 +0200 Subject: [PATCH 0175/1350] Update BINDINGS.md to include rayjs (#3212) --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 7fc88113e..ec66d4ab5 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -75,7 +75,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | hare-raylib | **auto** | [Hare](https://harelang.org/) | Zlib | https://git.sr.ht/~evantj/hare-raylib | | raylib-sunder | **auto** | [Sunder](https://github.com/ashn-dot-dev/sunder) | 0BSD | https://github.com/ashn-dot-dev/raylib-sunder | | rayed-bqn | **auto** | [BQN](https://mlochbaum.github.io/BQN/) | MIT | https://github.com/Brian-ED/rayed-bqn | - +| rayjs | 4.6-dev | [QuickJS](https://bellard.org/quickjs/) | MIT | https://github.com/mode777/rayjs | ### Utility Wrapers These are utility wrappers for specific languages, they are not required to use raylib in the language but may adapt the raylib API to be more inline with the language's pardigm. From 4fd40f0333e48ad0b10a853eacc367b8ac8cc02f Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Tue, 1 Aug 2023 05:42:50 -0300 Subject: [PATCH 0176/1350] Fixes GetCurrentMonitor() detection inconsistency issue (#3215) --- src/rcore.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 66a914ee7..282fec17e 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1835,20 +1835,24 @@ int GetCurrentMonitor(void) int mx = 0; int my = 0; - int width = 0; - int height = 0; - monitor = monitors[i]; - glfwGetMonitorWorkarea(monitor, &mx, &my, &width, &height); - - if ((x >= mx) && - (x < (mx + width)) && - (y >= my) && - (y < (my + height))) + glfwGetMonitorPos(monitor, &mx, &my); + const GLFWvidmode *mode = glfwGetVideoMode(monitor); + if (mode) { - index = i; - break; + const int width = mode->width; + const int height = mode->height; + + if ((x >= mx) && + (x < (mx + width)) && + (y >= my) && + (y < (my + height))) + { + index = i; + break; + } } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); } } } From e7664d5684f4b7c487d2a08645f23a1d0485f9e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20C=2E=20Fran=C3=A7a?= Date: Tue, 1 Aug 2023 05:46:40 -0300 Subject: [PATCH 0177/1350] build change (#3214) ref.: https://github.com/ziglang/zig/pull/16446 --- src/build.zig | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/build.zig b/src/build.zig index 64480a4f1..28a5d727d 100644 --- a/src/build.zig +++ b/src/build.zig @@ -18,7 +18,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built // No GLFW required on PLATFORM_DRM if (!options.platform_drm) { - raylib.addIncludePath(srcdir ++ "/external/glfw/include"); + raylib.addIncludePath(.{ .path = srcdir ++ "/external/glfw/include" }); } raylib.addCSourceFiles(&.{ @@ -36,9 +36,9 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built if (options.raygui) { _ = gen_step.add(srcdir ++ "/raygui.c", "#define RAYGUI_IMPLEMENTATION\n#include \"raygui.h\"\n"); - raylib.addCSourceFile(srcdir ++ "/raygui.c", raylib_flags); - raylib.addIncludePath(srcdir); - raylib.addIncludePath(srcdir ++ "/../../raygui/src"); + raylib.addCSourceFile(.{ .file = .{ .path = srcdir ++ "/raygui.c" }, .flags = raylib_flags }); + raylib.addIncludePath(.{ .path = srcdir }); + raylib.addIncludePath(.{ .path = srcdir ++ "/../../raygui/src" }); } switch (target.getOsTag()) { @@ -47,7 +47,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built raylib.linkSystemLibrary("winmm"); raylib.linkSystemLibrary("gdi32"); raylib.linkSystemLibrary("opengl32"); - raylib.addIncludePath("external/glfw/deps/mingw"); + raylib.addIncludePath(.{ .path = "external/glfw/deps/mingw" }); raylib.defineCMacro("PLATFORM_DESKTOP", null); }, @@ -59,7 +59,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built raylib.linkSystemLibrary("dl"); raylib.linkSystemLibrary("m"); raylib.linkSystemLibrary("X11"); - raylib.addIncludePath("/usr/include"); + raylib.addIncludePath(.{ .path = "/usr/include" }); raylib.defineCMacro("PLATFORM_DESKTOP", null); } else { @@ -71,7 +71,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built raylib.linkSystemLibrary("rt"); raylib.linkSystemLibrary("m"); raylib.linkSystemLibrary("dl"); - raylib.addIncludePath("/usr/include/libdrm"); + raylib.addIncludePath(.{ .path = "/usr/include/libdrm" }); raylib.defineCMacro("PLATFORM_DRM", null); raylib.defineCMacro("GRAPHICS_API_OPENGL_ES2", null); @@ -125,7 +125,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built var dir = std.fs.openDirAbsolute(cache_include, std.fs.Dir.OpenDirOptions{ .access_sub_paths = true, .no_follow = true }) catch @panic("No emscripten cache. Generate it!"); dir.close(); - raylib.addIncludePath(cache_include); + raylib.addIncludePath(.{ .path = cache_include }); }, else => { @panic("Unsupported OS"); From b60c69181663a5e7f2cb0ec04c5e7c388cdcad3e Mon Sep 17 00:00:00 2001 From: Joseph Montanez Date: Wed, 2 Aug 2023 00:05:02 -0700 Subject: [PATCH 0178/1350] Update BINDINGS.md (#3217) Updated Raylib-PHP to support Raylib 4.5 --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index ec66d4ab5..00672d9bf 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -56,7 +56,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | raylibpyctbg | **4.5** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylibpyctbg | | raylib-py | **4.5** | [Python](https://www.python.org/) | MIT | https://github.com/overdev/raylib-py | | raylib-python-ctypes | 4.6-dev | [Python](https://www.python.org/) | MIT | https://github.com/sDos280/raylib-python-ctypes | -| raylib-php | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/joseph-montanez/raylib-php | +| raylib-php | **4.5** | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/joseph-montanez/raylib-php | | raylib-phpcpp | 3.5 | [PHP](https://en.wikipedia.org/wiki/PHP) | Zlib | https://github.com/oraoto/raylib-phpcpp | | raylibr | 4.0 | [R](https://www.r-project.org) | MIT | https://github.com/jeroenjanssens/raylibr | | raylib-ffi | 4.5 | [Rust](https://www.rust-lang.org/) | GPLv3 | https://github.com/ewpratten/raylib-ffi | From 04678bc585b51bf218310387b048e846ca4a7718 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Wed, 2 Aug 2023 10:12:38 -0700 Subject: [PATCH 0179/1350] int math done with floats causes warnings. (#3218) --- src/rcore.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 282fec17e..fc87e7354 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1693,8 +1693,8 @@ void SetWindowMonitor(int monitor) if ((screenWidth >= monitorWorkareaWidth) || (screenHeight >= monitorWorkareaHeight)) glfwSetWindowPos(CORE.Window.handle, monitorWorkareaX, monitorWorkareaY); else { - const int x = monitorWorkareaX + (monitorWorkareaWidth*0.5f) - (screenWidth*0.5f); - const int y = monitorWorkareaY + (monitorWorkareaHeight*0.5f) - (screenHeight*0.5f); + const int x = monitorWorkareaX + (monitorWorkareaWidth/2) - (screenWidth/2); + const int y = monitorWorkareaY + (monitorWorkareaHeight/2) - (screenHeight/2); glfwSetWindowPos(CORE.Window.handle, x, y); } } From 464e714a2e256e7501239e0a5a12a2e9ee4e7f04 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Wed, 2 Aug 2023 14:16:35 -0300 Subject: [PATCH 0180/1350] Adds BORDERLESS_WINDOWED_MODE for PLATFORM_DESKTOP (#3216) --- src/raylib.h | 2 ++ src/rcore.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/src/raylib.h b/src/raylib.h index b3f17bd53..45a6120f5 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -526,6 +526,7 @@ typedef enum { FLAG_WINDOW_TRANSPARENT = 0x00000010, // Set to allow transparent framebuffer FLAG_WINDOW_HIGHDPI = 0x00002000, // Set to support HighDPI FLAG_WINDOW_MOUSE_PASSTHROUGH = 0x00004000, // Set to support mouse passthrough, only supported when FLAG_WINDOW_UNDECORATED + FLAG_BORDERLESS_WINDOWED_MODE = 0x00008000, // Set to run program in borderless windowed mode FLAG_MSAA_4X_HINT = 0x00000020, // Set to try enabling MSAA 4X FLAG_INTERLACED_HINT = 0x00010000 // Set to try enabling interlaced video format (for V3D) } ConfigFlags; @@ -947,6 +948,7 @@ RLAPI bool IsWindowState(unsigned int flag); // Check if on RLAPI void SetWindowState(unsigned int flags); // Set window configuration state using flags (only PLATFORM_DESKTOP) RLAPI void ClearWindowState(unsigned int flags); // Clear window configuration state flags RLAPI void ToggleFullscreen(void); // Toggle window state: fullscreen/windowed (only PLATFORM_DESKTOP) +RLAPI void ToggleBorderlessWindowed(void); // Toggle window state: borderless windowed (only PLATFORM_DESKTOP) RLAPI void MaximizeWindow(void); // Set window state: maximized, if resizable (only PLATFORM_DESKTOP) RLAPI void MinimizeWindow(void); // Set window state: minimized, if resizable (only PLATFORM_DESKTOP) RLAPI void RestoreWindow(void); // Set window state: not minimized/maximized (only PLATFORM_DESKTOP) diff --git a/src/rcore.c b/src/rcore.c index fc87e7354..d5addad48 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -412,6 +412,8 @@ typedef struct CoreData { Size render; // Framebuffer width and height (render area, including black bars if required) Point renderOffset; // Offset from render area (must be divided by 2) Matrix screenScale; // Matrix to scale screen (framebuffer rendering) + Point previousPosition; // Previous screen position (required on borderless windowed toggle) + Size previousScreen; // Previous screen size (required on borderless windowed toggle) char **dropFilepaths; // Store dropped files paths pointers (provided by GLFW) unsigned int dropFileCount; // Count dropped files strings @@ -1324,6 +1326,82 @@ void ToggleFullscreen(void) #endif } +// Toggle borderless windowed mode (only PLATFORM_DESKTOP) +void ToggleBorderlessWindowed(void) +{ +#if defined(PLATFORM_DESKTOP) + // Leave fullscreen before attempting to set borderless windowed mode and get screen position from it + bool wasOnFullscreen = false; + if (CORE.Window.fullscreen) + { + CORE.Window.previousPosition = CORE.Window.position; + ToggleFullscreen(); + wasOnFullscreen = true; + } + + const int monitor = GetCurrentMonitor(); + int monitorCount; + GLFWmonitor **monitors = glfwGetMonitors(&monitorCount); + if ((monitor >= 0) && (monitor < monitorCount)) + { + const GLFWvidmode *mode = glfwGetVideoMode(monitors[monitor]); + if (mode) + { + if (!IsWindowState(FLAG_BORDERLESS_WINDOWED_MODE)) + { + // Store screen position and size + // NOTE: If it was on fullscreen, screen position was already stored, so skip setting it here + if (!wasOnFullscreen) glfwGetWindowPos(CORE.Window.handle, &CORE.Window.previousPosition.x, &CORE.Window.previousPosition.y); + CORE.Window.previousScreen = CORE.Window.screen; + + // Set undecorated and topmost modes and flags + glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_FALSE); + CORE.Window.flags |= FLAG_WINDOW_UNDECORATED; + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_TRUE); + CORE.Window.flags |= FLAG_WINDOW_TOPMOST; + + // Get monitor position and size + int monitorPosX = 0; + int monitorPosY = 0; + glfwGetMonitorPos(monitors[monitor], &monitorPosX, &monitorPosY); + const int monitorWidth = mode->width; + const int monitorHeight = mode->height; + glfwSetWindowSize(CORE.Window.handle, monitorWidth, monitorHeight); + + // Set screen position and size + glfwSetWindowPos(CORE.Window.handle, monitorPosX, monitorPosY); + glfwSetWindowSize(CORE.Window.handle, monitorWidth, monitorHeight); + + // Refocus window + glfwFocusWindow(CORE.Window.handle); + + CORE.Window.flags |= FLAG_BORDERLESS_WINDOWED_MODE; + } + else + { + // Remove topmost and undecorated modes and flags + glfwSetWindowAttrib(CORE.Window.handle, GLFW_FLOATING, GLFW_FALSE); + CORE.Window.flags &= ~FLAG_WINDOW_TOPMOST; + glfwSetWindowAttrib(CORE.Window.handle, GLFW_DECORATED, GLFW_TRUE); + CORE.Window.flags &= ~FLAG_WINDOW_UNDECORATED; + + // Return previous screen size and position + // NOTE: The order matters here, it must set size first, then set position, otherwise the screen will be positioned incorrectly + glfwSetWindowSize(CORE.Window.handle, CORE.Window.previousScreen.width, CORE.Window.previousScreen.height); + glfwSetWindowPos(CORE.Window.handle, CORE.Window.previousPosition.x, CORE.Window.previousPosition.y); + + // Refocus window + glfwFocusWindow(CORE.Window.handle); + + CORE.Window.flags &= ~FLAG_BORDERLESS_WINDOWED_MODE; + } + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find video mode for selected monitor"); + } + else TRACELOG(LOG_WARNING, "GLFW: Failed to find selected monitor"); +#endif +} + // Set window state: maximized, if resizable (only PLATFORM_DESKTOP) void MaximizeWindow(void) { @@ -1373,6 +1451,13 @@ void SetWindowState(unsigned int flags) CORE.Window.flags |= FLAG_VSYNC_HINT; } + // State change: FLAG_BORDERLESS_WINDOWED_MODE + // NOTE: This must be handled before FLAG_FULLSCREEN_MODE because ToggleBorderlessWindowed() needs to get some fullscreen values if fullscreen is running + if (((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) != (flags & FLAG_BORDERLESS_WINDOWED_MODE)) && ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)) + { + ToggleBorderlessWindowed(); // NOTE: Window state flag updated inside function + } + // State change: FLAG_FULLSCREEN_MODE if ((CORE.Window.flags & FLAG_FULLSCREEN_MODE) != (flags & FLAG_FULLSCREEN_MODE)) { @@ -1483,6 +1568,13 @@ void ClearWindowState(unsigned int flags) CORE.Window.flags &= ~FLAG_VSYNC_HINT; } + // State change: FLAG_BORDERLESS_WINDOWED_MODE + // NOTE: This must be handled before FLAG_FULLSCREEN_MODE because ToggleBorderlessWindowed() needs to get some fullscreen values if fullscreen is running + if (((CORE.Window.flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0) && ((flags & FLAG_BORDERLESS_WINDOWED_MODE) > 0)) + { + ToggleBorderlessWindowed(); // NOTE: Window state flag updated inside function + } + // State change: FLAG_FULLSCREEN_MODE if (((CORE.Window.flags & FLAG_FULLSCREEN_MODE) > 0) && ((flags & FLAG_FULLSCREEN_MODE) > 0)) { From 5b4aaf4eb1b848d023b3bac0ce7a8e34faab9ba1 Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Thu, 3 Aug 2023 05:29:45 -0300 Subject: [PATCH 0181/1350] Adds CMake option for SUPPORT_CUSTOM_FRAME_CONTROL (#3221) --- CMakeOptions.txt | 1 + cmake/CompileDefinitions.cmake | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CMakeOptions.txt b/CMakeOptions.txt index c643427c6..cc57a851f 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -46,6 +46,7 @@ cmake_dependent_option(SUPPORT_BUSY_WAIT_LOOP "Use busy wait loop for timing syn cmake_dependent_option(SUPPORT_EVENTS_WAITING "Wait for events passively (sleeping while no events) instead of polling them actively every frame" OFF CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_WINMM_HIGHRES_TIMER "Setting a higher resolution can improve the accuracy of time-out intervals in wait functions" OFF CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_COMPRESSION_API "Support for compression API" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_CUSTOM_FRAME_CONTROL "Enabling this flag allows manual control of the frame processes, use at your own risk" OFF CUSTOMIZE_BUILD OFF) # rshapes.c cmake_dependent_option(SUPPORT_QUADS_DRAW_MODE "Use QUADS instead of TRIANGLES for drawing when possible. Some lines-based shapes could still use lines" ON CUSTOMIZE_BUILD ON) diff --git a/cmake/CompileDefinitions.cmake b/cmake/CompileDefinitions.cmake index 749222455..689f98ac4 100644 --- a/cmake/CompileDefinitions.cmake +++ b/cmake/CompileDefinitions.cmake @@ -28,6 +28,7 @@ if (${CUSTOMIZE_BUILD}) define_if("raylib" SUPPORT_EVENTS_WAITING) define_if("raylib" SUPPORT_WINMM_HIGHRES_TIMER) define_if("raylib" SUPPORT_COMPRESSION_API) + define_if("raylib" SUPPORT_CUSTOM_FRAME_CONTROL) define_if("raylib" SUPPORT_QUADS_DRAW_MODE) define_if("raylib" SUPPORT_IMAGE_EXPORT) define_if("raylib" SUPPORT_IMAGE_GENERATION) @@ -69,17 +70,17 @@ if (${CUSTOMIZE_BUILD}) else () target_compile_definitions("raylib" PUBLIC "MAX_FILEPATH_LENGTH=512") endif () - + target_compile_definitions("raylib" PUBLIC "MAX_GAMEPADS=4") target_compile_definitions("raylib" PUBLIC "MAX_GAMEPAD_AXIS=8") target_compile_definitions("raylib" PUBLIC "MAX_GAMEPAD_BUTTONS=32") target_compile_definitions("raylib" PUBLIC "MAX_TOUCH_POINTS=10") target_compile_definitions("raylib" PUBLIC "MAX_KEY_PRESSED_QUEUE=16") - + target_compile_definitions("raylib" PUBLIC "STORAGE_DATA_FILE=\"storage.data\"") target_compile_definitions("raylib" PUBLIC "MAX_CHAR_PRESSED_QUEUE=16") target_compile_definitions("raylib" PUBLIC "MAX_DECOMPRESSION_SIZE=64") - + if (${GRAPHICS} MATCHES "GRAPHICS_API_OPENGL_33" OR ${GRAPHICS} MATCHES "GRAPHICS_API_OPENGL_11") target_compile_definitions("raylib" PUBLIC "DEFAULT_BATCH_BUFFER_ELEMENTS=8192") elseif (${GRAPHICS} MATCHES "GRAPHICS_API_OPENGL_ES2") From d3058fe58972b80684591237c6358104f9e28ba0 Mon Sep 17 00:00:00 2001 From: vitopigno <103512727+VitusVeit@users.noreply.github.com> Date: Fri, 4 Aug 2023 12:04:19 +0200 Subject: [PATCH 0182/1350] [CORE] Support for SetWindowTitle and InitWindow for web (#3222) * Update raylib.h Changed SetWindowTitle's description * Update rcore.c SetWindowTitle now works on web * Update rcore.c InitWindow title now works with web platform too. --- src/raylib.h | 2 +- src/rcore.c | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index 45a6120f5..8b5a80700 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -954,7 +954,7 @@ RLAPI void MinimizeWindow(void); // Set window RLAPI void RestoreWindow(void); // Set window state: not minimized/maximized (only PLATFORM_DESKTOP) RLAPI void SetWindowIcon(Image image); // Set icon for window (single image, RGBA 32bit, only PLATFORM_DESKTOP) RLAPI void SetWindowIcons(Image *images, int count); // Set icon for window (multiple images, RGBA 32bit, only PLATFORM_DESKTOP) -RLAPI void SetWindowTitle(const char *title); // Set title for window (only PLATFORM_DESKTOP) +RLAPI void SetWindowTitle(const char *title); // Set title for window (only PLATFORM_DESKTOP and PLATFORM_WEB) RLAPI void SetWindowPosition(int x, int y); // Set window position on screen (only PLATFORM_DESKTOP) RLAPI void SetWindowMonitor(int monitor); // Set monitor for the current window RLAPI void SetWindowMinSize(int width, int height); // Set window minimum dimensions (for FLAG_WINDOW_RESIZABLE) diff --git a/src/rcore.c b/src/rcore.c index d5addad48..344e851f3 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -1736,13 +1736,16 @@ void SetWindowIcons(Image *images, int count) #endif } -// Set title for window (only PLATFORM_DESKTOP) +// Set title for window (only PLATFORM_DESKTOP and PLATFORM_WEB) void SetWindowTitle(const char *title) { CORE.Window.title = title; #if defined(PLATFORM_DESKTOP) glfwSetWindowTitle(CORE.Window.handle, title); #endif +#if defined(PLATFORM_WEB) + emscripten_set_window_title(title); +#endif } // Set window position on screen (windowed mode) @@ -4433,6 +4436,11 @@ static bool InitGraphicsDevice(int width, int height) return false; } +// glfwCreateWindow title doesn't work with emscripten. +#if defined(PLATFORM_WEB) + emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " "); +#endif + // Set window callback events glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! #if !defined(PLATFORM_WEB) From 601cadbae6a7561d2114de891f374a2d9f2f497e Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Fri, 4 Aug 2023 07:40:10 -0700 Subject: [PATCH 0183/1350] [AUDIO] Add a function to make an alias of a sound and share it's sample data (#3219) * Add a function to clone a sound and share data with another sound. * rename items based on feedback * PR Feedback, use custom unload for sound alias, not variant of normal sound unloading --- src/raudio.c | 37 +++++++++++++++++++++++++++++++++++++ src/raylib.h | 2 ++ 2 files changed, 39 insertions(+) diff --git a/src/raudio.c b/src/raudio.c index 5e2ccf996..5f84688d9 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -916,6 +916,32 @@ Sound LoadSoundFromWave(Wave wave) return sound; } +// Clone sound from existing sound data, clone does not own wave data +// Wave data must +// NOTE: Wave data must be unallocated manually and will be shared across all clones +Sound LoadSoundAlias(Sound source) +{ + Sound sound = { 0 }; + + if (source.stream.buffer->data != NULL) + { + AudioBuffer* audioBuffer = LoadAudioBuffer(AUDIO_DEVICE_FORMAT, AUDIO_DEVICE_CHANNELS, AUDIO.System.device.sampleRate, source.frameCount, AUDIO_BUFFER_USAGE_STATIC); + if (audioBuffer == NULL) + { + TRACELOG(LOG_WARNING, "SOUND: Failed to create buffer"); + return sound; // early return to avoid dereferencing the audioBuffer null pointer + } + audioBuffer->data = source.stream.buffer->data; + sound.frameCount = source.frameCount; + sound.stream.sampleRate = AUDIO.System.device.sampleRate; + sound.stream.sampleSize = 32; + sound.stream.channels = AUDIO_DEVICE_CHANNELS; + sound.stream.buffer = audioBuffer; + } + + return sound; +} + // Checks if a sound is ready bool IsSoundReady(Sound sound) { @@ -940,6 +966,17 @@ void UnloadSound(Sound sound) //TRACELOG(LOG_INFO, "SOUND: Unloaded sound data from RAM"); } +void UnloadSoundAlias(Sound alias) +{ + // untrack and unload just the sound buffer, not the sample data, it is shared with the source for the alias + if (alias.stream.buffer != NULL) + { + ma_data_converter_uninit(&alias.stream.buffer->converter, NULL); + UntrackAudioBuffer(alias.stream.buffer); + RL_FREE(alias.stream.buffer); + } +} + // Update sound buffer with new data void UpdateSound(Sound sound, const void *data, int sampleCount) { diff --git a/src/raylib.h b/src/raylib.h index 8b5a80700..a49e19a57 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1533,10 +1533,12 @@ RLAPI Wave LoadWaveFromMemory(const char *fileType, const unsigned char *fileDat RLAPI bool IsWaveReady(Wave wave); // Checks if wave data is ready RLAPI Sound LoadSound(const char *fileName); // Load sound from file RLAPI Sound LoadSoundFromWave(Wave wave); // Load sound from wave data +RLAPI Sound LoadSoundAlias(Sound source); // Create a new sound that shares the same sample data as the source sound, does not own the sound data RLAPI bool IsSoundReady(Sound sound); // Checks if a sound is ready RLAPI void UpdateSound(Sound sound, const void *data, int sampleCount); // Update sound buffer with new data RLAPI void UnloadWave(Wave wave); // Unload wave data RLAPI void UnloadSound(Sound sound); // Unload sound +RLAPI void UnloadSoundAlias(Sound alias); // Unload a sound alias (does not deallocate sample data) RLAPI bool ExportWave(Wave wave, const char *fileName); // Export wave data to file, returns true on success RLAPI bool ExportWaveAsCode(Wave wave, const char *fileName); // Export wave sample data to code (.h), returns true on success From 62f5382d560bde7fca0565dedfebcdded34ade96 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Fri, 4 Aug 2023 08:14:04 -0700 Subject: [PATCH 0184/1350] [AUDIO] Add an example of how to use LoadSoundAlias (#3223) * Add a function to clone a sound and share data with another sound. * rename items based on feedback * PR Feedback, use custom unload for sound alias, not variant of normal sound unloading * sound_multi example --- examples/Makefile | 1 + examples/audio/audio_sound_multi.c | 87 ++++ .../VS2022/examples/audio_sound_multi.vcxproj | 390 ++++++++++++++++++ projects/VS2022/raylib.sln | 35 +- 4 files changed, 497 insertions(+), 16 deletions(-) create mode 100644 examples/audio/audio_sound_multi.c create mode 100644 projects/VS2022/examples/audio_sound_multi.vcxproj diff --git a/examples/Makefile b/examples/Makefile index 1a4d5f06c..ff5098948 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -534,6 +534,7 @@ AUDIO = \ audio/audio_music_stream \ audio/audio_raw_stream \ audio/audio_sound_loading \ + audio/audio_sound_multi \ audio/audio_stream_effects \ audio/audio_mixed_processor diff --git a/examples/audio/audio_sound_multi.c b/examples/audio/audio_sound_multi.c new file mode 100644 index 000000000..d5472efab --- /dev/null +++ b/examples/audio/audio_sound_multi.c @@ -0,0 +1,87 @@ +/******************************************************************************************* +* +* raylib [audio] example - Playing sound multiple times +* +* Example originally created with raylib 4.6 +* +* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified, +* BSD-like license that allows static linking with closed source software +* +* Copyright (c) 2023 Jeffery Myers (@JeffM2501) +* +********************************************************************************************/ + +#include "raylib.h" + +#define MAX_SOUNDS 10 +Sound soundArray[MAX_SOUNDS] = { 0 }; +int currentSound; + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +int main(void) +{ + // Initialization + //-------------------------------------------------------------------------------------- + const int screenWidth = 800; + const int screenHeight = 450; + + InitWindow(screenWidth, screenHeight, "raylib [audio] example - playing sound multiple times"); + + InitAudioDevice(); // Initialize audio device + + // load the sound list + soundArray[0] = LoadSound("resources/sound.wav"); // Load WAV audio file into the first slot as the 'source' sound + // this sound owns the sample data + for (int i = 1; i < MAX_SOUNDS; i++) + { + soundArray[i] = LoadSoundAlias(soundArray[0]); // Load an alias of the sound into slots 1-9. These do not own the sound data, but can be played + } + currentSound = 0; // set the sound list to the start + + SetTargetFPS(60); // Set our game to run at 60 frames-per-second + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!WindowShouldClose()) // Detect window close button or ESC key + { + // Update + //---------------------------------------------------------------------------------- + if (IsKeyPressed(KEY_SPACE)) + { + PlaySound(soundArray[currentSound]); // play the next open sound slot + currentSound++; // increment the sound slot + if (currentSound >= MAX_SOUNDS) // if the sound slot is out of bounds, go back to 0 + currentSound = 0; + + // Note: a better way would be to look at the list for the first sound that is not playing and use that slot + } + + //---------------------------------------------------------------------------------- + + // Draw + //---------------------------------------------------------------------------------- + BeginDrawing(); + + ClearBackground(RAYWHITE); + + DrawText("Press SPACE to PLAY a WAV sound!", 200, 180, 20, LIGHTGRAY); + + EndDrawing(); + //---------------------------------------------------------------------------------- + } + + // De-Initialization + //-------------------------------------------------------------------------------------- + for (int i = 1; i < MAX_SOUNDS; i++) + UnloadSoundAlias(soundArray[i]); // Unload sound aliases + UnloadSound(soundArray[0]); // Unload source sound data + + CloseAudioDevice(); // Close audio device + + CloseWindow(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + + return 0; +} \ No newline at end of file diff --git a/projects/VS2022/examples/audio_sound_multi.vcxproj b/projects/VS2022/examples/audio_sound_multi.vcxproj new file mode 100644 index 000000000..5bfed78fb --- /dev/null +++ b/projects/VS2022/examples/audio_sound_multi.vcxproj @@ -0,0 +1,390 @@ + + + + + Debug.DLL + Win32 + + + Debug.DLL + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release.DLL + Win32 + + + Release.DLL + x64 + + + Release + Win32 + + + Release + x64 + + + + {F81C5819-85B4-4D2E-B6DC-104A7634461B} + Win32Proj + audio_sound_multi + 10.0 + audio_sound_multi + + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + true + $(DefaultPlatformToolset) + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + Application + false + $(DefaultPlatformToolset) + true + Unicode + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + true + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + false + $(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)\ + $(SolutionDir)\build\$(ProjectName)\obj\$(Platform)\$(Configuration)\ + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + $(SolutionDir)..\..\examples\audio + WindowsLocalDebugger + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + /FS %(AdditionalOptions) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;PLATFORM_DESKTOP;%(PreprocessorDefinitions) + CompileAsC + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + + + Console + true + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + Copy Debug DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + Level3 + + + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions);PLATFORM_DESKTOP + $(SolutionDir)..\..\src;%(AdditionalIncludeDirectories) + CompileAsC + true + + + Console + true + true + true + raylib.lib;opengl32.lib;kernel32.lib;user32.lib;gdi32.lib;winmm.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies) + $(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\ + + + xcopy /y /d "$(SolutionDir)\build\raylib\bin\$(Platform)\$(Configuration)\raylib.dll" "$(SolutionDir)\build\$(ProjectName)\bin\$(Platform)\$(Configuration)" + + + Copy Release DLL to output directory + + + + + + + + + + + {e89d61ac-55de-4482-afd4-df7242ebc859} + + + + + + \ No newline at end of file diff --git a/projects/VS2022/raylib.sln b/projects/VS2022/raylib.sln index f58f2ed75..173637554 100644 --- a/projects/VS2022/raylib.sln +++ b/projects/VS2022/raylib.sln @@ -269,6 +269,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_write_depth", "exam EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shaders_hybrid_render", "examples\shaders_hybrid_render.vcxproj", "{3755E9F4-CB48-4EC3-B561-3B85964EBDEF}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_sound_multi", "examples\audio_sound_multi.vcxproj", "{F81C5819-85B4-4D2E-B6DC-104A7634461B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug.DLL|x64 = Debug.DLL|x64 @@ -377,22 +379,6 @@ Global {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release|x64.Build.0 = Release|x64 {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release|x86.ActiveCfg = Release|Win32 {E6784F91-4E4E-4956-A079-73FAB1AC7BE6}.Release|x86.Build.0 = Release|Win32 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Debug|x64.ActiveCfg = Debug|x64 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Debug|x64.Build.0 = Debug|x64 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Debug|x86.ActiveCfg = Debug|Win32 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Debug|x86.Build.0 = Debug|Win32 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Release.DLL|x64.Build.0 = Release.DLL|x64 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Release.DLL|x86.Build.0 = Release.DLL|Win32 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Release|x64.ActiveCfg = Release|x64 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Release|x64.Build.0 = Release|x64 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Release|x86.ActiveCfg = Release|Win32 - {9875B0C8-3893-43F8-88B2-4BAAAE3E8E6F}.Release|x86.Build.0 = Release|Win32 {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 {BFB22AB2-041B-4A1B-80C0-1D4BE410C8A9}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 @@ -2277,6 +2263,22 @@ Global {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|x64.Build.0 = Release|x64 {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|x86.ActiveCfg = Release|Win32 {3755E9F4-CB48-4EC3-B561-3B85964EBDEF}.Release|x86.Build.0 = Release|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug.DLL|x64.ActiveCfg = Debug.DLL|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug.DLL|x64.Build.0 = Debug.DLL|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug.DLL|x86.ActiveCfg = Debug.DLL|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug.DLL|x86.Build.0 = Debug.DLL|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug|x64.ActiveCfg = Debug|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug|x64.Build.0 = Debug|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug|x86.ActiveCfg = Debug|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Debug|x86.Build.0 = Debug|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release.DLL|x64.ActiveCfg = Release.DLL|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release.DLL|x64.Build.0 = Release.DLL|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release.DLL|x86.ActiveCfg = Release.DLL|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release.DLL|x86.Build.0 = Release.DLL|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x64.ActiveCfg = Release|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x64.Build.0 = Release|x64 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x86.ActiveCfg = Release|Win32 + {F81C5819-85B4-4D2E-B6DC-104A7634461B}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2413,6 +2415,7 @@ Global {6D9E00D8-2893-45E4-9363-3F7F61D416BD} = {AF5BEC5C-1F2B-4DA8-B12D-D09FE569237C} {70B35F59-AFC2-4D8F-8833-5314D2047A81} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} {3755E9F4-CB48-4EC3-B561-3B85964EBDEF} = {5317807F-61D4-4E0F-B6DC-2D9F12621ED9} + {F81C5819-85B4-4D2E-B6DC-104A7634461B} = {CC132A4D-D081-4C26-BFB9-AB11984054F8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E926C768-6307-4423-A1EC-57E95B1FAB29} From 6094869e3e845e90e1e8ae41b98e889fb3e13e78 Mon Sep 17 00:00:00 2001 From: Ray Date: Fri, 4 Aug 2023 18:14:47 +0200 Subject: [PATCH 0185/1350] Fix material loading #3126 --- src/external/tinyobj_loader_c.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/external/tinyobj_loader_c.h b/src/external/tinyobj_loader_c.h index 6d34d25f7..502a55a7e 100644 --- a/src/external/tinyobj_loader_c.h +++ b/src/external/tinyobj_loader_c.h @@ -746,7 +746,7 @@ static int tinyobj_parse_and_index_mtl_file(tinyobj_material_t **materials_out, (*materials_out) = NULL; (*num_materials_out) = 0; - fp = fopen(filename, "r"); + fp = fopen(filename, "rt"); if (!fp) { fprintf(stderr, "TINYOBJ: Error reading file '%s': %s (%d)\n", filename, strerror(errno), errno); return TINYOBJ_ERROR_FILE_OPERATION; From dc621ca388077a8bd814c8d399dea09efbfc25ce Mon Sep 17 00:00:00 2001 From: Nikolas Date: Sat, 5 Aug 2023 23:16:26 +0200 Subject: [PATCH 0186/1350] Support 16-Bit HDR textures (#3220) * Support 16-Bit HDR textures * Fix build on emscripten * Move helper functions --- src/raylib.h | 3 + src/rlgl.h | 39 ++++++--- src/rtextures.c | 208 +++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 238 insertions(+), 12 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index a49e19a57..f7b0df66b 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -812,6 +812,9 @@ typedef enum { PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) + PIXELFORMAT_UNCOMPRESSED_R16, // 16 bpp (1 channel - half float) + PIXELFORMAT_UNCOMPRESSED_R16G16B16, // 16*3 bpp (3 channels - half float) + PIXELFORMAT_UNCOMPRESSED_R16G16B16A16, // 16*4 bpp (4 channels - half float) PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp diff --git a/src/rlgl.h b/src/rlgl.h index 470295b0a..717fa59fb 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -419,6 +419,9 @@ typedef enum { RL_PIXELFORMAT_UNCOMPRESSED_R32, // 32 bpp (1 channel - float) RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32, // 32*3 bpp (3 channels - float) RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32, // 32*4 bpp (4 channels - float) + RL_PIXELFORMAT_UNCOMPRESSED_R16, // 16 bpp (1 channel - half float) + RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16, // 16*3 bpp (3 channels - half float) + RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16, // 16*4 bpp (4 channels - half float) RL_PIXELFORMAT_COMPRESSED_DXT1_RGB, // 4 bpp (no alpha) RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA, // 4 bpp (1 bit alpha) RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA, // 8 bpp @@ -1006,6 +1009,7 @@ typedef struct rlglData { bool texDepth; // Depth textures supported (GL_ARB_depth_texture, GL_OES_depth_texture) bool texDepthWebGL; // Depth textures supported WebGL specific (GL_WEBGL_depth_texture) bool texFloat32; // float textures support (32 bit per channel) (GL_OES_texture_float) + bool texFloat16; // half float textures support (16 bit per channel) (GL_OES_texture_half_float) bool texCompDXT; // DDS texture compression support (GL_EXT_texture_compression_s3tc, GL_WEBGL_compressed_texture_s3tc, GL_WEBKIT_WEBGL_compressed_texture_s3tc) bool texCompETC1; // ETC1 texture compression support (GL_OES_compressed_ETC1_RGB8_texture, GL_WEBGL_compressed_texture_etc1) bool texCompETC2; // ETC2/EAC texture compression support (GL_ARB_ES3_compatibility) @@ -2189,6 +2193,7 @@ void rlLoadExtensions(void *loader) RLGL.ExtSupported.instancing = (GLAD_GL_EXT_draw_instanced && GLAD_GL_ARB_instanced_arrays); RLGL.ExtSupported.texNPOT = GLAD_GL_ARB_texture_non_power_of_two; RLGL.ExtSupported.texFloat32 = GLAD_GL_ARB_texture_float; + RLGL.ExtSupported.texFloat16 = GLAD_GL_ARB_texture_float; RLGL.ExtSupported.texDepth = GLAD_GL_ARB_depth_texture; RLGL.ExtSupported.maxDepthBits = 32; RLGL.ExtSupported.texAnisoFilter = GLAD_GL_EXT_texture_filter_anisotropic; @@ -2200,6 +2205,7 @@ void rlLoadExtensions(void *loader) RLGL.ExtSupported.instancing = true; RLGL.ExtSupported.texNPOT = true; RLGL.ExtSupported.texFloat32 = true; + RLGL.ExtSupported.texFloat16 = true; RLGL.ExtSupported.texDepth = true; RLGL.ExtSupported.maxDepthBits = 32; RLGL.ExtSupported.texAnisoFilter = true; @@ -2224,6 +2230,7 @@ void rlLoadExtensions(void *loader) RLGL.ExtSupported.instancing = true; RLGL.ExtSupported.texNPOT = true; RLGL.ExtSupported.texFloat32 = true; + RLGL.ExtSupported.texFloat16 = true; RLGL.ExtSupported.texDepth = true; RLGL.ExtSupported.texDepthWebGL = true; RLGL.ExtSupported.maxDepthBits = 24; @@ -2320,6 +2327,7 @@ void rlLoadExtensions(void *loader) // Check texture float support if (strcmp(extList[i], (const char *)"GL_OES_texture_float") == 0) RLGL.ExtSupported.texFloat32 = true; + if (strcmp(extList[i], (const char *)"GL_OES_texture_half_float") == 0) RLGL.ExtSupported.texFloat16 = true; // Check depth texture support if (strcmp(extList[i], (const char *)"GL_OES_depth_texture") == 0) RLGL.ExtSupported.texDepth = true; @@ -3163,13 +3171,9 @@ unsigned int rlLoadTextureCubemap(const void *data, int size, int format) { if (format < RL_PIXELFORMAT_COMPRESSED_DXT1_RGB) { - if (format == RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32) - { - // Instead of using a sized internal texture format (GL_RGB16F, GL_RGB32F), we let the driver to choose the better format for us (GL_RGB) - if (RLGL.ExtSupported.texFloat32) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, size, size, 0, GL_RGB, GL_FLOAT, NULL); - else TRACELOG(RL_LOG_WARNING, "TEXTURES: Cubemap requested format not supported"); - } - else if ((format == RL_PIXELFORMAT_UNCOMPRESSED_R32) || (format == RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32)) TRACELOG(RL_LOG_WARNING, "TEXTURES: Cubemap requested format not supported"); + if ((format == RL_PIXELFORMAT_UNCOMPRESSED_R32) || (format == RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32) + || (format == RL_PIXELFORMAT_UNCOMPRESSED_R16) || (format == RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16)) + TRACELOG(RL_LOG_WARNING, "TEXTURES: Cubemap requested format not supported"); else glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, glInternalFormat, size, size, 0, glFormat, glType, NULL); } else TRACELOG(RL_LOG_WARNING, "TEXTURES: Empty cubemap creation does not support compressed format"); @@ -3256,10 +3260,16 @@ void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_R32F_EXT; *glFormat = GL_RED_EXT; *glType = GL_FLOAT; break; case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB32F_EXT; *glFormat = GL_RGB; *glType = GL_FLOAT; break; case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA32F_EXT; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_R16F_EXT; *glFormat = GL_RED_EXT; *glType = GL_HALF_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGB16F_EXT; *glFormat = GL_RGB; *glType = GL_HALF_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGBA16F_EXT; *glFormat = GL_RGBA; *glType = GL_HALF_FLOAT; break; #else - case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float - case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float - case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + case RL_PIXELFORMAT_UNCOMPRESSED_R16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_HALF_FLOAT_OES; break; // NOTE: Requires extension OES_texture_half_float + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_HALF_FLOAT_OES; break; // NOTE: Requires extension OES_texture_half_float + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_HALF_FLOAT_OES; break; // NOTE: Requires extension OES_texture_half_float #endif #endif #elif defined(GRAPHICS_API_OPENGL_33) @@ -3273,6 +3283,9 @@ void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_R32F; *glFormat = GL_RED; *glType = GL_FLOAT; break; case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB32F; *glFormat = GL_RGB; *glType = GL_FLOAT; break; case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA32F; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_R16F; *glFormat = GL_RED; *glType = GL_HALF_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGB16F; *glFormat = GL_RGB; *glType = GL_HALF_FLOAT; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGBA16F; *glFormat = GL_RGBA; *glType = GL_HALF_FLOAT; break; #endif #if !defined(GRAPHICS_API_OPENGL_11) case RL_PIXELFORMAT_COMPRESSED_DXT1_RGB: if (RLGL.ExtSupported.texCompDXT) *glInternalFormat = GL_COMPRESSED_RGB_S3TC_DXT1_EXT; break; @@ -4480,6 +4493,9 @@ const char *rlGetPixelFormatName(unsigned int format) case RL_PIXELFORMAT_UNCOMPRESSED_R32: return "R32"; break; // 32 bpp (1 channel - float) case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: return "R32G32B32"; break; // 32*3 bpp (3 channels - float) case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: return "R32G32B32A32"; break; // 32*4 bpp (4 channels - float) + case RL_PIXELFORMAT_UNCOMPRESSED_R16: return "R16"; break; // 16 bpp (1 channel - half float) + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16: return "R16G16B16"; break; // 16*3 bpp (3 channels - half float) + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: return "R16G16B16A16"; break; // 16*4 bpp (4 channels - half float) case RL_PIXELFORMAT_COMPRESSED_DXT1_RGB: return "DXT1_RGB"; break; // 4 bpp (no alpha) case RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA: return "DXT1_RGBA"; break; // 4 bpp (1 bit alpha) case RL_PIXELFORMAT_COMPRESSED_DXT3_RGBA: return "DXT3_RGBA"; break; // 8 bpp @@ -4721,6 +4737,9 @@ static int rlGetPixelDataSize(int width, int height, int format) case RL_PIXELFORMAT_UNCOMPRESSED_R32: bpp = 32; break; case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 32*3; break; case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16: bpp = 16; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16: bpp = 16*3; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: bpp = 16*4; break; case RL_PIXELFORMAT_COMPRESSED_DXT1_RGB: case RL_PIXELFORMAT_COMPRESSED_DXT1_RGBA: case RL_PIXELFORMAT_COMPRESSED_ETC1_RGB: diff --git a/src/rtextures.c b/src/rtextures.c index c13e331cd..501e3fdea 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -223,6 +223,8 @@ extern void LoadFontDefault(void); // [Module: text] Loads default font //---------------------------------------------------------------------------------- // Module specific Functions Declaration //---------------------------------------------------------------------------------- +static float HalfToFloat(unsigned short x); +static unsigned short FloatToHalf(float x); static Vector4 *LoadImageDataNormalized(Image image); // Load pixel data from image as Vector4 array (float normalized) //---------------------------------------------------------------------------------- @@ -1286,6 +1288,40 @@ void ImageFormat(Image *image, int newFormat) ((float *)image->data)[i + 3] = pixels[k].w; } } break; + case PIXELFORMAT_UNCOMPRESSED_R16: + { + // WARNING: Image is converted to GRAYSCALE equivalent 16bit + + image->data = (unsigned short *)RL_MALLOC(image->width*image->height*sizeof(unsigned short)); + + for (int i = 0; i < image->width*image->height; i++) + { + ((unsigned short *)image->data)[i] = FloatToHalf((float)(pixels[i].x*0.299f + pixels[i].y*0.587f + pixels[i].z*0.114f)); + } + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16: + { + image->data = (unsigned short *)RL_MALLOC(image->width*image->height*3*sizeof(unsigned short)); + + for (int i = 0, k = 0; i < image->width*image->height*3; i += 3, k++) + { + ((unsigned short *)image->data)[i] = FloatToHalf(pixels[k].x); + ((unsigned short *)image->data)[i + 1] = FloatToHalf(pixels[k].y); + ((unsigned short *)image->data)[i + 2] = FloatToHalf(pixels[k].z); + } + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + { + image->data = (unsigned short *)RL_MALLOC(image->width*image->height*4*sizeof(unsigned short)); + + for (int i = 0, k = 0; i < image->width*image->height*4; i += 4, k++) + { + ((unsigned short *)image->data)[i] = FloatToHalf(pixels[k].x); + ((unsigned short *)image->data)[i + 1] = FloatToHalf(pixels[k].y); + ((unsigned short *)image->data)[i + 2] = FloatToHalf(pixels[k].z); + ((unsigned short *)image->data)[i + 3] = FloatToHalf(pixels[k].w); + } + } break; default: break; } @@ -1652,6 +1688,19 @@ void ImageAlphaClear(Image *image, Color color, float threshold) } } } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + { + for (int i = 3; i < image->width*image->height*4; i += 4) + { + if (HalfToFloat(((unsigned short *)image->data)[i]) <= threshold) + { + ((unsigned short *)image->data)[i - 3] = FloatToHalf((float)color.r/255.0f); + ((unsigned short *)image->data)[i - 2] = FloatToHalf((float)color.g/255.0f); + ((unsigned short *)image->data)[i - 1] = FloatToHalf((float)color.b/255.0f); + ((unsigned short *)image->data)[i] = FloatToHalf((float)color.a/255.0f); + } + } + } break; default: break; } } @@ -2493,6 +2542,10 @@ Color *LoadImageColors(Image image) (image.format == PIXELFORMAT_UNCOMPRESSED_R32G32B32) || (image.format == PIXELFORMAT_UNCOMPRESSED_R32G32B32A32)) TRACELOG(LOG_WARNING, "IMAGE: Pixel format converted from 32bit to 8bit per channel"); + if ((image.format == PIXELFORMAT_UNCOMPRESSED_R16) || + (image.format == PIXELFORMAT_UNCOMPRESSED_R16G16B16) || + (image.format == PIXELFORMAT_UNCOMPRESSED_R16G16B16A16)) TRACELOG(LOG_WARNING, "IMAGE: Pixel format converted from 16bit to 8bit per channel"); + for (int i = 0, k = 0; i < image.width*image.height; i++) { switch (image.format) @@ -2588,6 +2641,32 @@ Color *LoadImageColors(Image image) k += 4; } break; + case PIXELFORMAT_UNCOMPRESSED_R16: + { + pixels[i].r = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k])*255.0f); + pixels[i].g = 0; + pixels[i].b = 0; + pixels[i].a = 255; + + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16: + { + pixels[i].r = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k])*255.0f); + pixels[i].g = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k + 1])*255.0f); + pixels[i].b = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k + 2])*255.0f); + pixels[i].a = 255; + + k += 3; + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + { + pixels[i].r = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k])*255.0f); + pixels[i].g = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k])*255.0f); + pixels[i].b = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k])*255.0f); + pixels[i].a = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[k])*255.0f); + + k += 4; + } break; default: break; } } @@ -2799,6 +2878,30 @@ Color GetImageColor(Image image, int x, int y) color.b = (unsigned char)(((float *)image.data)[(y*image.width + x)*4]*255.0f); color.a = (unsigned char)(((float *)image.data)[(y*image.width + x)*4]*255.0f); + } break; + case PIXELFORMAT_UNCOMPRESSED_R16: + { + color.r = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[y*image.width + x])*255.0f); + color.g = 0; + color.b = 0; + color.a = 255; + + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16: + { + color.r = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*3])*255.0f); + color.g = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*3 + 1])*255.0f); + color.b = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*3 + 2])*255.0f); + color.a = 255; + + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + { + color.r = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*4])*255.0f); + color.g = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*4])*255.0f); + color.b = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*4])*255.0f); + color.a = (unsigned char)(HalfToFloat(((unsigned short *)image.data)[(y*image.width + x)*4])*255.0f); + } break; default: TRACELOG(LOG_WARNING, "Compressed image format does not support color reading"); break; } @@ -2938,6 +3041,34 @@ void ImageDrawPixel(Image *dst, int x, int y, Color color) ((float *)dst->data)[(y*dst->width + x)*4 + 2] = coln.z; ((float *)dst->data)[(y*dst->width + x)*4 + 3] = coln.w; + } break; + case PIXELFORMAT_UNCOMPRESSED_R16: + { + // NOTE: Calculate grayscale equivalent color (normalized to 32bit) + Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f }; + + ((unsigned short*)dst->data)[y*dst->width + x] = FloatToHalf(coln.x*0.299f + coln.y*0.587f + coln.z*0.114f); + + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16: + { + // NOTE: Calculate R32G32B32 equivalent color (normalized to 32bit) + Vector3 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f }; + + ((unsigned short *)dst->data)[(y*dst->width + x)*3] = FloatToHalf(coln.x); + ((unsigned short *)dst->data)[(y*dst->width + x)*3 + 1] = FloatToHalf(coln.y); + ((unsigned short *)dst->data)[(y*dst->width + x)*3 + 2] = FloatToHalf(coln.z); + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + { + // NOTE: Calculate R32G32B32A32 equivalent color (normalized to 32bit) + Vector4 coln = { (float)color.r/255.0f, (float)color.g/255.0f, (float)color.b/255.0f, (float)color.a/255.0f }; + + ((unsigned short *)dst->data)[(y*dst->width + x)*4] = FloatToHalf(coln.x); + ((unsigned short *)dst->data)[(y*dst->width + x)*4 + 1] = FloatToHalf(coln.y); + ((unsigned short *)dst->data)[(y*dst->width + x)*4 + 2] = FloatToHalf(coln.z); + ((unsigned short *)dst->data)[(y*dst->width + x)*4 + 3] = FloatToHalf(coln.w); + } break; default: break; } @@ -3234,7 +3365,7 @@ void ImageDraw(Image *dst, Image src, Rectangle srcRec, Rectangle dstRec, Color // [-] GetPixelColor(): Get Vector4 instead of Color, easier for ColorAlphaBlend() // [ ] Support f32bit channels drawing - // TODO: Support PIXELFORMAT_UNCOMPRESSED_R32, PIXELFORMAT_UNCOMPRESSED_R32G32B32, PIXELFORMAT_UNCOMPRESSED_R32G32B32A32 + // TODO: Support PIXELFORMAT_UNCOMPRESSED_R32, PIXELFORMAT_UNCOMPRESSED_R32G32B32, PIXELFORMAT_UNCOMPRESSED_R32G32B32A32 and 16-bit equivalents Color colSrc, colDst, blend; bool blendRequired = true; @@ -4366,6 +4497,33 @@ Color GetPixelColor(void *srcPtr, int format) color.b = (unsigned char)(((float *)srcPtr)[2]*255.0f); color.a = (unsigned char)(((float *)srcPtr)[3]*255.0f); + } break; + case PIXELFORMAT_UNCOMPRESSED_R16: + { + // NOTE: Pixel normalized float value is converted to [0..255] + color.r = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[0])*255.0f); + color.g = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[0])*255.0f); + color.b = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[0])*255.0f); + color.a = 255; + + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16: + { + // NOTE: Pixel normalized float value is converted to [0..255] + color.r = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[0])*255.0f); + color.g = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[1])*255.0f); + color.b = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[2])*255.0f); + color.a = 255; + + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + { + // NOTE: Pixel normalized float value is converted to [0..255] + color.r = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[0])*255.0f); + color.g = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[1])*255.0f); + color.b = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[2])*255.0f); + color.a = (unsigned char)(HalfToFloat(((unsigned short *)srcPtr)[3])*255.0f); + } break; default: break; } @@ -4473,6 +4631,9 @@ int GetPixelDataSize(int width, int height, int format) case PIXELFORMAT_UNCOMPRESSED_R32: bpp = 32; break; case PIXELFORMAT_UNCOMPRESSED_R32G32B32: bpp = 32*3; break; case PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: bpp = 32*4; break; + case PIXELFORMAT_UNCOMPRESSED_R16: bpp = 16; break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16: bpp = 16*3; break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: bpp = 16*4; break; case PIXELFORMAT_COMPRESSED_DXT1_RGB: case PIXELFORMAT_COMPRESSED_DXT1_RGBA: case PIXELFORMAT_COMPRESSED_ETC1_RGB: @@ -4503,6 +4664,24 @@ int GetPixelDataSize(int width, int height, int format) //---------------------------------------------------------------------------------- // Module specific Functions Definition //---------------------------------------------------------------------------------- +// From https://stackoverflow.com/questions/1659440/32-bit-to-16-bit-floating-point-conversion/60047308#60047308 + +static float HalfToFloat(unsigned short x) { + const unsigned int e = (x&0x7C00)>>10; // exponent + const unsigned int m = (x&0x03FF)<<13; // mantissa + const float fm = (float)m; + const unsigned int v = (*(unsigned int*)&fm)>>23; // evil log2 bit hack to count leading zeros in denormalized format + const unsigned int r = (x&0x8000)<<16 | (e!=0)*((e+112)<<23|m) | ((e==0)&(m!=0))*((v-37)<<23|((m<<(150-v))&0x007FE000)); // sign : normalized : denormalized + return *(float*)&r; +} + +static unsigned short FloatToHalf(float x) { + const unsigned int b = (*(unsigned int*)&x)+0x00001000; // round-to-nearest-even: add last bit after truncated mantissa + const unsigned int e = (b&0x7F800000)>>23; // exponent + const unsigned int m = b&0x007FFFFF; // mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding + return (b&0x80000000)>>16 | (e>112)*((((e-112)<<10)&0x7C00)|m>>13) | ((e<113)&(e>101))*((((0x007FF000+m)>>(125-e))+1)>>1) | (e>143)*0x7FFF; // sign : normalized : denormalized : saturate +} + // Get pixel data from image as Vector4 array (float normalized) static Vector4 *LoadImageDataNormalized(Image image) { @@ -4605,7 +4784,32 @@ static Vector4 *LoadImageDataNormalized(Image image) pixels[i].w = ((float *)image.data)[k + 3]; k += 4; - } + } break; + case PIXELFORMAT_UNCOMPRESSED_R16: + { + pixels[i].x = HalfToFloat(((unsigned short *)image.data)[k]); + pixels[i].y = 0.0f; + pixels[i].z = 0.0f; + pixels[i].w = 1.0f; + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16: + { + pixels[i].x = HalfToFloat(((unsigned short *)image.data)[k]); + pixels[i].y = HalfToFloat(((unsigned short *)image.data)[k + 1]); + pixels[i].z = HalfToFloat(((unsigned short *)image.data)[k + 2]); + pixels[i].w = 1.0f; + + k += 3; + } break; + case PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: + { + pixels[i].x = HalfToFloat(((unsigned short *)image.data)[k]); + pixels[i].y = HalfToFloat(((unsigned short *)image.data)[k + 1]); + pixels[i].z = HalfToFloat(((unsigned short *)image.data)[k + 2]); + pixels[i].w = HalfToFloat(((unsigned short *)image.data)[k + 3]); + + k += 4; + } break; default: break; } } From b82217eaaaabd4540a595958970b78f08cd62844 Mon Sep 17 00:00:00 2001 From: yujiri8 Date: Sun, 6 Aug 2023 03:25:37 -0400 Subject: [PATCH 0187/1350] Tweak build.zig to work with cross-compiling (#3225) --- src/build.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/build.zig b/src/build.zig index 28a5d727d..9aab4812a 100644 --- a/src/build.zig +++ b/src/build.zig @@ -59,6 +59,7 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built raylib.linkSystemLibrary("dl"); raylib.linkSystemLibrary("m"); raylib.linkSystemLibrary("X11"); + raylib.addLibraryPath(.{ .path = "/usr/lib" }); raylib.addIncludePath(.{ .path = "/usr/include" }); raylib.defineCMacro("PLATFORM_DESKTOP", null); From c9864d8ac1345819ce5520da578f96dffae4e967 Mon Sep 17 00:00:00 2001 From: Le Juez Victor <90587919+Bigfoot71@users.noreply.github.com> Date: Sun, 6 Aug 2023 22:04:20 +0200 Subject: [PATCH 0188/1350] Fixed Android app black screen issue when reopening after incomplete closing (#3227) * Fixed black screen issue when resuming the app on Android Partly explained here: https://github.com/raysan5/raylib/issues/3127 * Fix APP_CMD_TERM_WINDOW for Android --- src/rcore.c | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 344e851f3..732cc7e47 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -720,8 +720,8 @@ void android_main(struct android_app *app) char arg0[] = "raylib"; // NOTE: argv[] are mutable CORE.Android.app = app; - // NOTE: We get the main return for exit() - int ret = main(1, (char *[]) { arg0, NULL }); + // NOTE: Return from main is ignored + (void)main(1, (char *[]) { arg0, NULL }); // Request to end the native activity ANativeActivity_finish(app->activity); @@ -731,19 +731,13 @@ void android_main(struct android_app *app) int pollEvents = 0; // Waiting for application events before complete finishing - while (!CORE.Android.app->destroyRequested) + while (!app->destroyRequested) { while ((pollResult = ALooper_pollAll(0, NULL, &pollEvents, (void **)&CORE.Android.source)) >= 0) { - if (CORE.Android.source != NULL) CORE.Android.source->process(CORE.Android.app, CORE.Android.source); + if (CORE.Android.source != NULL) CORE.Android.source->process(app, CORE.Android.source); } } - - // WARNING: Make sure you free resources properly and no other process is running from Java code or other. - // NOTE: You can use JNI to call a NativeLoader method (which will call finish() from the UI thread) - // to handle the full close from Java, without using exit(0) like here. - - exit(ret); // Close the application directly, without going through Java } // NOTE: Add this to header (if apps really need it) @@ -5897,21 +5891,28 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) case APP_CMD_TERM_WINDOW: { // Dettach OpenGL context and destroy display surface - // NOTE 1: Detaching context before destroying display surface avoids losing our resources (textures, shaders, VBOs...) - // NOTE 2: In some cases (too many context loaded), OS could unload context automatically... :( - eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - eglDestroySurface(CORE.Window.device, CORE.Window.surface); + // NOTE 1: This case is used when the user exits the app without closing it. We detach the context to ensure everything is recoverable upon resuming. + // NOTE 2: Detaching context before destroying display surface avoids losing our resources (textures, shaders, VBOs...) + // NOTE 3: In some cases (too many context loaded), OS could unload context automatically... :( + if (CORE.Window.device != EGL_NO_DISPLAY) + { + eglMakeCurrent(CORE.Window.device, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); + + if (CORE.Window.surface != EGL_NO_SURFACE) + { + eglDestroySurface(CORE.Window.device, CORE.Window.surface); + CORE.Window.surface = EGL_NO_SURFACE; + } + + CORE.Android.contextRebindRequired = true; + } + // If 'CORE.Window.device' is already set to 'EGL_NO_DISPLAY' + // this means that the user has already called 'CloseWindow()' - CORE.Android.contextRebindRequired = true; } break; case APP_CMD_SAVE_STATE: break; case APP_CMD_STOP: break; - case APP_CMD_DESTROY: - { - // NOTE 1: Call ANativeActivity_finish again to free resources unconditionally. - // NOTE 2: You can deallocate other things that are NativeActivity related here. - ANativeActivity_finish(CORE.Android.app->activity); - } break; + case APP_CMD_DESTROY: break; case APP_CMD_CONFIG_CHANGED: { //AConfiguration_fromAssetManager(CORE.Android.app->config, CORE.Android.app->activity->assetManager); From db55bed72b45c97616f0cfaf26448475f80174d5 Mon Sep 17 00:00:00 2001 From: mohad12211 <51754973+mohad12211@users.noreply.github.com> Date: Mon, 7 Aug 2023 21:51:36 +0400 Subject: [PATCH 0189/1350] fix: check if ctrl modifier is among the currently set modifiers (#3230) --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 732cc7e47..97d010a9f 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -5609,7 +5609,7 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i if ((key == GLFW_KEY_F12) && (action == GLFW_PRESS)) { #if defined(SUPPORT_GIF_RECORDING) - if (mods == GLFW_MOD_CONTROL) + if (mods & GLFW_MOD_CONTROL) { if (gifRecording) { From bef818e210f8fe83a0b5b9d80d6d2001654f07e2 Mon Sep 17 00:00:00 2001 From: Nikolas Date: Tue, 8 Aug 2023 20:09:22 +0200 Subject: [PATCH 0190/1350] Fix build for OpenGL 2.1, where half floats are part of an extension (#3233) --- src/rlgl.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/rlgl.h b/src/rlgl.h index 717fa59fb..3756e5ac7 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -3267,11 +3267,17 @@ void rlGetGlTextureFormats(int format, unsigned int *glInternalFormat, unsigned case RL_PIXELFORMAT_UNCOMPRESSED_R32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float case RL_PIXELFORMAT_UNCOMPRESSED_R32G32B32A32: if (RLGL.ExtSupported.texFloat32) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_FLOAT; break; // NOTE: Requires extension OES_texture_float + #if defined(GRAPHICS_API_OPENGL_21) + case RL_PIXELFORMAT_UNCOMPRESSED_R16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_HALF_FLOAT_ARB; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_HALF_FLOAT_ARB; break; + case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_HALF_FLOAT_ARB; break; + #else // defined(GRAPHICS_API_OPENGL_ES2) case RL_PIXELFORMAT_UNCOMPRESSED_R16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_LUMINANCE; *glFormat = GL_LUMINANCE; *glType = GL_HALF_FLOAT_OES; break; // NOTE: Requires extension OES_texture_half_float case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGB; *glFormat = GL_RGB; *glType = GL_HALF_FLOAT_OES; break; // NOTE: Requires extension OES_texture_half_float case RL_PIXELFORMAT_UNCOMPRESSED_R16G16B16A16: if (RLGL.ExtSupported.texFloat16) *glInternalFormat = GL_RGBA; *glFormat = GL_RGBA; *glType = GL_HALF_FLOAT_OES; break; // NOTE: Requires extension OES_texture_half_float #endif #endif + #endif #elif defined(GRAPHICS_API_OPENGL_33) case RL_PIXELFORMAT_UNCOMPRESSED_GRAYSCALE: *glInternalFormat = GL_R8; *glFormat = GL_RED; *glType = GL_UNSIGNED_BYTE; break; case RL_PIXELFORMAT_UNCOMPRESSED_GRAY_ALPHA: *glInternalFormat = GL_RG8; *glFormat = GL_RG; *glType = GL_UNSIGNED_BYTE; break; From 03ecf2202e61532bebaffc3b49941261bf967614 Mon Sep 17 00:00:00 2001 From: RadsammyT <32146976+RadsammyT@users.noreply.github.com> Date: Tue, 8 Aug 2023 14:10:40 -0400 Subject: [PATCH 0191/1350] Fix typos in rcore.c comments (#3234) * Fix typos in rcore.c comments thought i corrected "dettach" Fix typos in rcore.c comments * 'fordward' > forward --- src/rcore.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 97d010a9f..c28834987 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3694,7 +3694,7 @@ unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize) // Ref: https://github.com/raysan5/raylib/issues/686 void OpenURL(const char *url) { - // Security check to (aprtially) avoid malicious code on PLATFORM_WEB + // Security check to (partially) avoid malicious code on PLATFORM_WEB if (strchr(url, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided URL could be potentially malicious, avoid [\'] character"); else { @@ -4277,9 +4277,9 @@ static bool InitGraphicsDevice(int width, int height) glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Profiles Hint: Only 3.3 and above! // Values: GLFW_OPENGL_CORE_PROFILE, GLFW_OPENGL_ANY_PROFILE, GLFW_OPENGL_COMPAT_PROFILE #if defined(__APPLE__) - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); // OSX Requires fordward compatibility + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); // OSX Requires forward compatibility #else - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Fordward Compatibility Hint: Only 3.3 and above! + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_FALSE); // Forward Compatibility Hint: Only 3.3 and above! #endif //glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); // Request OpenGL DEBUG context } @@ -5890,7 +5890,7 @@ static void AndroidCommandCallback(struct android_app *app, int32_t cmd) } break; case APP_CMD_TERM_WINDOW: { - // Dettach OpenGL context and destroy display surface + // Detach OpenGL context and destroy display surface // NOTE 1: This case is used when the user exits the app without closing it. We detach the context to ensure everything is recoverable upon resuming. // NOTE 2: Detaching context before destroying display surface avoids losing our resources (textures, shaders, VBOs...) // NOTE 3: In some cases (too many context loaded), OS could unload context automatically... :( @@ -6575,7 +6575,7 @@ static void ConfigureEvdevDevice(char *device) { ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absBits)), absBits); - // Check for absolute movement support (usualy touchscreens, but also joysticks) + // Check for absolute movement support (usually touchscreens, but also joysticks) if (TEST_BIT(absBits, ABS_X) && TEST_BIT(absBits, ABS_Y)) { hasAbs = true; @@ -6589,7 +6589,7 @@ static void ConfigureEvdevDevice(char *device) worker->absRange.height = absinfo.maximum - absinfo.minimum; } - // Check for multiple absolute movement support (usualy multitouch touchscreens) + // Check for multiple absolute movement support (usually multitouch touchscreens) if (TEST_BIT(absBits, ABS_MT_POSITION_X) && TEST_BIT(absBits, ABS_MT_POSITION_Y)) { hasAbsMulti = true; @@ -6604,7 +6604,7 @@ static void ConfigureEvdevDevice(char *device) } } - // Check for relative movement support (usualy mouse) + // Check for relative movement support (usually mouse) if (TEST_BIT(evBits, EV_REL)) { ioctl(fd, EVIOCGBIT(EV_REL, sizeof(relBits)), relBits); @@ -6612,7 +6612,7 @@ static void ConfigureEvdevDevice(char *device) if (TEST_BIT(relBits, REL_X) && TEST_BIT(relBits, REL_Y)) hasRel = true; } - // Check for button support to determine the device type(usualy on all input devices) + // Check for button support to determine the device type(usually on all input devices) if (TEST_BIT(evBits, EV_KEY)) { ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keyBits)), keyBits); @@ -6740,7 +6740,7 @@ static void PollKeyboardEvents(void) // Keyboard button parsing if ((event.code >= 1) && (event.code <= 255)) //Keyboard keys appear for codes 1 to 255 { - keycode = keymapUS[event.code & 0xFF]; // The code we get is a scancode so we look up the apropriate keycode + keycode = keymapUS[event.code & 0xFF]; // The code we get is a scancode so we look up the appropriate keycode // Make sure we got a valid keycode if ((keycode > 0) && (keycode < sizeof(CORE.Input.Keyboard.currentKeyState))) @@ -6817,8 +6817,8 @@ static void *EventThread(void *arg) // Basic movement if (event.code == ABS_X) { - CORE.Input.Mouse.currentPosition.x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale acording to absRange - CORE.Input.Touch.position[0].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale acording to absRange + CORE.Input.Mouse.currentPosition.x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange + CORE.Input.Touch.position[0].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange touchAction = 2; // TOUCH_ACTION_MOVE gestureUpdate = true; @@ -6826,8 +6826,8 @@ static void *EventThread(void *arg) if (event.code == ABS_Y) { - CORE.Input.Mouse.currentPosition.y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale acording to absRange - CORE.Input.Touch.position[0].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale acording to absRange + CORE.Input.Mouse.currentPosition.y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange + CORE.Input.Touch.position[0].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange touchAction = 2; // TOUCH_ACTION_MOVE gestureUpdate = true; @@ -6838,12 +6838,12 @@ static void *EventThread(void *arg) if (event.code == ABS_MT_POSITION_X) { - if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale acording to absRange + if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].x = (event.value - worker->absRange.x)*CORE.Window.screen.width/worker->absRange.width; // Scale according to absRange } if (event.code == ABS_MT_POSITION_Y) { - if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale acording to absRange + if (worker->touchSlot < MAX_TOUCH_POINTS) CORE.Input.Touch.position[worker->touchSlot].y = (event.value - worker->absRange.y)*CORE.Window.screen.height/worker->absRange.height; // Scale according to absRange } if (event.code == ABS_MT_TRACKING_ID) From 42cfabc67069fd01ee8c0b00d58862e32f0942fb Mon Sep 17 00:00:00 2001 From: Ray Date: Wed, 9 Aug 2023 10:00:26 +0200 Subject: [PATCH 0192/1350] REVIEWED: Old pragma formating --- src/raudio.c | 27 +++++++++++++++------------ src/rmodels.c | 17 ++++++++--------- src/rtextures.c | 15 ++++++++------- 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 5f84688d9..31e21c6e4 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -227,17 +227,20 @@ typedef struct tagBITMAPINFOHEADER { #define QOA_MALLOC RL_MALLOC #define QOA_FREE RL_FREE -#if defined(_MSC_VER ) // par shapes has 2 warnings on windows, so disable them just fof this file -#pragma warning( push ) -#pragma warning( disable : 4018) -#pragma warning( disable : 4267) -#pragma warning( disable : 4244) -#endif - + #if defined(_MSC_VER) // Disable some MSVC warning + #pragma warning(push) + #pragma warning(disable : 4018) + #pragma warning(disable : 4267) + #pragma warning(disable : 4244) + #endif #define QOA_IMPLEMENTATION #include "external/qoa.h" // QOA loading and saving functions #include "external/qoaplay.c" // QOA stream playing helper functions + + #if defined(_MSC_VER) + #pragma warning(pop) // Disable MSVC warning suppression + #endif #endif #if defined(SUPPORT_FILEFORMAT_FLAC) @@ -254,16 +257,16 @@ typedef struct tagBITMAPINFOHEADER { #define JARXM_MALLOC RL_MALLOC #define JARXM_FREE RL_FREE - #if defined(_MSC_VER ) // jar_xm has warnings on windows, so disable them just for this file - #pragma warning( push ) - #pragma warning( disable : 4244) + #if defined(_MSC_VER) // Disable some MSVC warning + #pragma warning(push) + #pragma warning(disable : 4244) #endif #define JAR_XM_IMPLEMENTATION #include "external/jar_xm.h" // XM loading functions - #if defined(_MSC_VER ) - #pragma warning( pop ) + #if defined(_MSC_VER) + #pragma warning(pop) // Disable MSVC warning suppression #endif #endif diff --git a/src/rmodels.c b/src/rmodels.c index 8c7e088cb..294d13c5a 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -101,19 +101,18 @@ #define PAR_REALLOC(T, BUF, N) ((T*)RL_REALLOC(BUF, sizeof(T)*(N))) #define PAR_FREE RL_FREE -#if defined(_MSC_VER ) // par shapes has 2 warnings on windows, so disable them just fof this file -#pragma warning( push ) -#pragma warning( disable : 4244) -#pragma warning( disable : 4305) -#endif + #if defined(_MSC_VER) // Disable some MSVC warning + #pragma warning(push) + #pragma warning(disable : 4244) + #pragma warning(disable : 4305) + #endif #define PAR_SHAPES_IMPLEMENTATION #include "external/par_shapes.h" // Shapes 3d parametric generation -#if defined(_MSC_VER ) // disable MSVC warning suppression for par shapes -#pragma warning( pop ) -#endif - + #if defined(_MSC_VER) + #pragma warning(pop) // Disable MSVC warning suppression + #endif #endif #if defined(_WIN32) diff --git a/src/rtextures.c b/src/rtextures.c index 501e3fdea..8a3914776 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -162,16 +162,17 @@ #define QOI_MALLOC RL_MALLOC #define QOI_FREE RL_FREE -#if defined(_MSC_VER ) // qoi has warnings on windows, so disable them just for this file -#pragma warning( push ) -#pragma warning( disable : 4267) -#endif + #if defined(_MSC_VER) // Disable some MSVC warning + #pragma warning(push) + #pragma warning(disable : 4267) + #endif + #define QOI_IMPLEMENTATION #include "external/qoi.h" -#if defined(_MSC_VER ) -#pragma warning( pop ) -#endif + #if defined(_MSC_VER) + #pragma warning(pop) // Disable MSVC warning suppression + #endif #endif From 90f17499658275ace1e4d1bed1aa231e34ac38b6 Mon Sep 17 00:00:00 2001 From: ashn <60763262+ashn-dot-dev@users.noreply.github.com> Date: Wed, 9 Aug 2023 13:17:12 -0400 Subject: [PATCH 0193/1350] Ignore unused function warnings from external headers when compiling with GCC and Clang (#3235) --- src/rtext.c | 9 +++++++++ src/rtextures.c | 18 ++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/src/rtext.c b/src/rtext.c index a7d9903a8..d6aa6d736 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -71,12 +71,21 @@ #include // Required for: toupper(), tolower() [Used in TextToUpper(), TextToLower()] #if defined(SUPPORT_FILEFORMAT_TTF) + #if defined(__GNUC__) // GCC and Clang + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-function" + #endif + #define STB_RECT_PACK_IMPLEMENTATION #include "external/stb_rect_pack.h" // Required for: ttf font rectangles packaging #define STBTT_STATIC #define STB_TRUETYPE_IMPLEMENTATION #include "external/stb_truetype.h" // Required for: ttf font data reading + + #if defined(__GNUC__) // GCC and Clang + #pragma GCC diagnostic pop + #endif #endif //---------------------------------------------------------------------------------- diff --git a/src/rtextures.c b/src/rtextures.c index 8a3914776..f6e7e49c3 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -138,6 +138,11 @@ defined(SUPPORT_FILEFORMAT_PIC) || \ defined(SUPPORT_FILEFORMAT_PNM)) + #if defined(__GNUC__) // GCC and Clang + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-function" + #endif + #define STBI_MALLOC RL_MALLOC #define STBI_FREE RL_FREE #define STBI_REALLOC RL_REALLOC @@ -145,6 +150,10 @@ #define STB_IMAGE_IMPLEMENTATION #include "external/stb_image.h" // Required for: stbi_load_from_file() // NOTE: Used to read image data (multiple formats support) + + #if defined(__GNUC__) // GCC and Clang + #pragma GCC diagnostic pop + #endif #endif #if (defined(SUPPORT_FILEFORMAT_DDS) || \ @@ -153,9 +162,18 @@ defined(SUPPORT_FILEFORMAT_PVR) || \ defined(SUPPORT_FILEFORMAT_ASTC)) + #if defined(__GNUC__) // GCC and Clang + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-function" + #endif + #define RL_GPUTEX_IMPLEMENTATION #include "external/rl_gputex.h" // Required for: rl_load_xxx_from_memory() // NOTE: Used to read compressed textures data (multiple formats support) + + #if defined(__GNUC__) // GCC and Clang + #pragma GCC diagnostic pop + #endif #endif #if defined(SUPPORT_FILEFORMAT_QOI) From 0959f6ebf69f44d5bb0225cc94fd187ebedf8be5 Mon Sep 17 00:00:00 2001 From: RadsammyT <32146976+RadsammyT@users.noreply.github.com> Date: Wed, 9 Aug 2023 18:21:14 -0400 Subject: [PATCH 0194/1350] fix typos in rmodels, rshapes, rtext modules (#3236) --- src/rmodels.c | 8 ++++---- src/rshapes.c | 2 +- src/rtext.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index 294d13c5a..a512cfc6f 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -2457,7 +2457,7 @@ Mesh GenMeshCube(float width, float height, float length) // Platonic solids: par_shapes_mesh* par_shapes_create_tetrahedron(); // 4 sides polyhedron (pyramid) par_shapes_mesh* par_shapes_create_cube(); // 6 sides polyhedron (cube) -par_shapes_mesh* par_shapes_create_octahedron(); // 8 sides polyhedron (dyamond) +par_shapes_mesh* par_shapes_create_octahedron(); // 8 sides polyhedron (diamond) par_shapes_mesh* par_shapes_create_dodecahedron(); // 12 sides polyhedron par_shapes_mesh* par_shapes_create_icosahedron(); // 20 sides polyhedron */ @@ -4244,7 +4244,7 @@ static Model LoadIQM(const char *fileName) model.meshes[i].triangleCount = imesh[i].num_triangles; model.meshes[i].indices = RL_CALLOC(model.meshes[i].triangleCount*3, sizeof(unsigned short)); - // Animated verted data, what we actually process for rendering + // Animated vertex data, what we actually process for rendering // NOTE: Animated vertex should be re-uploaded to GPU (if not using GPU skinning) model.meshes[i].animVertices = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); model.meshes[i].animNormals = RL_CALLOC(model.meshes[i].vertexCount*3, sizeof(float)); @@ -4787,7 +4787,7 @@ static Model LoadGLTF(const char *fileName) RESTRICTIONS: - Only triangle meshes supported - - Vertex attibute types and formats supported: + - Vertex attribute types and formats supported: > Vertices (position): vec3: float > Normals: vec3: float > Texcoords: vec2: float @@ -5286,7 +5286,7 @@ static bool GetPoseAtTimeGLTF(cgltf_accessor *input, cgltf_accessor *output, flo Vector4 v2 = {tmp[0], tmp[1], tmp[2], tmp[3]}; Vector4 *r = data; - // Only v4 is for rotations, so we know it's a quat + // Only v4 is for rotations, so we know it's a quaternion *r = QuaternionSlerp(v1, v2, t); } diff --git a/src/rshapes.c b/src/rshapes.c index 86e998a6d..8b319ed15 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -99,7 +99,7 @@ void SetShapesTexture(Texture2D texture, Rectangle source) { // Reset texture to default pixel if required // WARNING: Shapes texture should be probably better validated, - // it can break the rendering of all shapes if missused + // it can break the rendering of all shapes if misused if ((texture.id == 0) || (source.width == 0) || (source.height == 0)) { texShapes = (Texture2D){ 1, 1, 1, 1, 7 }; diff --git a/src/rtext.c b/src/rtext.c index d6aa6d736..f5234ecf9 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1697,7 +1697,7 @@ const char *TextToPascal(const char *text) // WARNING: Allocated memory must be manually freed char *LoadUTF8(const int *codepoints, int length) { - // We allocate enough memory fo fit all possible codepoints + // We allocate enough memory to fit all possible codepoints // NOTE: 5 bytes for every codepoint should be enough char *text = (char *)RL_CALLOC(length*5, 1); const char *utf8 = NULL; From f1c31bee279285ad941acc1140caeb3dca86867f Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 10 Aug 2023 22:45:25 +0200 Subject: [PATCH 0195/1350] Fix #3177 #3109 --- src/rmodels.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/rmodels.c b/src/rmodels.c index a512cfc6f..ebb454e4a 100644 --- a/src/rmodels.c +++ b/src/rmodels.c @@ -110,7 +110,7 @@ #define PAR_SHAPES_IMPLEMENTATION #include "external/par_shapes.h" // Shapes 3d parametric generation - #if defined(_MSC_VER) + #if defined(_MSC_VER) #pragma warning(pop) // Disable MSVC warning suppression #endif #endif @@ -5576,7 +5576,7 @@ static Model LoadM3D(const char *fileName) m3dp_t *prop = NULL; unsigned int bytesRead = 0; unsigned char *fileData = LoadFileData(fileName, &bytesRead); - int i, j, k, l, n, mi = -2; + int i, j, k, l, n, mi = -2, vcolor = 0; if (fileData != NULL) { @@ -5606,10 +5606,13 @@ static Model LoadM3D(const char *fileName) } else { - model.meshCount = model.materialCount = 1; + model.meshCount = 1; model.materialCount = 0; TRACELOG(LOG_INFO, "MODEL: No materials, putting all meshes in a default material"); } + // We always need a default material, so we add +1 + model.materialCount++; + model.meshes = (Mesh *)RL_CALLOC(model.meshCount, sizeof(Mesh)); model.meshMaterial = (int *)RL_CALLOC(model.meshCount, sizeof(int)); model.materials = (Material *)RL_CALLOC(model.materialCount + 1, sizeof(Material)); @@ -5634,7 +5637,14 @@ static Model LoadM3D(const char *fileName) k++; mi = m3d->face[i].materialid; - for (j = i, l = 0; (j < (int)m3d->numface) && (mi == m3d->face[j].materialid); j++, l++); + // Only allocate colors VertexBuffer if there's a color vertex in the model for this material batch + // if all colors are fully transparent black for all verteces of this materal, then we assume no vertex colors + for (j = i, l = vcolor = 0; (j < (int)m3d->numface) && (mi == m3d->face[j].materialid); j++, l++) + { + if (!m3d->vertex[m3d->face[j].vertex[0]].color || + !m3d->vertex[m3d->face[j].vertex[1]].color || + !m3d->vertex[m3d->face[j].vertex[2]].color) vcolor = 1; + } model.meshes[k].vertexCount = l*3; model.meshes[k].triangleCount = l; @@ -5642,9 +5652,9 @@ static Model LoadM3D(const char *fileName) model.meshes[k].texcoords = (float *)RL_CALLOC(model.meshes[k].vertexCount*2, sizeof(float)); model.meshes[k].normals = (float *)RL_CALLOC(model.meshes[k].vertexCount*3, sizeof(float)); - // If no map is provided, we allocate storage for vertex colors - // M3D specs only consider vertex colors if no material is provided - if (mi != M3D_UNDEF) model.meshes[k].colors = RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); + // If no map is provided, or we have colors defined, we allocate storage for vertex colors + // M3D specs only consider vertex colors if no material is provided, however raylib uses both and mixes the colors + if ((mi == M3D_UNDEF) || vcolor) model.meshes[k].colors = RL_CALLOC(model.meshes[k].vertexCount*4, sizeof(unsigned char)); if (m3d->numbone && m3d->numskin) { From 93f59a6f5912aa07fcfc2268ce119da09dff458a Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 10 Aug 2023 22:47:17 +0200 Subject: [PATCH 0196/1350] Review tabs and trail-spaces --- src/raudio.c | 4 ++-- src/raymath.h | 6 +++--- src/rcore.c | 2 +- src/rtext.c | 4 ++-- src/rtextures.c | 16 ++++++++-------- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/raudio.c b/src/raudio.c index 31e21c6e4..9d1683a1f 100644 --- a/src/raudio.c +++ b/src/raudio.c @@ -237,7 +237,7 @@ typedef struct tagBITMAPINFOHEADER { #define QOA_IMPLEMENTATION #include "external/qoa.h" // QOA loading and saving functions #include "external/qoaplay.c" // QOA stream playing helper functions - + #if defined(_MSC_VER) #pragma warning(pop) // Disable MSVC warning suppression #endif @@ -920,7 +920,7 @@ Sound LoadSoundFromWave(Wave wave) } // Clone sound from existing sound data, clone does not own wave data -// Wave data must +// Wave data must // NOTE: Wave data must be unallocated manually and will be shared across all clones Sound LoadSoundAlias(Sound source) { diff --git a/src/raymath.h b/src/raymath.h index 9281691fa..86d2c8eb8 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -315,11 +315,11 @@ RMAPI float Vector2DistanceSqr(Vector2 v1, Vector2 v2) RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) { float result = 0.0f; - + float dot = v1.x*v2.x + v1.y*v2.y; float det = v1.x*v2.y - v1.y*v2.x; result = -atan2f(det, dot); - + return result; } @@ -329,7 +329,7 @@ RMAPI float Vector2Angle(Vector2 v1, Vector2 v2) RMAPI float Vector2LineAngle(Vector2 start, Vector2 end) { float result = 0.0f; - + result = atan2f(end.y - start.y, end.x - start.x); return result; diff --git a/src/rcore.c b/src/rcore.c index c28834987..7d8811b05 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -4434,7 +4434,7 @@ static bool InitGraphicsDevice(int width, int height) #if defined(PLATFORM_WEB) emscripten_set_window_title((CORE.Window.title != 0)? CORE.Window.title : " "); #endif - + // Set window callback events glfwSetWindowSizeCallback(CORE.Window.handle, WindowSizeCallback); // NOTE: Resizing not allowed by default! #if !defined(PLATFORM_WEB) diff --git a/src/rtext.c b/src/rtext.c index f5234ecf9..a66c58101 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -852,7 +852,7 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC RL_FREE(nodes); RL_FREE(context); } - + #if defined(SUPPORT_FONT_ATLAS_WHITE_REC) // Add a 3x3 white rectangle at the bottom-right corner of the generated atlas, // useful to use as the white texture to draw shapes with raylib, using this rectangle @@ -865,7 +865,7 @@ Image GenImageFontAtlas(const GlyphInfo *chars, Rectangle **charRecs, int glyphC k -= atlas.width; } #endif - + // Convert image data from GRAYSCALE to GRAY_ALPHA unsigned char *dataGrayAlpha = (unsigned char *)RL_MALLOC(atlas.width*atlas.height*sizeof(unsigned char)*2); // Two channels diff --git a/src/rtextures.c b/src/rtextures.c index f6e7e49c3..4b697f777 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -184,7 +184,7 @@ #pragma warning(push) #pragma warning(disable : 4267) #endif - + #define QOI_IMPLEMENTATION #include "external/qoi.h" @@ -3300,13 +3300,13 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color) { memcpy(pSrcPixel + x*bytesPerPixel, pSrcPixel, bytesPerPixel); } - - // Repeat the first row data for all other rows - int bytesPerRow = bytesPerPixel * (int)rec.width; - for (int y = 1; y < (int)rec.height; y++) - { - memcpy(pSrcPixel + (y*dst->width)*bytesPerPixel, pSrcPixel, bytesPerRow); - } + + // Repeat the first row data for all other rows + int bytesPerRow = bytesPerPixel * (int)rec.width; + for (int y = 1; y < (int)rec.height; y++) + { + memcpy(pSrcPixel + (y*dst->width)*bytesPerPixel, pSrcPixel, bytesPerRow); + } } // Draw rectangle lines within an image From c25b52b1b3f3e606ce2210e834c0e479aec7859e Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Fri, 11 Aug 2023 05:19:50 -0300 Subject: [PATCH 0197/1350] Fix rcamera.h issues (#3240) --- src/rcamera.h | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/rcamera.h b/src/rcamera.h index c5382684e..de169fc39 100644 --- a/src/rcamera.h +++ b/src/rcamera.h @@ -184,9 +184,10 @@ RLAPI Matrix GetCameraProjectionMatrix(Camera* camera, float aspect); //---------------------------------------------------------------------------------- #define CAMERA_MOVE_SPEED 0.09f #define CAMERA_ROTATION_SPEED 0.03f +#define CAMERA_PAN_SPEED 0.2f // Camera mouse movement sensitivity -#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f // TODO: it should be independant of framerate +#define CAMERA_MOUSE_MOVE_SENSITIVITY 0.003f // TODO: it should be independant of framerate #define CAMERA_MOUSE_SCROLL_SENSITIVITY 1.5f #define CAMERA_ORBITAL_SPEED 0.5f // Radians per second @@ -435,7 +436,7 @@ void UpdateCamera(Camera *camera, int mode) bool moveInWorldPlane = ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON)); bool rotateAroundTarget = ((mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL)); bool lockView = ((mode == CAMERA_FIRST_PERSON) || (mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL)); - bool rotateUp = (mode == CAMERA_FREE); + bool rotateUp = false; if (mode == CAMERA_ORBITAL) { @@ -458,10 +459,23 @@ void UpdateCamera(Camera *camera, int mode) // Camera movement if (!IsGamepadAvailable(0)) { - // Mouse/Keyboard support - CameraYaw(camera, -mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); - CameraPitch(camera, -mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); + // Camera pan (for CAMERA_FREE) + if ((mode == CAMERA_FREE) && (IsMouseButtonDown(MOUSE_BUTTON_MIDDLE))) + { + const Vector2 mouseDelta = GetMouseDelta(); + if (mouseDelta.x > 0.0f) CameraMoveRight(camera, CAMERA_PAN_SPEED, moveInWorldPlane); + if (mouseDelta.x < 0.0f) CameraMoveRight(camera, -CAMERA_PAN_SPEED, moveInWorldPlane); + if (mouseDelta.y > 0.0f) CameraMoveUp(camera, -CAMERA_PAN_SPEED); + if (mouseDelta.y < 0.0f) CameraMoveUp(camera, CAMERA_PAN_SPEED); + } + else + { + // Mouse support + CameraYaw(camera, -mousePositionDelta.x*CAMERA_MOUSE_MOVE_SENSITIVITY, rotateAroundTarget); + CameraPitch(camera, -mousePositionDelta.y*CAMERA_MOUSE_MOVE_SENSITIVITY, lockView, rotateAroundTarget, rotateUp); + } + // Keyboard support if (IsKeyDown(KEY_W)) CameraMoveForward(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); if (IsKeyDown(KEY_A)) CameraMoveRight(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); if (IsKeyDown(KEY_S)) CameraMoveForward(camera, -CAMERA_MOVE_SPEED, moveInWorldPlane); @@ -479,11 +493,14 @@ void UpdateCamera(Camera *camera, int mode) if (GetGamepadAxisMovement(0, GAMEPAD_AXIS_LEFT_X) >= 0.25f) CameraMoveRight(camera, CAMERA_MOVE_SPEED, moveInWorldPlane); } - //if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, CAMERA_MOVE_SPEED); - //if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -CAMERA_MOVE_SPEED); + if (mode == CAMERA_FREE) + { + if (IsKeyDown(KEY_SPACE)) CameraMoveUp(camera, CAMERA_MOVE_SPEED); + if (IsKeyDown(KEY_LEFT_CONTROL)) CameraMoveUp(camera, -CAMERA_MOVE_SPEED); + } } - if ((mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL)) + if ((mode == CAMERA_THIRD_PERSON) || (mode == CAMERA_ORBITAL) || (mode == CAMERA_FREE)) { // Zoom target distance CameraMoveToTarget(camera, -GetMouseWheelMove()); From fc885180673d09e2eb074019c581ec200a480262 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 12 Aug 2023 14:00:50 +0200 Subject: [PATCH 0198/1350] ADDED: Spline drawing functions -> - `DrawLineBSpline()` - `DrawLineCatmullRom()` --- src/raylib.h | 2 + src/rshapes.c | 200 +++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 175 insertions(+), 27 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index f7b0df66b..abc8371bc 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1192,6 +1192,8 @@ RLAPI void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color RLAPI void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color); // Draw a line using cubic-bezier curves in-out RLAPI void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, float thick, Color color); // Draw line using quadratic bezier curves with a control point RLAPI void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlPos, Vector2 endControlPos, float thick, Color color); // Draw line using cubic bezier curves with 2 control points +RLAPI void DrawLineBSpline(Vector2 *points, int pointCount, float thick, Color color); // Draw a B-Spline line, minimum 4 points +RLAPI void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color color); // Draw a Catmull Rom spline line, minimum 4 points RLAPI void DrawLineStrip(Vector2 *points, int pointCount, Color color); // Draw lines sequence RLAPI void DrawCircle(int centerX, int centerY, float radius, Color color); // Draw a color-filled circle RLAPI void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color); // Draw a piece of a circle diff --git a/src/rshapes.c b/src/rshapes.c index 8b319ed15..6987fe9da 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -67,8 +67,8 @@ #ifndef SMOOTH_CIRCLE_ERROR_RATE #define SMOOTH_CIRCLE_ERROR_RATE 0.5f // Circle error rate #endif -#ifndef BEZIER_LINE_DIVISIONS - #define BEZIER_LINE_DIVISIONS 24 // Bezier line divisions +#ifndef SPLINE_LINE_DIVISIONS + #define SPLINE_LINE_DIVISIONS 24 // Spline lines segment divisions #endif @@ -208,14 +208,14 @@ void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color) Vector2 previous = startPos; Vector2 current = { 0 }; - Vector2 points[2*BEZIER_LINE_DIVISIONS + 2] = { 0 }; + Vector2 points[2*SPLINE_LINE_DIVISIONS + 2] = { 0 }; - for (int i = 1; i <= BEZIER_LINE_DIVISIONS; i++) + for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++) { // Cubic easing in-out // NOTE: Easing is calculated only for y position value - current.y = EaseCubicInOut((float)i, startPos.y, endPos.y - startPos.y, (float)BEZIER_LINE_DIVISIONS); - current.x = previous.x + (endPos.x - startPos.x)/ (float)BEZIER_LINE_DIVISIONS; + current.y = EaseCubicInOut((float)i, startPos.y, endPos.y - startPos.y, (float)SPLINE_LINE_DIVISIONS); + current.x = previous.x + (endPos.x - startPos.x)/(float)SPLINE_LINE_DIVISIONS; float dy = current.y-previous.y; float dx = current.x-previous.x; @@ -237,21 +237,21 @@ void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color) previous = current; } - DrawTriangleStrip(points, 2*BEZIER_LINE_DIVISIONS+2, color); + DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS+2, color); } // Draw line using quadratic bezier curves with a control point void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, float thick, Color color) { - const float step = 1.0f/BEZIER_LINE_DIVISIONS; + const float step = 1.0f/SPLINE_LINE_DIVISIONS; Vector2 previous = startPos; Vector2 current = { 0 }; float t = 0.0f; - Vector2 points[2*BEZIER_LINE_DIVISIONS + 2] = { 0 }; + Vector2 points[2*SPLINE_LINE_DIVISIONS + 2] = { 0 }; - for (int i = 1; i <= BEZIER_LINE_DIVISIONS; i++) + for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++) { t = step*i; float a = powf(1 - t, 2); @@ -282,52 +282,198 @@ void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, fl previous = current; } - DrawTriangleStrip(points, 2*BEZIER_LINE_DIVISIONS+2, color); + DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS+2, color); } // Draw line using cubic bezier curves with 2 control points void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlPos, Vector2 endControlPos, float thick, Color color) { - const float step = 1.0f/BEZIER_LINE_DIVISIONS; + const float step = 1.0f/SPLINE_LINE_DIVISIONS; Vector2 previous = startPos; Vector2 current = { 0 }; float t = 0.0f; - Vector2 points[2*BEZIER_LINE_DIVISIONS + 2] = { 0 }; + Vector2 points[2*SPLINE_LINE_DIVISIONS + 2] = { 0 }; - for (int i = 1; i <= BEZIER_LINE_DIVISIONS; i++) + for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++) { t = step*i; float a = powf(1 - t, 3); float b = 3*powf(1 - t, 2)*t; - float c = 3*(1-t)*powf(t, 2); + float c = 3*(1 - t)*powf(t, 2); float d = powf(t, 3); current.y = a*startPos.y + b*startControlPos.y + c*endControlPos.y + d*endPos.y; current.x = a*startPos.x + b*startControlPos.x + c*endControlPos.x + d*endPos.x; - float dy = current.y-previous.y; - float dx = current.x-previous.x; + float dy = current.y - previous.y; + float dx = current.x - previous.x; float size = 0.5f*thick/sqrtf(dx*dx+dy*dy); - if (i==1) + if (i == 1) { - points[0].x = previous.x+dy*size; - points[0].y = previous.y-dx*size; - points[1].x = previous.x-dy*size; - points[1].y = previous.y+dx*size; + points[0].x = previous.x + dy*size; + points[0].y = previous.y - dx*size; + points[1].x = previous.x - dy*size; + points[1].y = previous.y + dx*size; } - points[2*i+1].x = current.x-dy*size; - points[2*i+1].y = current.y+dx*size; - points[2*i].x = current.x+dy*size; - points[2*i].y = current.y-dx*size; + points[2*i + 1].x = current.x - dy*size; + points[2*i + 1].y = current.y + dx*size; + points[2*i].x = current.x + dy*size; + points[2*i].y = current.y - dx*size; previous = current; } - DrawTriangleStrip(points, 2*BEZIER_LINE_DIVISIONS+2, color); + DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS + 2, color); +} + +// Draw a B-Spline line, minimum 4 points +void DrawLineBSpline(Vector2 *points, int pointCount, float thick, Color color) +{ + if (pointCount < 4) return; + + float a[4] = { 0 }; + float b[4] = { 0 }; + float dy = 0.0f; + float dx = 0.0f; + float size = 0.0f; + + Vector2 currentPoint = { 0 }; + Vector2 nextPoint = { 0 }; + Vector2 vertices[2*SPLINE_LINE_DIVISIONS + 2] = { 0 }; + + for (int i = 0; i < (pointCount - 3); i++) + { + Vector2 p1 = points[i], p2 = points[i + 1], p3 = points[i + 2], p4 = points[i + 3]; + + a[0] = (-p1.x + 3*p2.x - 3*p3.x + p4.x)/6.0f; + a[1] = (3*p1.x - 6*p2.x + 3*p3.x)/6.0f; + a[2] = (-3*p1.x + 3*p3.x)/6.0f; + a[3] = (p1.x + 4*p2.x + p3.x)/6.0f; + + b[0] = (-p1.y + 3*p2.y - 3*p3.y + p4.y)/6.0f; + b[1] = (3*p1.y - 6*p2.y + 3*p3.y)/6.0f; + b[2] = (-3*p1.y + 3*p3.y)/6.0f; + b[3] = (p1.y + 4*p2.y + p3.y)/6.0f; + + currentPoint.x = a[3]; + currentPoint.y = b[3]; + + if (i == 0) DrawCircleV(currentPoint, thick/2.0f, color); + + float t = 0.0f; + + if (i > 0) + { + vertices[0].x = currentPoint.x + dy*size; + vertices[0].y = currentPoint.y - dx*size; + vertices[1].x = currentPoint.x - dy*size; + vertices[1].y = currentPoint.y + dx*size; + } + + for (int j = 1; j <= SPLINE_LINE_DIVISIONS; j++) + { + t = ((float)j)/((float)SPLINE_LINE_DIVISIONS); + + nextPoint.x = a[3] + t*(a[2] + t*(a[1] + t*a[0])); + nextPoint.y = b[3] + t*(b[2] + t*(b[1] + t*b[0])); + + dy = nextPoint.y - currentPoint.y; + dx = nextPoint.x - currentPoint.x; + size = 0.5f*thick/sqrtf(dx*dx+dy*dy); + + if ((j == 1) && (i == 0)) + { + vertices[0].x = currentPoint.x + dy*size; + vertices[0].y = currentPoint.y - dx*size; + vertices[1].x = currentPoint.x - dy*size; + vertices[1].y = currentPoint.y + dx*size; + } + + vertices[2*j + 1].x = nextPoint.x - dy*size; + vertices[2*j + 1].y = nextPoint.y + dx*size; + vertices[2*j].x = nextPoint.x + dy*size; + vertices[2*j].y = nextPoint.y - dx*size; + + currentPoint = nextPoint; + } + + DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS+2, color); + } + + DrawCircleV(currentPoint, thick/2.0f, color); +} + +// Draw a Catmull Rom spline line, minimum 4 points +void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color color) +{ + if (pointCount < 4) return; + + float dy = 0.0f; + float dx = 0.0f; + float size = 0.0f; + + Vector2 currentPoint = points[1]; + Vector2 nextPoint = { 0 }; + Vector2 vertices[2*SPLINE_LINE_DIVISIONS + 2] = { 0 }; + + DrawCircleV(currentPoint, thick/2.0f, color); + + for (int i = 0; i < (pointCount - 3); i++) + { + Vector2 p1 = points[i], p2 = points[i + 1], p3 = points[i + 2], p4 = points[i + 3]; + + float t = 0.0f; + currentPoint = points[i]; + + if (i > 0) + { + vertices[0].x = currentPoint.x + dy*size; + vertices[0].y = currentPoint.y - dx*size; + vertices[1].x = currentPoint.x - dy*size; + vertices[1].y = currentPoint.y + dx*size; + } + + for (int i = 0; i <= SPLINE_LINE_DIVISIONS; i++) + { + t = ((float)i)/((float)SPLINE_LINE_DIVISIONS); + + float q0 = (-1*t*t*t) + (2*t*t) + (-1*t); + float q1 = (3*t*t*t) + (-5*t*t) + 2; + float q2 = (-3*t*t*t) + (4*t*t) + t; + float q3 = t*t*t - t*t; + + nextPoint.x = 0.5f*((p1.x*q0) + (p2.x*q1) + (p3.x*q2) + (p4.x*q3)); + nextPoint.y = 0.5f*((p1.y*q0) + (p2.y*q1) + (p3.y*q2) + (p4.y*q3)); + + float dy = nextPoint.y - currentPoint.y; + float dx = nextPoint.x - currentPoint.x; + float size = (0.5f*thick)/sqrtf(dx*dx + dy*dy); + + if (i == 1) + { + vertices[0].x = currentPoint.x + dy*size; + vertices[0].y = currentPoint.y - dx*size; + vertices[1].x = currentPoint.x - dy*size; + vertices[1].y = currentPoint.y + dx*size; + } + + vertices[2*i + 1].x = nextPoint.x - dy*size; + vertices[2*i + 1].y = nextPoint.y + dx*size; + vertices[2*i].x = nextPoint.x + dy*size; + vertices[2*i].y = nextPoint.y - dx*size; + + currentPoint = nextPoint; + } + + DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS+2, color); + + // TODO: REVIEW: HACK: Drawing a circle at points intersection to hide broken strip + DrawCircleV(currentPoint, thick/2.0f, color); + } } // Draw lines sequence From 9161c55d5970f4e7a7fe2a21606f92cfcecf2cd3 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 12 Aug 2023 18:40:18 +0200 Subject: [PATCH 0199/1350] REVIEWED: Code formatting --- src/rshapes.c | 100 +++++++++++++++++++++++++------------------------- 1 file changed, 51 insertions(+), 49 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index 6987fe9da..655f683dd 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -190,6 +190,7 @@ void DrawLineEx(Vector2 startPos, Vector2 endPos, float thick, Color color) if ((length > 0) && (thick > 0)) { float scale = thick/(2*length); + Vector2 radius = { -scale*delta.y, scale*delta.x }; Vector2 strip[4] = { { startPos.x - radius.x, startPos.y - radius.y }, @@ -217,27 +218,27 @@ void DrawLineBezier(Vector2 startPos, Vector2 endPos, float thick, Color color) current.y = EaseCubicInOut((float)i, startPos.y, endPos.y - startPos.y, (float)SPLINE_LINE_DIVISIONS); current.x = previous.x + (endPos.x - startPos.x)/(float)SPLINE_LINE_DIVISIONS; - float dy = current.y-previous.y; - float dx = current.x-previous.x; + float dy = current.y - previous.y; + float dx = current.x - previous.x; float size = 0.5f*thick/sqrtf(dx*dx+dy*dy); - if (i==1) + if (i == 1) { - points[0].x = previous.x+dy*size; - points[0].y = previous.y-dx*size; - points[1].x = previous.x-dy*size; - points[1].y = previous.y+dx*size; + points[0].x = previous.x + dy*size; + points[0].y = previous.y - dx*size; + points[1].x = previous.x - dy*size; + points[1].y = previous.y + dx*size; } - points[2*i+1].x = current.x-dy*size; - points[2*i+1].y = current.y+dx*size; - points[2*i].x = current.x+dy*size; - points[2*i].y = current.y-dx*size; + points[2*i + 1].x = current.x - dy*size; + points[2*i + 1].y = current.y + dx*size; + points[2*i].x = current.x + dy*size; + points[2*i].y = current.y - dx*size; previous = current; } - DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS+2, color); + DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS + 2, color); } // Draw line using quadratic bezier curves with a control point @@ -254,35 +255,36 @@ void DrawLineBezierQuad(Vector2 startPos, Vector2 endPos, Vector2 controlPos, fl for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++) { t = step*i; - float a = powf(1 - t, 2); - float b = 2*(1 - t)*t; + + float a = powf(1.0f - t, 2); + float b = 2.0f*(1.0f - t)*t; float c = powf(t, 2); // NOTE: The easing functions aren't suitable here because they don't take a control point current.y = a*startPos.y + b*controlPos.y + c*endPos.y; current.x = a*startPos.x + b*controlPos.x + c*endPos.x; - float dy = current.y-previous.y; - float dx = current.x-previous.x; + float dy = current.y - previous.y; + float dx = current.x - previous.x; float size = 0.5f*thick/sqrtf(dx*dx+dy*dy); - if (i==1) + if (i == 1) { - points[0].x = previous.x+dy*size; - points[0].y = previous.y-dx*size; - points[1].x = previous.x-dy*size; - points[1].y = previous.y+dx*size; + points[0].x = previous.x + dy*size; + points[0].y = previous.y - dx*size; + points[1].x = previous.x - dy*size; + points[1].y = previous.y + dx*size; } - points[2*i+1].x = current.x-dy*size; - points[2*i+1].y = current.y+dx*size; - points[2*i].x = current.x+dy*size; - points[2*i].y = current.y-dx*size; + points[2*i + 1].x = current.x - dy*size; + points[2*i + 1].y = current.y + dx*size; + points[2*i].x = current.x + dy*size; + points[2*i].y = current.y - dx*size; previous = current; } - DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS+2, color); + DrawTriangleStrip(points, 2*SPLINE_LINE_DIVISIONS + 2, color); } // Draw line using cubic bezier curves with 2 control points @@ -299,9 +301,10 @@ void DrawLineBezierCubic(Vector2 startPos, Vector2 endPos, Vector2 startControlP for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++) { t = step*i; - float a = powf(1 - t, 3); - float b = 3*powf(1 - t, 2)*t; - float c = 3*(1 - t)*powf(t, 2); + + float a = powf(1.0f - t, 3); + float b = 3.0f*powf(1.0f - t, 2)*t; + float c = 3.0f*(1.0f - t)*powf(t, 2); float d = powf(t, 3); current.y = a*startPos.y + b*startControlPos.y + c*endControlPos.y + d*endPos.y; @@ -347,24 +350,23 @@ void DrawLineBSpline(Vector2 *points, int pointCount, float thick, Color color) for (int i = 0; i < (pointCount - 3); i++) { + float t = 0.0f; Vector2 p1 = points[i], p2 = points[i + 1], p3 = points[i + 2], p4 = points[i + 3]; - a[0] = (-p1.x + 3*p2.x - 3*p3.x + p4.x)/6.0f; - a[1] = (3*p1.x - 6*p2.x + 3*p3.x)/6.0f; - a[2] = (-3*p1.x + 3*p3.x)/6.0f; - a[3] = (p1.x + 4*p2.x + p3.x)/6.0f; + a[0] = (-p1.x + 3.0f*p2.x - 3.0f*p3.x + p4.x)/6.0f; + a[1] = (3.0f*p1.x - 6.0f*p2.x + 3.0f*p3.x)/6.0f; + a[2] = (-3.0f*p1.x + 3.0f*p3.x)/6.0f; + a[3] = (p1.x + 4.0f*p2.x + p3.x)/6.0f; - b[0] = (-p1.y + 3*p2.y - 3*p3.y + p4.y)/6.0f; - b[1] = (3*p1.y - 6*p2.y + 3*p3.y)/6.0f; - b[2] = (-3*p1.y + 3*p3.y)/6.0f; - b[3] = (p1.y + 4*p2.y + p3.y)/6.0f; + b[0] = (-p1.y + 3.0f*p2.y - 3.0f*p3.y + p4.y)/6.0f; + b[1] = (3.0f*p1.y - 6.0f*p2.y + 3.0f*p3.y)/6.0f; + b[2] = (-3.0f*p1.y + 3.0f*p3.y)/6.0f; + b[3] = (p1.y + 4.0f*p2.y + p3.y)/6.0f; currentPoint.x = a[3]; currentPoint.y = b[3]; - if (i == 0) DrawCircleV(currentPoint, thick/2.0f, color); - - float t = 0.0f; + if (i == 0) DrawCircleV(currentPoint, thick/2.0f, color); // Draw init line circle-cap if (i > 0) { @@ -385,7 +387,7 @@ void DrawLineBSpline(Vector2 *points, int pointCount, float thick, Color color) dx = nextPoint.x - currentPoint.x; size = 0.5f*thick/sqrtf(dx*dx+dy*dy); - if ((j == 1) && (i == 0)) + if ((i == 0) && (j == 1)) { vertices[0].x = currentPoint.x + dy*size; vertices[0].y = currentPoint.y - dx*size; @@ -401,10 +403,10 @@ void DrawLineBSpline(Vector2 *points, int pointCount, float thick, Color color) currentPoint = nextPoint; } - DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS+2, color); + DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS + 2, color); } - DrawCircleV(currentPoint, thick/2.0f, color); + DrawCircleV(currentPoint, thick/2.0f, color); // Draw end line circle-cap } // Draw a Catmull Rom spline line, minimum 4 points @@ -420,13 +422,13 @@ void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color colo Vector2 nextPoint = { 0 }; Vector2 vertices[2*SPLINE_LINE_DIVISIONS + 2] = { 0 }; - DrawCircleV(currentPoint, thick/2.0f, color); + DrawCircleV(currentPoint, thick/2.0f, color); // Draw init line circle-cap for (int i = 0; i < (pointCount - 3); i++) { + float t = 0.0f; Vector2 p1 = points[i], p2 = points[i + 1], p3 = points[i + 2], p4 = points[i + 3]; - float t = 0.0f; currentPoint = points[i]; if (i > 0) @@ -441,9 +443,9 @@ void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color colo { t = ((float)i)/((float)SPLINE_LINE_DIVISIONS); - float q0 = (-1*t*t*t) + (2*t*t) + (-1*t); - float q1 = (3*t*t*t) + (-5*t*t) + 2; - float q2 = (-3*t*t*t) + (4*t*t) + t; + float q0 = (-1.0f*t*t*t) + (2.0f*t*t) + (-1.0f*t); + float q1 = (3.0f*t*t*t) + (-5.0f*t*t) + 2.0f; + float q2 = (-3.0f*t*t*t) + (4.0f*t*t) + t; float q3 = t*t*t - t*t; nextPoint.x = 0.5f*((p1.x*q0) + (p2.x*q1) + (p3.x*q2) + (p4.x*q3)); @@ -469,7 +471,7 @@ void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color colo currentPoint = nextPoint; } - DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS+2, color); + DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS + 2, color); // TODO: REVIEW: HACK: Drawing a circle at points intersection to hide broken strip DrawCircleV(currentPoint, thick/2.0f, color); From d873d0f173a2e12dce039132fe91fcd70923d98e Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 12 Aug 2023 18:45:59 +0200 Subject: [PATCH 0200/1350] ISSUE: `DrawLineCatmullRom()`, needs review --- src/rshapes.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index 655f683dd..98552f26e 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -429,8 +429,6 @@ void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color colo float t = 0.0f; Vector2 p1 = points[i], p2 = points[i + 1], p3 = points[i + 2], p4 = points[i + 3]; - currentPoint = points[i]; - if (i > 0) { vertices[0].x = currentPoint.x + dy*size; @@ -439,7 +437,9 @@ void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color colo vertices[1].y = currentPoint.y + dx*size; } - for (int i = 0; i <= SPLINE_LINE_DIVISIONS; i++) + // TODO: Something is wrong with this implementation, + // it should use 'j' instead of 'i' but it does not work... + for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++) { t = ((float)i)/((float)SPLINE_LINE_DIVISIONS); From bf705a63de32a8fc8b6023e89cc10431a1babc9b Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 12 Aug 2023 19:31:29 +0200 Subject: [PATCH 0201/1350] REVIEWED: `DrawLineCatmullRom()` --- src/rshapes.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index 98552f26e..ea45aefc4 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -437,11 +437,9 @@ void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color colo vertices[1].y = currentPoint.y + dx*size; } - // TODO: Something is wrong with this implementation, - // it should use 'j' instead of 'i' but it does not work... - for (int i = 1; i <= SPLINE_LINE_DIVISIONS; i++) + for (int j = 1; j <= SPLINE_LINE_DIVISIONS; j++) { - t = ((float)i)/((float)SPLINE_LINE_DIVISIONS); + t = ((float)j)/((float)SPLINE_LINE_DIVISIONS); float q0 = (-1.0f*t*t*t) + (2.0f*t*t) + (-1.0f*t); float q1 = (3.0f*t*t*t) + (-5.0f*t*t) + 2.0f; @@ -451,11 +449,11 @@ void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color colo nextPoint.x = 0.5f*((p1.x*q0) + (p2.x*q1) + (p3.x*q2) + (p4.x*q3)); nextPoint.y = 0.5f*((p1.y*q0) + (p2.y*q1) + (p3.y*q2) + (p4.y*q3)); - float dy = nextPoint.y - currentPoint.y; - float dx = nextPoint.x - currentPoint.x; - float size = (0.5f*thick)/sqrtf(dx*dx + dy*dy); + dy = nextPoint.y - currentPoint.y; + dx = nextPoint.x - currentPoint.x; + size = (0.5f*thick)/sqrtf(dx*dx + dy*dy); - if (i == 1) + if ((i == 0) && (j == 1)) { vertices[0].x = currentPoint.x + dy*size; vertices[0].y = currentPoint.y - dx*size; @@ -463,19 +461,18 @@ void DrawLineCatmullRom(Vector2 *points, int pointCount, float thick, Color colo vertices[1].y = currentPoint.y + dx*size; } - vertices[2*i + 1].x = nextPoint.x - dy*size; - vertices[2*i + 1].y = nextPoint.y + dx*size; - vertices[2*i].x = nextPoint.x + dy*size; - vertices[2*i].y = nextPoint.y - dx*size; + vertices[2*j + 1].x = nextPoint.x - dy*size; + vertices[2*j + 1].y = nextPoint.y + dx*size; + vertices[2*j].x = nextPoint.x + dy*size; + vertices[2*j].y = nextPoint.y - dx*size; currentPoint = nextPoint; } DrawTriangleStrip(vertices, 2*SPLINE_LINE_DIVISIONS + 2, color); - - // TODO: REVIEW: HACK: Drawing a circle at points intersection to hide broken strip - DrawCircleV(currentPoint, thick/2.0f, color); } + + DrawCircleV(currentPoint, thick/2.0f, color); // Draw end line circle-cap } // Draw lines sequence From 7a1c0d2547e836224277b85fe540447a05cd93a7 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 14 Aug 2023 21:10:24 +0200 Subject: [PATCH 0202/1350] Added fortran-raylib --- BINDINGS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/BINDINGS.md b/BINDINGS.md index 00672d9bf..48cf0e64b 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -24,6 +24,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | rayex | 3.7 | [elixir](https://elixir-lang.org/) | Apache-2.0 | https://github.com/shiryel/rayex | | raylib-factor | 4.0 | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | | raylib-freebasic | **4.5** | [FreeBASIC](https://www.freebasic.net/) | MIT | https://github.com/WIITD/raylib-freebasic | +| fortran-raylib | **4.5** | [Fortran](https://www.fortran.com/) | ISC | https://github.com/interkosmos/fortran-raylib | | raylib for Pascal | **4.5** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | Modified Zlib | https://github.com/tinyBigGAMES/raylib | | raylib-go | 4.5 | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | | raylib-guile | **auto** | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | From e0afb8942e0536b92e7da21ca177c804f698ebff Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Mon, 14 Aug 2023 16:55:13 -0300 Subject: [PATCH 0203/1350] Fix examples/others/rlgl_standalone.c compilation issue (#3242) --- examples/Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/Makefile b/examples/Makefile index ff5098948..9d0b7341b 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -261,6 +261,11 @@ ifeq ($(PLATFORM),PLATFORM_DRM) INCLUDE_PATHS += -I/usr/include/libdrm endif +# Include GLFW required for examples/others/rlgl_standalone.c +ifeq ($(USE_EXTERNAL_GLFW),FALSE) +all others: INCLUDE_PATHS += -I$(RAYLIB_PATH)/src/external/glfw/include +endif + # Define library paths containing required libs: LDFLAGS #------------------------------------------------------------------------------------------------ LDFLAGS = -L. -L$(RAYLIB_RELEASE_PATH) -L$(RAYLIB_PATH)/src From e2d4463886de84493ae2cebaa173ae402dc30701 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 15 Aug 2023 00:04:07 +0200 Subject: [PATCH 0204/1350] Update BINDINGS.md --- BINDINGS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BINDINGS.md b/BINDINGS.md index 48cf0e64b..d4bd8fc21 100644 --- a/BINDINGS.md +++ b/BINDINGS.md @@ -24,7 +24,7 @@ Some people ported raylib to other languages in form of bindings or wrappers to | rayex | 3.7 | [elixir](https://elixir-lang.org/) | Apache-2.0 | https://github.com/shiryel/rayex | | raylib-factor | 4.0 | [Factor](https://factorcode.org/) | BSD | https://github.com/factor/factor/blob/master/extra/raylib/raylib.factor | | raylib-freebasic | **4.5** | [FreeBASIC](https://www.freebasic.net/) | MIT | https://github.com/WIITD/raylib-freebasic | -| fortran-raylib | **4.5** | [Fortran](https://www.fortran.com/) | ISC | https://github.com/interkosmos/fortran-raylib | +| fortran-raylib | **4.5** | [Fortran](https://fortran-lang.org/) | ISC | https://github.com/interkosmos/fortran-raylib | | raylib for Pascal | **4.5** | [Object Pascal](https://en.wikipedia.org/wiki/Object_Pascal) | Modified Zlib | https://github.com/tinyBigGAMES/raylib | | raylib-go | 4.5 | [Go](https://golang.org/) | Zlib | https://github.com/gen2brain/raylib-go | | raylib-guile | **auto** | [Guile](https://www.gnu.org/software/guile/) | Zlib | https://github.com/petelliott/raylib-guile | From a86c93ebc0095f6c2ffc14656bfc9e1e37070f72 Mon Sep 17 00:00:00 2001 From: ashn <60763262+ashn-dot-dev@users.noreply.github.com> Date: Mon, 14 Aug 2023 18:09:27 -0400 Subject: [PATCH 0205/1350] Ignore unused return value of GetCodepointNext in GetCodepointCount (#3241) * Ignore unused return value of GetCodepointNext in GetCodepointCount Removes the last warning from non-external libraries when compiling with the default build configuration on x64 Linux. * Remove unnecessary void cast in GetCodepointCount --- src/rtext.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rtext.c b/src/rtext.c index a66c58101..3fb2b8861 100644 --- a/src/rtext.c +++ b/src/rtext.c @@ -1766,7 +1766,7 @@ int GetCodepointCount(const char *text) while (*ptr != '\0') { int next = 0; - int letter = GetCodepointNext(ptr, &next); + GetCodepointNext(ptr, &next); ptr += next; From e4dcbd518091a5854a517ea4cfc3f7e2d29de1a7 Mon Sep 17 00:00:00 2001 From: Ray Date: Thu, 17 Aug 2023 15:05:52 +0200 Subject: [PATCH 0206/1350] Fix #3246 --- src/rlgl.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rlgl.h b/src/rlgl.h index 3756e5ac7..9ca099acf 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2945,6 +2945,7 @@ bool rlCheckRenderBatchLimit(int vCount) unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount) { unsigned int id = 0; + if (data == NULL) return id; glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding From 016b7d0a3a6d67e5d78a19d8c0adaa555712e67d Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sat, 19 Aug 2023 08:43:00 -0300 Subject: [PATCH 0207/1350] Fix text_unicode.c example crashing (#3250) * Fix text_unicode.c example crashing * Adjust the text_unicode.c example crashing fix --- examples/text/text_unicode.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/examples/text/text_unicode.c b/examples/text/text_unicode.c index eb2a7843c..b25e3273b 100644 --- a/examples/text/text_unicode.c +++ b/examples/text/text_unicode.c @@ -187,12 +187,11 @@ int main(void) // Add a new set of emojis when SPACE is pressed if (IsKeyPressed(KEY_SPACE)) RandomizeEmoji(); - // Set the selected emoji and copy its text to clipboard + // Set the selected emoji if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && (hovered != -1) && (hovered != selected)) { selected = hovered; selectedPos = hoveredPos; - SetClipboardText(messages[emoji[selected].message].text); } Vector2 mouse = GetMousePosition(); @@ -267,7 +266,7 @@ int main(void) a = b; b = tmp; } - + if (msgRect.x + msgRect.width > screenWidth) msgRect.x -= (msgRect.x + msgRect.width) - screenWidth + 10; // Draw chat bubble @@ -287,11 +286,11 @@ int main(void) DrawText(info, (int)pos.x, (int)pos.y, 10, RAYWHITE); } //------------------------------------------------------------------------------ - + // Draw the info text DrawText("These emojis have something to tell you, click each to find out!", (screenWidth - 650)/2, screenHeight - 40, 20, GRAY); DrawText("Each emoji is a unicode character from a font, not a texture... Press [SPACEBAR] to refresh", (screenWidth - 484)/2, screenHeight - 16, 10, GRAY); - + EndDrawing(); //---------------------------------------------------------------------------------- } @@ -342,7 +341,7 @@ static void DrawTextBoxedSelectable(Font font, const char *text, Rectangle rec, { int length = TextLength(text); // Total length in bytes of the text, scanned by codepoints in loop - float textOffsetY = 0; // Offset between lines (on line break '\n') + float textOffsetY = 0.0f; // Offset between lines (on line break '\n') float textOffsetX = 0.0f; // Offset X to next character to draw float scaleFactor = fontSize/(float)font.baseSize; // Character rectangle scaling factor @@ -465,4 +464,4 @@ static void DrawTextBoxedSelectable(Font font, const char *text, Rectangle rec, textOffsetX += glyphWidth; } -} \ No newline at end of file +} From 5a33f19964c845fd891342a6291f082e82ed2bf4 Mon Sep 17 00:00:00 2001 From: Ray Date: Sat, 19 Aug 2023 20:09:43 +0200 Subject: [PATCH 0208/1350] Revert "Fix #3246" This reverts commit e4dcbd518091a5854a517ea4cfc3f7e2d29de1a7. --- src/rlgl.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/rlgl.h b/src/rlgl.h index 9ca099acf..3756e5ac7 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -2945,7 +2945,6 @@ bool rlCheckRenderBatchLimit(int vCount) unsigned int rlLoadTexture(const void *data, int width, int height, int format, int mipmapCount) { unsigned int id = 0; - if (data == NULL) return id; glBindTexture(GL_TEXTURE_2D, 0); // Free any old binding From 820343e7ac56546af93066f89425a828156ea82b Mon Sep 17 00:00:00 2001 From: "actondev (Christos)" Date: Sun, 20 Aug 2023 21:27:39 +0200 Subject: [PATCH 0209/1350] add build.zig options for individual modules (#3254) --- src/build.zig | 58 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 45 insertions(+), 13 deletions(-) diff --git a/src/build.zig b/src/build.zig index 9aab4812a..27250f5ff 100644 --- a/src/build.zig +++ b/src/build.zig @@ -6,7 +6,6 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built "-std=gnu99", "-D_GNU_SOURCE", "-DGL_SILENCE_DEPRECATION=199309L", - "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 }; const raylib = b.addStaticLibrary(.{ @@ -22,15 +21,38 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built } raylib.addCSourceFiles(&.{ - srcdir ++ "/raudio.c", srcdir ++ "/rcore.c", - srcdir ++ "/rmodels.c", - srcdir ++ "/rshapes.c", - srcdir ++ "/rtext.c", - srcdir ++ "/rtextures.c", srcdir ++ "/utils.c", }, raylib_flags); + if (options.raudio) { + raylib.addCSourceFiles(&.{ + srcdir ++ "/raudio.c", + }, raylib_flags); + } + if (options.rmodels) { + raylib.addCSourceFiles(&.{ + srcdir ++ "/rmodels.c", + }, &[_][]const u8{ + "-fno-sanitize=undefined", // https://github.com/raysan5/raylib/issues/1891 + } ++ raylib_flags); + } + if (options.rshapes) { + raylib.addCSourceFiles(&.{ + srcdir ++ "/rshapes.c", + }, raylib_flags); + } + if (options.rtext) { + raylib.addCSourceFiles(&.{ + srcdir ++ "/rtext.c", + }, raylib_flags); + } + if (options.rtextures) { + raylib.addCSourceFiles(&.{ + srcdir ++ "/rtextures.c", + }, raylib_flags); + } + var gen_step = std.build.Step.WriteFile.create(b); raylib.step.dependOn(&gen_step.step); @@ -137,6 +159,11 @@ pub fn addRaylib(b: *std.Build, target: std.zig.CrossTarget, optimize: std.built } pub const Options = struct { + raudio: bool = true, + rmodels: bool = true, + rshapes: bool = true, + rtext: bool = true, + rtextures: bool = true, raygui: bool = false, platform_drm: bool = false, }; @@ -152,19 +179,24 @@ pub fn build(b: *std.Build) void { // set a preferred release mode, allowing the user to decide how to optimize. const optimize = b.standardOptimizeOption(.{}); - const raygui = b.option(bool, "raygui", "Compile with raygui support"); - const platform_drm = b.option(bool, "platform_drm", "Compile raylib in native mode (no X11)"); + const defaults = Options{}; + const options = Options{ + .platform_drm = b.option(bool, "platform_drm", "Compile raylib in native mode (no X11)") orelse defaults.platform_drm, + .raudio = b.option(bool, "raudio", "Compile with audio support") orelse defaults.raudio, + .rmodels = b.option(bool, "rmodels", "Compile with models support") orelse defaults.rmodels, + .rtext = b.option(bool, "rtext", "Compile with text support") orelse defaults.rtext, + .rtextures = b.option(bool, "rtextures", "Compile with textures support") orelse defaults.rtextures, + .rshapes = b.option(bool, "rshapes", "Compile with shapes support") orelse defaults.rshapes, + .raygui = b.option(bool, "raygui", "Compile with raygui support") orelse defaults.raygui, + }; - const lib = addRaylib(b, target, optimize, .{ - .raygui = raygui orelse false, - .platform_drm = platform_drm orelse false, - }); + const lib = addRaylib(b, target, optimize, options); lib.installHeader("src/raylib.h", "raylib.h"); lib.installHeader("src/raymath.h", "raymath.h"); lib.installHeader("src/rlgl.h", "rlgl.h"); - if (raygui orelse false) { + if (options.raygui) { lib.installHeader("../raygui/src/raygui.h", "raygui.h"); } From 8189bddefbb91525386dfdd60e268cc33e956a1f Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 20 Aug 2023 21:36:36 +0200 Subject: [PATCH 0210/1350] tweaks --- src/rshapes.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/rshapes.c b/src/rshapes.c index ea45aefc4..f3061f8b3 100644 --- a/src/rshapes.c +++ b/src/rshapes.c @@ -498,6 +498,13 @@ void DrawCircle(int centerX, int centerY, float radius, Color color) DrawCircleV((Vector2){ (float)centerX, (float)centerY }, radius, color); } +// Draw a color-filled circle (Vector version) +// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues +void DrawCircleV(Vector2 center, float radius, Color color) +{ + DrawCircleSector(center, radius, 0, 360, 36, color); +} + // Draw a piece of a circle void DrawCircleSector(Vector2 center, float radius, float startAngle, float endAngle, int segments, Color color) { @@ -530,6 +537,7 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA rlSetTexture(texShapes.id); rlBegin(RL_QUADS); + // NOTE: Every QUAD actually represents two segments for (int i = 0; i < segments/2; i++) { @@ -539,7 +547,7 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA rlVertex2f(center.x, center.y); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); - rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength*2))*radius, center.y + sinf(DEG2RAD*(angle + stepLength*2))*radius); + rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength*2.0f))*radius, center.y + sinf(DEG2RAD*(angle + stepLength*2.0f))*radius); rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); rlVertex2f(center.x + cosf(DEG2RAD*(angle + stepLength))*radius, center.y + sinf(DEG2RAD*(angle + stepLength))*radius); @@ -547,11 +555,11 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA rlTexCoord2f(texShapesRec.x/texShapes.width, (texShapesRec.y + texShapesRec.height)/texShapes.height); rlVertex2f(center.x + cosf(DEG2RAD*angle)*radius, center.y + sinf(DEG2RAD*angle)*radius); - angle += (stepLength*2); + angle += (stepLength*2.0f); } // NOTE: In case number of segments is odd, we add one last piece to the cake - if (segments%2) + if ((segments%2) == 1) { rlColor4ub(color.r, color.g, color.b, color.a); @@ -567,6 +575,7 @@ void DrawCircleSector(Vector2 center, float radius, float startAngle, float endA rlTexCoord2f((texShapesRec.x + texShapesRec.width)/texShapes.width, texShapesRec.y/texShapes.height); rlVertex2f(center.x, center.y); } + rlEnd(); rlSetTexture(0); @@ -659,13 +668,6 @@ void DrawCircleGradient(int centerX, int centerY, float radius, Color color1, Co rlEnd(); } -// Draw a color-filled circle (Vector version) -// NOTE: On OpenGL 3.3 and ES2 we use QUADS to avoid drawing order issues -void DrawCircleV(Vector2 center, float radius, Color color) -{ - DrawCircleSector(center, radius, 0, 360, 36, color); -} - // Draw circle outline void DrawCircleLines(int centerX, int centerY, float radius, Color color) { From b3f82a148ac59ae0615597e553fb64b836616d63 Mon Sep 17 00:00:00 2001 From: "actondev (Christos)" Date: Sun, 20 Aug 2023 23:53:20 +0200 Subject: [PATCH 0211/1350] Add `IsKeyPressedRepeat` (desktop only) (#3245) Since the key pressed are handle by comparing current vs previous state (ie frame), a special way is needed to handle key repeats. --- src/raylib.h | 1 + src/rcore.c | 26 +++++++++++++++++++++++--- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/raylib.h b/src/raylib.h index abc8371bc..d932e3a34 100644 --- a/src/raylib.h +++ b/src/raylib.h @@ -1115,6 +1115,7 @@ RLAPI unsigned char *DecodeDataBase64(const unsigned char *data, int *outputSize // Input-related functions: keyboard RLAPI bool IsKeyPressed(int key); // Check if a key has been pressed once +RLAPI bool IsKeyPressedRepeat(int key); // Check if a key has been pressed again (Only PLATFORM_DESKTOP) RLAPI bool IsKeyDown(int key); // Check if a key is being pressed RLAPI bool IsKeyReleased(int key); // Check if a key has been released once RLAPI bool IsKeyUp(int key); // Check if a key is NOT being pressed diff --git a/src/rcore.c b/src/rcore.c index 7d8811b05..52a58d6d6 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -438,6 +438,8 @@ typedef struct CoreData { int exitKey; // Default exit key char currentKeyState[MAX_KEYBOARD_KEYS]; // Registers current frame key state char previousKeyState[MAX_KEYBOARD_KEYS]; // Registers previous frame key state + // NOTE: Since key press logic involves comparing prev vs cur key state, we need to handle key repeats specially + char keyRepeatInFrame[MAX_KEYBOARD_KEYS]; // Registers key repeats for current frame. int keyPressedQueue[MAX_KEY_PRESSED_QUEUE]; // Input keys queue int keyPressedQueueCount; // Input keys queue count @@ -3755,6 +3757,13 @@ bool IsKeyPressed(int key) return pressed; } +// Check if a key has been pressed again (only PLATFORM_DESKTOP) +bool IsKeyPressedRepeat(int key) +{ + if (CORE.Input.Keyboard.keyRepeatInFrame[key] == 1) return true; + else return true; +} + // Check if a key is being pressed (key held down) bool IsKeyDown(int key) { @@ -5170,6 +5179,8 @@ void PollInputEvents(void) // Reset keys/chars pressed registered CORE.Input.Keyboard.keyPressedQueueCount = 0; CORE.Input.Keyboard.charPressedQueueCount = 0; + // Reset key repeats + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; #if !(defined(PLATFORM_RPI) || defined(PLATFORM_DRM)) // Reset last gamepad button/axis registered state @@ -5179,7 +5190,11 @@ void PollInputEvents(void) #if defined(PLATFORM_RPI) || defined(PLATFORM_DRM) // Register previous keys states - for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) + { + CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } PollKeyboardEvents(); @@ -5586,7 +5601,8 @@ static void KeyCallback(GLFWwindow *window, int key, int scancode, int action, i // WARNING: GLFW could return GLFW_REPEAT, we need to consider it as 1 // to work properly with our implementation (IsKeyDown/IsKeyUp checks) if (action == GLFW_RELEASE) CORE.Input.Keyboard.currentKeyState[key] = 0; - else CORE.Input.Keyboard.currentKeyState[key] = 1; + else if(action == GLFW_PRESS) CORE.Input.Keyboard.currentKeyState[key] = 1; + else if(action == GLFW_REPEAT) CORE.Input.Keyboard.keyRepeatInFrame[key] = 1; #if !defined(PLATFORM_WEB) // WARNING: Check if CAPS/NUM key modifiers are enabled and force down state for those keys @@ -6357,7 +6373,11 @@ static void ProcessKeyboard(void) bufferByteCount = read(STDIN_FILENO, keysBuffer, MAX_KEYBUFFER_SIZE); // POSIX system call // Reset pressed keys array (it will be filled below) - for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.currentKeyState[i] = 0; + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) + { + CORE.Input.Keyboard.currentKeyState[i] = 0; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } // Fill all read bytes (looking for keys) for (int i = 0; i < bufferByteCount; i++) From dfd0436428f95c31885646183d64678f05a80494 Mon Sep 17 00:00:00 2001 From: Ray Date: Mon, 21 Aug 2023 00:34:35 +0200 Subject: [PATCH 0212/1350] Reviewed `IsKeyPressedRepeat()` #3248 --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 52a58d6d6..1cae7f582 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3761,7 +3761,7 @@ bool IsKeyPressed(int key) bool IsKeyPressedRepeat(int key) { if (CORE.Input.Keyboard.keyRepeatInFrame[key] == 1) return true; - else return true; + else return false; } // Check if a key is being pressed (key held down) From 5ed83dfa2928bae7e7f53076a6edca789e513173 Mon Sep 17 00:00:00 2001 From: vitopigno <103512727+VitusVeit@users.noreply.github.com> Date: Mon, 21 Aug 2023 00:36:00 +0200 Subject: [PATCH 0213/1350] Update rcore.c (#3255) --- src/rcore.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 1cae7f582..10303fe94 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -4470,13 +4470,16 @@ static bool InitGraphicsDevice(int width, int height) #endif // Try to enable GPU V-Sync, so frames are limited to screen refresh rate (60Hz -> 60 FPS) - // NOTE: V-Sync can be enabled by graphic driver configuration + // NOTE: V-Sync can be enabled by graphic driver configuration, it doesn't need + // to be activated on web platforms since VSync is enforced there. +#if !defined(PLATFORM_WEB) if (CORE.Window.flags & FLAG_VSYNC_HINT) { // WARNING: It seems to hit a critical render path in Intel HD Graphics glfwSwapInterval(1); TRACELOG(LOG_INFO, "DISPLAY: Trying to enable VSYNC"); } +#endif int fbWidth = CORE.Window.screen.width; int fbHeight = CORE.Window.screen.height; From 8f228626a2cdeb0749818b235f06b94e804bd873 Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Sat, 26 Aug 2023 09:35:53 -0300 Subject: [PATCH 0214/1350] Match CMakeOptions.txt options default values (#3258) --- CMakeOptions.txt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/CMakeOptions.txt b/CMakeOptions.txt index cc57a851f..5cb73bbdd 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -38,13 +38,13 @@ cmake_dependent_option(SUPPORT_MODULE_RAUDIO "Include module: raudio" ON CUSTOMI cmake_dependent_option(SUPPORT_CAMERA_SYSTEM "Provide camera module (rcamera.h) with multiple predefined cameras: free, 1st/3rd person, orbital" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_GESTURES_SYSTEM "Gestures module is included (rgestures.h) to support gestures detection: tap, hold, swipe, drag" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_MOUSE_GESTURES "Mouse gestures are directly mapped like touches and processed by gestures system" ON CUSTOMIZE_BUILD ON) -cmake_dependent_option(SUPPORT_SSH_KEYBOARD_RPI "Reconfigure standard input to receive key inputs, works with SSH connection" OFF CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_SSH_KEYBOARD_RPI "Reconfigure standard input to receive key inputs, works with SSH connection" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_DEFAULT_FONT "Default font is loaded on window initialization to be available for the user to render simple text. If enabled, uses external module functions to load default raylib font (module: text)" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_SCREEN_CAPTURE "Allow automatic screen capture of current screen pressing F12, defined in KeyCallback()" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_GIF_RECORDING "Allow automatic gif recording of current screen pressing CTRL+F12, defined in KeyCallback()" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_BUSY_WAIT_LOOP "Use busy wait loop for timing sync instead of a high-resolution timer" OFF CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_EVENTS_WAITING "Wait for events passively (sleeping while no events) instead of polling them actively every frame" OFF CUSTOMIZE_BUILD OFF) -cmake_dependent_option(SUPPORT_WINMM_HIGHRES_TIMER "Setting a higher resolution can improve the accuracy of time-out intervals in wait functions" OFF CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_WINMM_HIGHRES_TIMER "Setting a higher resolution can improve the accuracy of time-out intervals in wait functions" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_COMPRESSION_API "Support for compression API" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_CUSTOM_FRAME_CONTROL "Enabling this flag allows manual control of the frame processes, use at your own risk" OFF CUSTOMIZE_BUILD OFF) @@ -58,14 +58,14 @@ cmake_dependent_option(SUPPORT_IMAGE_MANIPULATION "Support multiple image editin cmake_dependent_option(SUPPORT_FILEFORMAT_PNG "Support loading PNG as textures" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_DDS "Support loading DDS as textures" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_HDR "Support loading HDR as textures" ON CUSTOMIZE_BUILD ON) -cmake_dependent_option(SUPPORT_FILEFORMAT_PNM "Support loading PNM as textures" ON CUSTOMIZE_BUILD ON) -cmake_dependent_option(SUPPORT_FILEFORMAT_KTX "Support loading KTX as textures" ON CUSTOMIZE_BUILD ON) -cmake_dependent_option(SUPPORT_FILEFORMAT_ASTC "Support loading ASTC as textures" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_PNM "Support loading PNM as textures" ${OFF} CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_FILEFORMAT_KTX "Support loading KTX as textures" ${OFF} CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_FILEFORMAT_ASTC "Support loading ASTC as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_BMP "Support loading BMP as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_TGA "Support loading TGA as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_JPG "Support loading JPG as textures" ${OFF} CUSTOMIZE_BUILD OFF) -cmake_dependent_option(SUPPORT_FILEFORMAT_GIF "Support loading GIF as textures" ${OFF} CUSTOMIZE_BUILD OFF) -cmake_dependent_option(SUPPORT_FILEFORMAT_QOI "Support loading QOI as textures" ${OFF} CUSTOMIZE_BUILD OFF) +cmake_dependent_option(SUPPORT_FILEFORMAT_GIF "Support loading GIF as textures" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_QOI "Support loading QOI as textures" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_PSD "Support loading PSD as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_PKM "Support loading PKM as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_PVR "Support loading PVR as textures" ${OFF} CUSTOMIZE_BUILD OFF) From 4fa66f26359ba21068db15b1268cf7fb03422afa Mon Sep 17 00:00:00 2001 From: ubkp <118854183+ubkp@users.noreply.github.com> Date: Sat, 26 Aug 2023 09:40:30 -0300 Subject: [PATCH 0215/1350] Fix SetClipboardText for web (#3257) --- src/rcore.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 10303fe94..8413df2a0 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2150,7 +2150,7 @@ void SetClipboardText(const char *text) #if defined(PLATFORM_WEB) // Security check to (partially) avoid malicious code if (strchr(text, '\'') != NULL) TRACELOG(LOG_WARNING, "SYSTEM: Provided Clipboard could be potentially malicious, avoid [\'] character"); - else emscripten_run_script(TextFormat("navigator.clipboard.writeText('%s')", text)); + else EM_ASM( { navigator.clipboard.writeText(UTF8ToString($0)); }, text); #endif } From 21f5482e0d6e935e69118f6028da472065ea8317 Mon Sep 17 00:00:00 2001 From: Jeffery Myers Date: Sat, 26 Aug 2023 05:43:14 -0700 Subject: [PATCH 0216/1350] [Image] Validate that ImageDrawRectangleRec is drawing entirely inside the image (#3264) * Add a function to clone a sound and share data with another sound. * rename items based on feedback * PR Feedback, use custom unload for sound alias, not variant of normal sound unloading * sound_multi example * Validate that image rect drawing is inside the image so we don't overflow a buffer * remove files that should not have been added. * remove changes that should not have been * revert * adsfasdfsdfsdf --- src/rtextures.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/rtextures.c b/src/rtextures.c index 4b697f777..f32a45d1e 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3284,6 +3284,20 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color) if (rec.width < 0) rec.width = 0; if (rec.height < 0) rec.height = 0; + // clamp the size the the image bounds + if (rec.x + rec.width >= dst->width) + rec.width = dst->width - rec.x; + + if (rec.y + rec.height >= dst->height) + rec.height = dst->height - rec.y; + + // check if the rect is even inside the image + if (rec.x > dst->width || rec.y > dst->height) + return; + + if (rec.x + rec.width < 0 || rec.y + rec.height < 0) + return; + int sy = (int)rec.y; int sx = (int)rec.x; From 828d2736987c280883c9a5082bfd4d1a834de8a9 Mon Sep 17 00:00:00 2001 From: Ethan Simpson Date: Sun, 27 Aug 2023 00:44:52 +1200 Subject: [PATCH 0217/1350] Add Vector3 Projecting and Rejection to Raymath (#3263) * Update raymath.h * formatting --- src/raymath.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/raymath.h b/src/raymath.h index 86d2c8eb8..def8bfd42 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -713,6 +713,32 @@ RMAPI Vector3 Vector3Normalize(Vector3 v) return result; } +//Calculate the projection of the vector v1 on to v2 +RMAPI Vector3 Vector3Project(Vector3 v1, Vector3 v2) +{ + float v1dv2 = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); + float v2dv2 = (v2.x*v2.x + v2.y*v2.y + v2.z*v2.z); + + float mag = v1dv2/v2dv2; + + Vector3 result = { v2.x*mag , v2.y*mag, v2.z*mag }; + + return result; +} + +//Calculate the rejection of the vector v1 on to v2 +RMAPI Vector3 Vector3Reject(Vector3 v1, Vector3 v2) +{ + float v1dv2 = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); + float v2dv2 = (v2.x*v2.x + v2.y*v2.y + v2.z*v2.z); + + float mag = v1dv2/v2dv2; + + Vector3 result = { v1.x - (v2.x*mag) , v1.y - (v2.y*mag), v1.z - (v2.z*mag) }; + + return result; +} + // Orthonormalize provided vectors // Makes vectors normalized and orthogonal to each other // Gram-Schmidt function implementation From 9c9fba6a6c1f23e98ecb55ed257960e94c57f765 Mon Sep 17 00:00:00 2001 From: Nickolas McDonald <43690021+n77y@users.noreply.github.com> Date: Sat, 26 Aug 2023 08:48:28 -0400 Subject: [PATCH 0218/1350] [Feature] IsKey... safety checks and more (#3256) * [Feature] Add GetKeyRepeat * Update rcore.c * Simpler design, only one repeat per frame * Update config.h * Update rcore.c * Add KEYBOARD_KEYS_MASK * Update config.h * reversions * Update rcore.c * Update rcore.c * change docs * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update raylib.h * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c * Update rcore.c --- src/rcore.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 8413df2a0..cb3dfe82a 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3750,6 +3750,7 @@ void OpenURL(const char *url) // Check if a key has been pressed once bool IsKeyPressed(int key) { + if ((key < 0) || (key >= MAX_KEYBOARD_KEYS)) return false; bool pressed = false; if ((CORE.Input.Keyboard.previousKeyState[key] == 0) && (CORE.Input.Keyboard.currentKeyState[key] == 1)) pressed = true; @@ -3760,6 +3761,7 @@ bool IsKeyPressed(int key) // Check if a key has been pressed again (only PLATFORM_DESKTOP) bool IsKeyPressedRepeat(int key) { + if ((key < 0) || (key >= MAX_KEYBOARD_KEYS)) return false; if (CORE.Input.Keyboard.keyRepeatInFrame[key] == 1) return true; else return false; } @@ -3767,6 +3769,7 @@ bool IsKeyPressedRepeat(int key) // Check if a key is being pressed (key held down) bool IsKeyDown(int key) { + if ((key < 0) || (key >= MAX_KEYBOARD_KEYS)) return false; if (CORE.Input.Keyboard.currentKeyState[key] == 1) return true; else return false; } @@ -3774,6 +3777,7 @@ bool IsKeyDown(int key) // Check if a key has been released once bool IsKeyReleased(int key) { + if ((key < 0) || (key >= MAX_KEYBOARD_KEYS)) return false; bool released = false; if ((CORE.Input.Keyboard.previousKeyState[key] == 1) && (CORE.Input.Keyboard.currentKeyState[key] == 0)) released = true; @@ -3784,6 +3788,7 @@ bool IsKeyReleased(int key) // Check if a key is NOT being pressed (key not held down) bool IsKeyUp(int key) { + if ((key < 0) || (key >= MAX_KEYBOARD_KEYS)) return false; if (CORE.Input.Keyboard.currentKeyState[key] == 0) return true; else return false; } @@ -5226,7 +5231,11 @@ void PollInputEvents(void) // Keyboard/Mouse input polling (automatically managed by GLFW3 through callback) // Register previous keys states - for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) + { + CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } // Register previous mouse states for (int i = 0; i < MAX_MOUSE_BUTTONS; i++) CORE.Input.Mouse.previousButtonState[i] = CORE.Input.Mouse.currentButtonState[i]; @@ -5408,7 +5417,11 @@ void PollInputEvents(void) #if defined(PLATFORM_ANDROID) // Register previous keys states // NOTE: Android supports up to 260 keys - for (int i = 0; i < 260; i++) CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + for (int i = 0; i < 260; i++) + { + CORE.Input.Keyboard.previousKeyState[i] = CORE.Input.Keyboard.currentKeyState[i]; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } // Android ALooper_pollAll() variables int pollResult = 0; @@ -6073,6 +6086,7 @@ static int32_t AndroidInputCallback(struct android_app *app, AInputEvent *event) CORE.Input.Keyboard.keyPressedQueue[CORE.Input.Keyboard.keyPressedQueueCount] = keycode; CORE.Input.Keyboard.keyPressedQueueCount++; } + else if (AKeyEvent_getAction(event) == AKEY_EVENT_ACTION_MULTIPLE) CORE.Input.Keyboard.keyRepeatInFrame[keycode] = 1; else CORE.Input.Keyboard.currentKeyState[keycode] = 0; // Key up if (keycode == AKEYCODE_POWER) @@ -6496,7 +6510,11 @@ static void InitEvdevInput(void) } // Reset keyboard key state - for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) CORE.Input.Keyboard.currentKeyState[i] = 0; + for (int i = 0; i < MAX_KEYBOARD_KEYS; i++) + { + CORE.Input.Keyboard.currentKeyState[i] = 0; + CORE.Input.Keyboard.keyRepeatInFrame[i] = 0; + } // Open the linux directory of "/dev/input" directory = opendir(DEFAULT_EVDEV_PATH); From b27e98a428a1732b2f146770e96631aa13aec8b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Branimir=20Ri=C4=8Dko?= Date: Sat, 26 Aug 2023 18:55:57 +0200 Subject: [PATCH 0219/1350] Fix bug where default shaders was not linking. (#3261) --- src/rlgl.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/rlgl.h b/src/rlgl.h index 3756e5ac7..f274931e2 100644 --- a/src/rlgl.h +++ b/src/rlgl.h @@ -4550,6 +4550,7 @@ static void rlLoadShaderDefault(void) #endif #if defined(GRAPHICS_API_OPENGL_ES2) "#version 100 \n" + "precision mediump float; \n" // Precision required for OpenGL ES2 (WebGL) (on some browsers) "attribute vec3 vertexPosition; \n" "attribute vec2 vertexTexCoord; \n" "attribute vec4 vertexColor; \n" From 96464972168e32221298fef24af7c24b292e55f7 Mon Sep 17 00:00:00 2001 From: Ray Date: Sun, 27 Aug 2023 00:30:56 +0200 Subject: [PATCH 0220/1350] Formating review --- src/raymath.h | 13 +++++++++++-- src/rtextures.c | 18 ++++++------------ 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/raymath.h b/src/raymath.h index def8bfd42..1fab43aad 100644 --- a/src/raymath.h +++ b/src/raymath.h @@ -15,6 +15,7 @@ * - Functions use always a "result" variable for return * - Functions are always defined inline * - Angles are always in radians (DEG2RAD/RAD2DEG macros provided for convenience) +* - No compound literals used to make sure libray is compatible with C++ * * CONFIGURATION: * #define RAYMATH_IMPLEMENTATION @@ -716,12 +717,16 @@ RMAPI Vector3 Vector3Normalize(Vector3 v) //Calculate the projection of the vector v1 on to v2 RMAPI Vector3 Vector3Project(Vector3 v1, Vector3 v2) { + Vector3 result = { 0 }; + float v1dv2 = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); float v2dv2 = (v2.x*v2.x + v2.y*v2.y + v2.z*v2.z); float mag = v1dv2/v2dv2; - Vector3 result = { v2.x*mag , v2.y*mag, v2.z*mag }; + result.x = v2.x*mag; + result.y = v2.y*mag; + result.z = v2.z*mag; return result; } @@ -729,12 +734,16 @@ RMAPI Vector3 Vector3Project(Vector3 v1, Vector3 v2) //Calculate the rejection of the vector v1 on to v2 RMAPI Vector3 Vector3Reject(Vector3 v1, Vector3 v2) { + Vector3 result = { 0 }; + float v1dv2 = (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z); float v2dv2 = (v2.x*v2.x + v2.y*v2.y + v2.z*v2.z); float mag = v1dv2/v2dv2; - Vector3 result = { v1.x - (v2.x*mag) , v1.y - (v2.y*mag), v1.z - (v2.z*mag) }; + result.x = v1.x - (v2.x*mag); + result.y = v1.y - (v2.y*mag); + result.z = v1.z - (v2.z*mag); return result; } diff --git a/src/rtextures.c b/src/rtextures.c index f32a45d1e..b88aeb154 100644 --- a/src/rtextures.c +++ b/src/rtextures.c @@ -3284,19 +3284,13 @@ void ImageDrawRectangleRec(Image *dst, Rectangle rec, Color color) if (rec.width < 0) rec.width = 0; if (rec.height < 0) rec.height = 0; - // clamp the size the the image bounds - if (rec.x + rec.width >= dst->width) - rec.width = dst->width - rec.x; + // Clamp the size the the image bounds + if ((rec.x + rec.width) >= dst->width) rec.width = dst->width - rec.x; + if ((rec.y + rec.height) >= dst->height) rec.height = dst->height - rec.y; - if (rec.y + rec.height >= dst->height) - rec.height = dst->height - rec.y; - - // check if the rect is even inside the image - if (rec.x > dst->width || rec.y > dst->height) - return; - - if (rec.x + rec.width < 0 || rec.y + rec.height < 0) - return; + // Check if the rect is even inside the image + if ((rec.x > dst->width) || (rec.y > dst->height)) return; + if (((rec.x + rec.width) < 0) || (rec.y + rec.height < 0)) return; int sy = (int)rec.y; int sx = (int)rec.x; From 71a8d09a6384837f273a1dd84e5a02d0df2cccdb Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Sun, 27 Aug 2023 10:42:45 -0300 Subject: [PATCH 0221/1350] Add missing cmake options (#3267) --- CMakeOptions.txt | 4 ++++ cmake/CompileDefinitions.cmake | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/CMakeOptions.txt b/CMakeOptions.txt index 5cb73bbdd..83faa5637 100644 --- a/CMakeOptions.txt +++ b/CMakeOptions.txt @@ -46,6 +46,7 @@ cmake_dependent_option(SUPPORT_BUSY_WAIT_LOOP "Use busy wait loop for timing syn cmake_dependent_option(SUPPORT_EVENTS_WAITING "Wait for events passively (sleeping while no events) instead of polling them actively every frame" OFF CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_WINMM_HIGHRES_TIMER "Setting a higher resolution can improve the accuracy of time-out intervals in wait functions" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_COMPRESSION_API "Support for compression API" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_EVENTS_AUTOMATION "Support automatic generated events, loading and recording of those events when required" OFF CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_CUSTOM_FRAME_CONTROL "Enabling this flag allows manual control of the frame processes, use at your own risk" OFF CUSTOMIZE_BUILD OFF) # rshapes.c @@ -58,6 +59,7 @@ cmake_dependent_option(SUPPORT_IMAGE_MANIPULATION "Support multiple image editin cmake_dependent_option(SUPPORT_FILEFORMAT_PNG "Support loading PNG as textures" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_DDS "Support loading DDS as textures" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_HDR "Support loading HDR as textures" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_PIC "Support loading PIC as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_PNM "Support loading PNM as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_KTX "Support loading KTX as textures" ${OFF} CUSTOMIZE_BUILD OFF) cmake_dependent_option(SUPPORT_FILEFORMAT_ASTC "Support loading ASTC as textures" ${OFF} CUSTOMIZE_BUILD OFF) @@ -83,6 +85,7 @@ cmake_dependent_option(SUPPORT_FILEFORMAT_MTL "Support loading MTL file format" cmake_dependent_option(SUPPORT_FILEFORMAT_IQM "Support loading IQM file format" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_GLTF "Support loading GLTF file format" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_VOX "Support loading VOX file format" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_M3D "Support loading M3D file format" ON CUSTOMIZE_BUILD ON) # raudio.c cmake_dependent_option(SUPPORT_FILEFORMAT_WAV "Support loading WAV for sound" ON CUSTOMIZE_BUILD ON) @@ -90,6 +93,7 @@ cmake_dependent_option(SUPPORT_FILEFORMAT_OGG "Support loading OGG for sound" O cmake_dependent_option(SUPPORT_FILEFORMAT_XM "Support loading XM for sound" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_MOD "Support loading MOD for sound" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_MP3 "Support loading MP3 for sound" ON CUSTOMIZE_BUILD ON) +cmake_dependent_option(SUPPORT_FILEFORMAT_QOA "Support loading QOA for sound" ON CUSTOMIZE_BUILD ON) cmake_dependent_option(SUPPORT_FILEFORMAT_FLAC "Support loading FLAC for sound" ${OFF} CUSTOMIZE_BUILD OFF) # utils.c diff --git a/cmake/CompileDefinitions.cmake b/cmake/CompileDefinitions.cmake index 689f98ac4..1458e2f3c 100644 --- a/cmake/CompileDefinitions.cmake +++ b/cmake/CompileDefinitions.cmake @@ -28,6 +28,7 @@ if (${CUSTOMIZE_BUILD}) define_if("raylib" SUPPORT_EVENTS_WAITING) define_if("raylib" SUPPORT_WINMM_HIGHRES_TIMER) define_if("raylib" SUPPORT_COMPRESSION_API) + define_if("raylib" SUPPORT_EVENTS_AUTOMATION) define_if("raylib" SUPPORT_CUSTOM_FRAME_CONTROL) define_if("raylib" SUPPORT_QUADS_DRAW_MODE) define_if("raylib" SUPPORT_IMAGE_EXPORT) @@ -36,6 +37,7 @@ if (${CUSTOMIZE_BUILD}) define_if("raylib" SUPPORT_FILEFORMAT_PNG) define_if("raylib" SUPPORT_FILEFORMAT_DDS) define_if("raylib" SUPPORT_FILEFORMAT_HDR) + define_if("raylib" SUPPORT_FILEFORMAT_PIC) define_if("raylib" SUPPORT_FILEFORMAT_PNM) define_if("raylib" SUPPORT_FILEFORMAT_KTX) define_if("raylib" SUPPORT_FILEFORMAT_ASTC) @@ -56,11 +58,13 @@ if (${CUSTOMIZE_BUILD}) define_if("raylib" SUPPORT_FILEFORMAT_IQM) define_if("raylib" SUPPORT_FILEFORMAT_GLTF) define_if("raylib" SUPPORT_FILEFORMAT_VOX) + define_if("raylib" SUPPORT_FILEFORMAT_M3D) define_if("raylib" SUPPORT_FILEFORMAT_WAV) define_if("raylib" SUPPORT_FILEFORMAT_OGG) define_if("raylib" SUPPORT_FILEFORMAT_XM) define_if("raylib" SUPPORT_FILEFORMAT_MOD) define_if("raylib" SUPPORT_FILEFORMAT_MP3) + define_if("raylib" SUPPORT_FILEFORMAT_QOA) define_if("raylib" SUPPORT_FILEFORMAT_FLAC) define_if("raylib" SUPPORT_STANDARD_FILEIO) define_if("raylib" SUPPORT_TRACELOG) From 76adf883fd7462e9a61aa6f6448a32099f8f29b0 Mon Sep 17 00:00:00 2001 From: iacore <74560659+iacore@users.noreply.github.com> Date: Sun, 27 Aug 2023 13:43:39 +0000 Subject: [PATCH 0222/1350] Fix CMake extraneous -lglfw (#3266) Closes #3265. The problem: LIBS_PRIVATE is a list of library names (used by pkg-config), but the shared library of the same name doesn't always exist. --- cmake/InstallConfigurations.cmake | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cmake/InstallConfigurations.cmake b/cmake/InstallConfigurations.cmake index 6a89ad55c..6c606e565 100644 --- a/cmake/InstallConfigurations.cmake +++ b/cmake/InstallConfigurations.cmake @@ -9,8 +9,7 @@ install( # PKG_CONFIG_LIBS_PRIVATE is used in raylib.pc.in if (NOT BUILD_SHARED_LIBS) include(LibraryPathToLinkerFlags) - library_path_to_linker_flags(__PKG_CONFIG_LIBS_PRIVATE "${LIBS_PRIVATE}") - set(PKG_CONFIG_LIBS_PRIVATE ${__PKG_CONFIG_LIBS_PRIVATE} ${GLFW_PKG_LIBS}) + set(PKG_CONFIG_LIBS_PRIVATE ${GLFW_PKG_LIBS}) string(REPLACE ";" " " PKG_CONFIG_LIBS_PRIVATE "${PKG_CONFIG_LIBS_PRIVATE}") elseif (BUILD_SHARED_LIBS) set(PKG_CONFIG_LIBS_EXTRA "") From 9393500bff8731c744f0693c7f6bc9db6c612b57 Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Sun, 27 Aug 2023 16:14:20 -0300 Subject: [PATCH 0223/1350] Fix example/models/models_loading_gltf.c controls (#3268) --- examples/models/models_loading_gltf.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/examples/models/models_loading_gltf.c b/examples/models/models_loading_gltf.c index d5ebff237..28847316a 100644 --- a/examples/models/models_loading_gltf.c +++ b/examples/models/models_loading_gltf.c @@ -4,9 +4,9 @@ * * LIMITATIONS: * - Only supports 1 armature per file, and skips loading it if there are multiple armatures -* - Only supports linear interpolation (default method in Blender when checked +* - Only supports linear interpolation (default method in Blender when checked * "Always Sample Animations" when exporting a GLTF file) -* - Only supports translation/rotation/scale animation channel.path, +* - Only supports translation/rotation/scale animation channel.path, * weights not considered (i.e. morph targets) * * Example originally created with raylib 3.7, last time updated with raylib 4.2 @@ -42,7 +42,7 @@ int main(void) // Load gltf model Model model = LoadModel("resources/models/gltf/robot.glb"); - + // Load gltf model animations unsigned int animsCount = 0; unsigned int animIndex = 0; @@ -63,9 +63,9 @@ int main(void) //---------------------------------------------------------------------------------- UpdateCamera(&camera, CAMERA_THIRD_PERSON); // Select current animation - if (IsKeyPressed(KEY_UP)) animIndex = (animIndex + 1)%animsCount; - else if (IsKeyPressed(KEY_DOWN)) animIndex = (animIndex + animsCount - 1)%animsCount; - + if (IsMouseButtonPressed(MOUSE_BUTTON_RIGHT)) animIndex = (animIndex + 1)%animsCount; + else if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) animIndex = (animIndex + animsCount - 1)%animsCount; + // Update model animation ModelAnimation anim = modelAnimations[animIndex]; animCurrentFrame = (animCurrentFrame + 1)%anim.frameCount; @@ -85,7 +85,7 @@ int main(void) EndMode3D(); - DrawText("Use the UP/DOWN arrow keys to switch animation", 10, 10, 20, GRAY); + DrawText("Use the LEFT/RIGHT mouse buttons to switch animation", 10, 10, 20, GRAY); DrawText(TextFormat("Animation: %s", anim.name), 10, GetScreenHeight() - 20, 10, DARKGRAY); EndDrawing(); From de3dc94d5ba135f9e179d1513814134e850471b3 Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Sun, 27 Aug 2023 16:15:02 -0300 Subject: [PATCH 0224/1350] Fix example/models/models_loading_m3d.c controls (#3269) --- examples/models/models_loading_m3d.c | 35 ++++++++++++++-------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/examples/models/models_loading_m3d.c b/examples/models/models_loading_m3d.c index b9674b6f6..1e56d51ca 100644 --- a/examples/models/models_loading_m3d.c +++ b/examples/models/models_loading_m3d.c @@ -40,7 +40,7 @@ int main(void) camera.projection = CAMERA_PERSPECTIVE; // Camera projection type Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model position - + char modelFileName[128] = "resources/models/m3d/cesium_man.m3d"; bool drawMesh = 1; bool drawSkeleton = 1; @@ -72,27 +72,27 @@ int main(void) if (IsKeyDown(KEY_SPACE) || IsKeyPressed(KEY_N)) { animFrameCounter++; - + if (animFrameCounter >= anims[animId].frameCount) animFrameCounter = 0; - + UpdateModelAnimation(model, anims[animId], animFrameCounter); animPlaying = true; } - - // Select animation by pressing A - if (IsKeyPressed(KEY_A)) + + // Select animation by pressing C + if (IsKeyPressed(KEY_C)) { animFrameCounter = 0; animId++; - + if (animId >= animsCount) animId = 0; UpdateModelAnimation(model, anims[animId], 0); animPlaying = true; } } - + // Toggle skeleton drawing - if (IsKeyPressed(KEY_S)) drawSkeleton ^= 1; + if (IsKeyPressed(KEY_B)) drawSkeleton ^= 1; // Toggle mesh drawing if (IsKeyPressed(KEY_M)) drawMesh ^= 1; @@ -112,19 +112,19 @@ int main(void) // Draw the animated skeleton if (drawSkeleton) { - // Loop to (boneCount - 1) because the last one is a special "no bone" bone, + // Loop to (boneCount - 1) because the last one is a special "no bone" bone, // needed to workaround buggy models // without a -1, we would always draw a cube at the origin for (int i = 0; i < model.boneCount - 1; i++) { - // By default the model is loaded in bind-pose by LoadModel(). - // But if UpdateModelAnimation() has been called at least once + // By default the model is loaded in bind-pose by LoadModel(). + // But if UpdateModelAnimation() has been called at least once // then the model is already in animation pose, so we need the animated skeleton if (!animPlaying || !animsCount) { // Display the bind-pose skeleton DrawCube(model.bindPose[i].translation, 0.04f, 0.04f, 0.04f, RED); - + if (model.bones[i].parent >= 0) { DrawLine3D(model.bindPose[i].translation, @@ -135,7 +135,7 @@ int main(void) { // Display the frame-pose skeleton DrawCube(anims[animId].framePoses[animFrameCounter][i].translation, 0.05f, 0.05f, 0.05f, RED); - + if (anims[animId].bones[i].parent >= 0) { DrawLine3D(anims[animId].framePoses[animFrameCounter][i].translation, @@ -149,9 +149,10 @@ int main(void) EndMode3D(); - DrawText("PRESS SPACE to PLAY MODEL ANIMATION", 10, GetScreenHeight() - 60, 10, MAROON); - DrawText("PRESS A to CYCLE THROUGH ANIMATIONS", 10, GetScreenHeight() - 40, 10, DARKGRAY); - DrawText("PRESS M to toggle MESH, S to toggle SKELETON DRAWING", 10, GetScreenHeight() - 20, 10, DARKGRAY); + DrawText("PRESS SPACE to PLAY MODEL ANIMATION", 10, GetScreenHeight() - 80, 10, MAROON); + DrawText("PRESS N to STEP ONE ANIMATION FRAME", 10, GetScreenHeight() - 60, 10, DARKGRAY); + DrawText("PRESS C to CYCLE THROUGH ANIMATIONS", 10, GetScreenHeight() - 40, 10, DARKGRAY); + DrawText("PRESS M to toggle MESH, B to toggle SKELETON DRAWING", 10, GetScreenHeight() - 20, 10, DARKGRAY); DrawText("(c) CesiumMan model by KhronosGroup", GetScreenWidth() - 210, GetScreenHeight() - 20, 10, GRAY); EndDrawing(); From fc0d13256626e18de8fbcdefb262d8655005b561 Mon Sep 17 00:00:00 2001 From: Ethan Conneely Date: Mon, 28 Aug 2023 21:49:45 +0100 Subject: [PATCH 0225/1350] Remove e from secondes (#3270) --- examples/shaders/resources/shaders/glsl100/wave.fs | 6 +++--- examples/shaders/resources/shaders/glsl330/wave.fs | 6 +++--- examples/shaders/shaders_texture_waves.c | 2 +- src/rcore.c | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/examples/shaders/resources/shaders/glsl100/wave.fs b/examples/shaders/resources/shaders/glsl100/wave.fs index 50c4e02f3..cd4ba9de4 100644 --- a/examples/shaders/resources/shaders/glsl100/wave.fs +++ b/examples/shaders/resources/shaders/glsl100/wave.fs @@ -10,7 +10,7 @@ varying vec4 fragColor; uniform sampler2D texture0; uniform vec4 colDiffuse; -uniform float secondes; +uniform float seconds; uniform vec2 size; @@ -29,8 +29,8 @@ void main() { float boxTop = 0.0; vec2 p = fragTexCoord; - p.x += cos((fragTexCoord.y - boxTop) * freqX / ( pixelWidth * 750.0) + (secondes * speedX)) * ampX * pixelWidth; - p.y += sin((fragTexCoord.x - boxLeft) * freqY * aspect / ( pixelHeight * 750.0) + (secondes * speedY)) * ampY * pixelHeight; + p.x += cos((fragTexCoord.y - boxTop) * freqX / ( pixelWidth * 750.0) + (seconds * speedX)) * ampX * pixelWidth; + p.y += sin((fragTexCoord.x - boxLeft) * freqY * aspect / ( pixelHeight * 750.0) + (seconds * speedY)) * ampY * pixelHeight; gl_FragColor = texture2D(texture0, p)*colDiffuse*fragColor; } diff --git a/examples/shaders/resources/shaders/glsl330/wave.fs b/examples/shaders/resources/shaders/glsl330/wave.fs index 43efee234..1f22bee09 100644 --- a/examples/shaders/resources/shaders/glsl330/wave.fs +++ b/examples/shaders/resources/shaders/glsl330/wave.fs @@ -11,7 +11,7 @@ uniform vec4 colDiffuse; // Output fragment color out vec4 finalColor; -uniform float secondes; +uniform float seconds; uniform vec2 size; @@ -30,8 +30,8 @@ void main() { float boxTop = 0.0; vec2 p = fragTexCoord; - p.x += cos((fragTexCoord.y - boxTop) * freqX / ( pixelWidth * 750.0) + (secondes * speedX)) * ampX * pixelWidth; - p.y += sin((fragTexCoord.x - boxLeft) * freqY * aspect / ( pixelHeight * 750.0) + (secondes * speedY)) * ampY * pixelHeight; + p.x += cos((fragTexCoord.y - boxTop) * freqX / ( pixelWidth * 750.0) + (seconds * speedX)) * ampX * pixelWidth; + p.y += sin((fragTexCoord.x - boxLeft) * freqY * aspect / ( pixelHeight * 750.0) + (seconds * speedY)) * ampY * pixelHeight; finalColor = texture(texture0, p)*colDiffuse*fragColor; } diff --git a/examples/shaders/shaders_texture_waves.c b/examples/shaders/shaders_texture_waves.c index a087ec4d0..27ad1f6e4 100644 --- a/examples/shaders/shaders_texture_waves.c +++ b/examples/shaders/shaders_texture_waves.c @@ -46,7 +46,7 @@ int main(void) // Load shader and setup location points and values Shader shader = LoadShader(0, TextFormat("resources/shaders/glsl%i/wave.fs", GLSL_VERSION)); - int secondsLoc = GetShaderLocation(shader, "secondes"); + int secondsLoc = GetShaderLocation(shader, "seconds"); int freqXLoc = GetShaderLocation(shader, "freqX"); int freqYLoc = GetShaderLocation(shader, "freqY"); int ampXLoc = GetShaderLocation(shader, "ampX"); diff --git a/src/rcore.c b/src/rcore.c index cb3dfe82a..6e5d94964 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -2999,7 +2999,7 @@ int GetFPS(void) #if !defined(SUPPORT_CUSTOM_FRAME_CONTROL) #define FPS_CAPTURE_FRAMES_COUNT 30 // 30 captures - #define FPS_AVERAGE_TIME_SECONDS 0.5f // 500 millisecondes + #define FPS_AVERAGE_TIME_SECONDS 0.5f // 500 milliseconds #define FPS_STEP (FPS_AVERAGE_TIME_SECONDS/FPS_CAPTURE_FRAMES_COUNT) static int index = 0; From d047597244017ba81ab12d9fd714d001899a992e Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Tue, 29 Aug 2023 14:04:27 -0300 Subject: [PATCH 0226/1350] Fix example/audio/audio_module_player.c help instructions and small bug (#3272) * Fix example/audio/audio_module_player.c help instructions and small bug * Update example/audio/audio_module_player.png screenshot --- examples/audio/audio_module_playing.c | 11 ++++++++++- examples/audio/audio_module_playing.png | Bin 47970 -> 45137 bytes 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/examples/audio/audio_module_playing.c b/examples/audio/audio_module_playing.c index b8482aa57..bf5530fdc 100644 --- a/examples/audio/audio_module_playing.c +++ b/examples/audio/audio_module_playing.c @@ -79,6 +79,7 @@ int main(void) { StopMusicStream(music); PlayMusicStream(music); + pause = false; } // Pause/Resume music playing @@ -134,6 +135,14 @@ int main(void) DrawRectangle(20, screenHeight - 20 - 12, (int)timePlayed, 12, MAROON); DrawRectangleLines(20, screenHeight - 20 - 12, screenWidth - 40, 12, GRAY); + // Draw help instructions + DrawRectangle(20, 20, 425, 145, WHITE); + DrawRectangleLines(20, 20, 425, 145, GRAY); + DrawText("PRESS SPACE TO RESTART MUSIC", 40, 40, 20, BLACK); + DrawText("PRESS P TO PAUSE/RESUME", 40, 70, 20, BLACK); + DrawText("PRESS UP/DOWN TO CHANGE SPEED", 40, 100, 20, BLACK); + DrawText(TextFormat("SPEED: %f", pitch), 40, 130, 20, MAROON); + EndDrawing(); //---------------------------------------------------------------------------------- } @@ -148,4 +157,4 @@ int main(void) //-------------------------------------------------------------------------------------- return 0; -} \ No newline at end of file +} diff --git a/examples/audio/audio_module_playing.png b/examples/audio/audio_module_playing.png index 8bde9879d4aa7c8f57c9bb334888b0fd52c1102e..63003e0472eccf6c7861ffee50dad0215c88cab0 100644 GIT binary patch literal 45137 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 Date: Tue, 29 Aug 2023 19:25:44 +0200 Subject: [PATCH 0227/1350] Use type name instead of valid specifier long long --> long long int --- src/rcore.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/rcore.c b/src/rcore.c index 6e5d94964..210100fbf 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -506,7 +506,7 @@ typedef struct CoreData { double frame; // Time measure for one frame double target; // Desired time for one frame, if 0 not applied #if defined(PLATFORM_ANDROID) || defined(PLATFORM_RPI) || defined(PLATFORM_DRM) - unsigned long long base; // Base time measure for hi-res timer + unsigned long long int base; // Base time measure for hi-res timer #endif unsigned int frameCounter; // Frame counter } Time; @@ -3495,10 +3495,10 @@ bool ChangeDirectory(const char *dir) // Check if a given path point to a file bool IsPathFile(const char *path) { - struct stat pathStat = { 0 }; - stat(path, &pathStat); + struct stat result = { 0 }; + stat(path, &result); - return S_ISREG(pathStat.st_mode); + return S_ISREG(result.st_mode); } // Check if a file has been dropped into window From 8157d4283e5afc5e7822e5885aa7b27cd36ddf65 Mon Sep 17 00:00:00 2001 From: Ray Date: Tue, 29 Aug 2023 19:26:15 +0200 Subject: [PATCH 0228/1350] REVIEWED: `GetFileLength()`, added comment #3262 --- src/rcore.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/rcore.c b/src/rcore.c index 210100fbf..2c53eb5e6 100644 --- a/src/rcore.c +++ b/src/rcore.c @@ -3192,13 +3192,24 @@ bool DirectoryExists(const char *dirPath) int GetFileLength(const char *fileName) { int size = 0; + + // NOTE: On Unix-like systems, it can by used the POSIX system call: stat(), + // but depending on the platform that call could not be available + //struct stat result = { 0 }; + //stat(fileName, &result); + //return result.st_size; FILE *file = fopen(fileName, "rb"); if (file != NULL) { fseek(file, 0L, SEEK_END); - size = (int)ftell(file); + long int fileSize = ftell(file); + + // Check for size overflow (INT_MAX) + if (fileSize > 2147483647) TRACELOG(LOG_WARNING, "[%s] File size overflows expected limit, do not use GetFileLength()", fileName); + else size = (int)fileSize; + fclose(file); } From 752baac00c92a10bebd8b97da4bbf83e8a8345e3 Mon Sep 17 00:00:00 2001 From: Asdqwe Date: Tue, 29 Aug 2023 14:28:03 -0300 Subject: [PATCH 0229/1350] Update examples/models/models_loading_gltf.png;m3d.png screenshots (#3273) --- examples/models/models_loading_gltf.png | Bin 25896 -> 26632 bytes examples/models/models_loading_m3d.png | Bin 23682 -> 25881 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/examples/models/models_loading_gltf.png b/examples/models/models_loading_gltf.png index 34748a22e587a86dc297238c939fbbe148fb5153..ce2a85284077b0cce9fbfb8b63f326ca48013be8 100644 GIT binary patch literal 26632 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 literal 25896 zcmeEuXY$DkwG>pg4q;l@`v5LpTIxX=Ia`6^`LhsX3HcX&739Vwsv{X*gDP ztf-@v9TiJTGf^{B^JH47MzJ7k( zP3l)hhv#Iw-hDQ7b5?0)_oUJ6L)9(L@%mRjo$sYz-oMgwQ+1zxXZr+WNi<^1kFvOF z%%-1Nr?FF6#--?|o?##EG zM<3@ZU+=T;qxaE2yk?==NGjq2JaPPrg^wuM@(!)}i0m|q^pph_27=JNmMSz?=70E; zw9AnC0gvrQSsCir(_-8=tch?hI-MS_a+LpF826gF@+&F0(!ONC^Uaj!vJa{+kzu^* z^JPn-ztb=6M_zC!x=}l&w$Nq&4b=U?2WyH)N!GOz`$KIPok;7quU(?AwPji6jr;PM zlX?uv6u5bQ>3?1cSlzI;a!fDWWfZlz#$tB0ONP(amDh87#*J)V9VMP31szEeWtEKcIK<%7msB32QR)(J6!Q{mv{5IdS4`F?xD6N z+mIAJdUL9zt51Hp!hlz!$%f75e)eJ1Z znM1G|ihRg=p|;TXB`F$AwNW$`EC{Nq{8(yv&bxQ{@IZPv zM`aUb;ReSRUEjOvS6-@Ke_OWkV3Fn4x`vbO(}E5AtbKYlglh3IKQ(SdvxKHL^1W8O zFk=uwmOKBGHAW~zewj*DEd{@+Fr4 z#m$3dF6z$Y;MSiLRcRptVkg&&6O#<=3FbrOQ-y(7^Mr1I{No07p73;{D&Af#^;87 zSB%P&frHZ9ir01EbtF3Cz0Oz)H{_@M-NHjMjjeko*I1h!uw^29*?nG|8!cS{ZVI1@6 zcL#0_47%|5AaFh(%Nz>2?<;YiHAk~+i;QkD$w%uqQP4e|(HDuxz zE5ywM;=r7%-NCKM5VH~#@5_ycC^rkLRAu^tl_`B-nlx@{fqa@eurcZCu4{pPqd+INNrziQI(2GG#cf37|bCq0!4 zNVRpYRB!KSY@_lR{Sk5%DOR}7`R5Q~^m#_>VXpQK`M#APXW;=Ya32btp>dDp2E2D2 zm}_B>qrnxinMG87bZ)smX6Ww?D~w+$RfgjYe+`;S1t2K=CSiH&$d!fQ zNOijS*Hi#Tf#$1$!1t8wuwN;p{yI|j9_-(-ItrK?DG=$@K;Pj^ONUM`OIPMbrk4MY zsQVvMf+Q@iUZ%Ir4s_q_%OE=x_?>P%7W_ytbBdBCzRAiM|SZ)x>{yMUgYW$bXi7oYY zhJyRivZt##b;C0+Nrcc46IUh6W8CmZO~cz#igaEMM^VlUM*qHE*Wvu==q>uH3yO_f zL&|EAX-BkJMeOJq4#H*i378oYg>JId1@2wD4f@nfcG4qIP!^{uS!GTV$I{Sehp6`} zf<^Q7vg{42^6&*1ys1zI_c#eMPuVdk+bbUp^X=C$?@`598<{1nHxKE~1TI<~3RVN) zSpNbyA9`gtOL^+%C2S{3Z!D#4+2*z{-i<}V-6~sl1PasS{zT?<2nw@@p-rpk;!GPMz4}6F<~3+HOI2upC8E!{*=1Ao3mM27 z8w*m1aJ@n`AVm73$UM%t-2J0V!IWV5lx-(Pzlwj(Deyk4=pVRB6=88(4_oEmhVHQ^ z3Vov$d7aiE$!+1L7}>11*B3`w`0Q}ucXSNDzbH|LEY}#ay%-3O7`rfSA0={I)@5;i zm?*Orq-1-c$^)sH)rhBNGwrVnR(zDLWfFMS`96Y*eKIZ90jH2S^cdAlxr`mxO^aM*8@)cuAe5~@6Q~sO-%j=mAYr~6UTO&(g zK7MQMGidg@f+JJ10li|O&<)tS>shC^(Q9Yw7zr#~)A;8%Ho+~%={p=~ z>J_q?2IeRWR#nl~DINjS2r#?cn7q!a!W(}$6E%Vy@(mSvS2ZwEw3tQs)o)5e%CK3j z;69Y?6ho0o6H472RcLGeAb7@*fWNla5riI)D}(IHj8X0SC*IF=P10MH89BBP$35Lf z4^VD6iJ;}K<7qFH8K=OIY6~15mA3A_{1njb*rZr%MDM*lj7Z-2kz}KM2|h#eLV<{+ zUn-Frk?jB;cSP(^;Q`}F&ycIwV42JnE!y$-2`Rvm-2Z}9n4F+BzzUE#(8B#F zbeZSy)SbLYve6UC%S~xMKCl1Ee6=>f?!!ygBE_u-?vDnX(1KyqmNk&6i^;~{O>WQ&XC;sY3Ax3xUIGJ`Jw*=0WsK)Z;<83f1*8>sO)^2b`djF zN5EuCS`_<~D&NyQKTNTH58#(^L{AU77G)Pys!Az(`NuB*!><1=IdwY%&ixU~MGlw% zHYlV)URfeP5C(ATIwPcIcD#F^Sz3?_BPbU`RNviy7XEMl{d<~Pj#Hkh&f8_#fY^lM z0!hc@+JdzMdO+EAPfv{+3QM+VEfLMV-KL(hNlo%9TD4EtX zCCfco(t9X2pO-!8JWJWNa+Y}yE|`hKuqDd>T#Y1Yf&mc4DQFzt= zG6@ZttL$1d%bfCl`FrzL>b-sI0!LUY|8@-vWLNJf6ss7VI_7u^H_nN1qQxDRS^vCA z3P1fJ{v#W#5yF<`o&px)@9Vxa~ACqkd zFvfCDgeMaY{;YQa`OfiiFXC7tf9{a;X-+@x36uNgnRk>Asqpfh{le!8aAk;BHW9qD zmiu7WMJiMa!>LiQV+Z3^_^L^r#l)=o*?(43h=P*Ga*d)>A!QEAW;dLxEN;HSdW;8K zVJzF6)DT@2?}AvBm>CDc7nBrbfp7%~kr;>&DRwomAR+F4zx+tsjM3gr9NXKrzz*H@ zlwXhiQ&5m0ae#UHX)t%-s_fL&+#hXOkXAziO6v zN5~@tXFZC$%SGR_N~R1~YZX)hLM0!A=k?d)mEpBU;RY1_&NCy_HNC(%C1;>TWkXzD ztqen=d$H7Ale{<@Vw0t2=W&yScn4bUCZ0AVQDB&am+fag#01ZCQR>3#f<(D51LX+v zW@4t~umUbdLV-3Gxn8BXCDMf+ANq z+NB~3zcP)MULidDY37>-DdRfuU@1=dAu_w;M4+%eDP9>(v|JX3H>A|l=)@@V!Q`CE zkbo$y4&^;X;C!V;g?PD+_+YymgIGqXlJB~Lr*R6+$yt#J`9VKN!2;+v={r0*yJixe z{ce{3U2!vi09ULMZuuB8&=G9mU2_v(6)|h!kvsPzjABe^xlEpRKXd>fYXqpELxD=$ z3{}!Xv6*q+D0(Af*eB^lR@b=hSRz+@3p8BH#25%h6e~rnL~*C)Zx{J@DR&($4hHw> zgPCs-H19{q4hLj*sHLMUw$N~`cxQiew}0e<%?T|`DUeWqEBkZ+tGrquv6Y)S2?WAi zdtT|+B9M#}WJ)X{H2W|2nG=KJT0fx8nsf(XGR7&t71H)PD5QRnsfe!$3$tvc?Qo-6 zC|kKE$aE7Ao>3()IZ zq2oP`EiArby`G{k+E#gXO6~rI3DKIPEP9u6Nn+$+3EVKCwY=rVsWek2F&oNs=0Viim(C8RBu- zjwDzrK=>JA)^P(Vpe71NpgV1;;tp|u#}t&7lt2UG2-DC8oaBfk={6+pj9i?7)O}`2 zjFiFSUQH}tIY@#%09yla>zLxP6z~|A+cIPhwqUwI@ooaNQBV1nsDNXMqj3(k15e7; z)>ORv9hy=>78I@pqZk}oJ7Fw{s|{0(`V$&eEngT8&_iQq+LpD^?Qw$5P=08sB1MH# zaH9yoAiu$l6=LTu0uJAQ151j-Z~g-NiVf?`3BQz4*~?TStu8q)eP%kxCg41JyDhi0 zS1}(Zxd?K-*}u@?oB)CDma)BU z?l~G%MvTg_c1zUPiVk3QN}>{qmpto zc}qoy-OyitWJxscvvU5X^Z~Nm4-^EEB_IKOfyEsyzN!hei~f~1%v>_W8e^0pcsP(XYui*k+a#7;=k1;Cw3Gb9}$Y; z7QfuoIc~`X49zttTL&xZho}L{F9M}3x0>K6#Pb0Mw+-TBe<{7fRAC7*PP8>Mbv*mz z7DJYrHn7pf{|HhXkv!&r0^V^)1i{*h(g(3RWoENZJ-c*}J0L#>Xoc$1P=!zAI?3^xieT zAOw-Jk%*nzj171(Bgc(qpDnn1SAHN2sAZu-Q2}vB!8{0sRT)k0yBjrvUbEXX8i&tj zNnR)@b7QE?u~<I2&gJaSDyC?zdP11q(E8k*8%JKtZ0}bU*DPfY6BWw=?JhFSa zzIJF-Oh}N>{qNha0az2owLfD>W)x&(-RV=a6gfZaZ9|67)R(TuNijb#N~v<8JvAPw zqqfFy$3Ng|@VkT-=Dyuf#NvQjfs|+{O_8}UEiGlX|JENMN3f_EI6b52t`pXG;-|qv z3*I;$Z<+!bE1>+Om66Y~CjcAFuJE_%`HXwWk{01Z{w~4`p`Cq@tq1TKe0!on=GwvP z4Ph#6ETl@j@r(Q!MW4`O=9%wxa~89$h?&~C^0iZW(6e;eYgZ?g$%irArL}~c67|u% zw)eLE_1flaNASQ&#RI^>NHZ)bs|G1UW1LCk)-N`MF*dN82S^Vx=E9AWiPwLd_ZG3Hp-#;`)l?}ZFP zdFvyvOd%h-ei2YOJt5m8Tw9r4<@uSSM6g`m;Yp~CIIIHAIoD0EPnpyJBx@v@s=c3t?QIrQ5f zdk)aoVMLQ-Np*>|)dam!qGrSuTZ)Au+<-!s)Dv6EJTVpI7--Dgp;n}I=Gq=Ze8Fej z7d+<->3CL$bum52UUN%-tzV!l}_HEMX3@Ja|!YoD)-T zVd*TF-&&DkPXQ6zv%|xiVmRIYA=2;g4D|y6-!R_xeLIqA7iGmo*eH~4ZIDHG>>TAG zleV%`2CC)RRCi=XKTZz_SyS5BrCJn{kNg}^{!oLFznu&t2xZdEjJ+8ya^jXToiNZR) zXTW}5L(fTFh_pOdWJqokoDj068R6QEAD_;mnpo40JFU?H;C zq(yo`)Ihw>lEX6)?nDlt>tV(k9!IPE-JLCqGtT2mp1egS2H)40$Ejmn7T@1#Z1_l*->Zr*2R1ZQ7u0t0(02T52E$IJ zAwP&}Aln93XayR9NDxz8anMqQ1I+(-iL1Ac*DxkNnqmlSD{HGRy3`A0vOxUsiR<-2 zGpV+ZgIR)7>V_Bj2o`}{g%gQFzn-t&cs75A8oNjs5lXg>rmH8{>nInWHL^NG(W~;o zgPNITDp1iRz~WN!8W2I!j*L-C=oDuTH}kg-X5AsqX(Nf!Sk;ZTTTe-=elK#Tyof8# zIU+a-Qnp{o6JkP^P*5yf`Z2P&+ZqV!ePD>7GA2 zIvNle8L6~~HUSpi1r`n}QPNaQ+FWITf%#$787tJI-@A{bJy%2WZOvBwhG;fHUBUE% zWhOu31HS996I@yCdR8~SexqJS+ymN0XTE0}x1n>*FiJ^pfUyEg0aZ?N>f zx`;3nK(4i2F(R}SV145qs%Cs(?n>&oWg=aUwJIni$@_ak!E#<=aVB%RnWr#CA?#zm zI>Hf&YJC^zKh!DAK$?8!wH|Ey&3Il3TzSu&${ntV^MlF1S(vxcpK5U4hXdvGH~^p8 z12NCBJdV;F7iLwa!QtEPk9ea(lxEmDjTLXaP|@r;wNXd=-YMOE$a>Aba~Eqoejl?Z zEZz;VstN9hKwMa@bE(-~>Z3eCM@`{B4KM)cxmLQI|LBeIO+}h!l$C5)s1kBHb%(KS ztYPK?LA(=H4Y(@Ti@5W&g+Wzu4pnzmY9HLJua1c^5CsaO!L1+A1stM8mSa#8QRtUz z$!%zqDQ)hEgilxRR6Rs~O(LR0w>YDsM)BRat~mk>z+HtKpitVyLNQg7v#$2%W+UpTM^qg{ zmY0Kb2)@TcrX;LD-`RUU9+_dc&jNmh)Gw+StfwA%A6*^mfTKkc&3lr-5ky1vupT@H z20UZ77cx3UWL-MwyWg?R-{rTGO@;TC)D&PnD9Z$=GC_wdN=+?PeC z@#ZExI{ch65>XuEfVyiGZOcvBtQp|_iB(|HGfcq1+@XBCAx#}Nq^30VS;AxUjg-`L z;Yf|78N>S`mfdi(W#~K#m8jyka#n0JbM^9*n(-aYAYnv3cZW}tsJ=egUwC^($|II9 zerF;N@GR28Fp}nmdae_ze4htg(v8UEBX_AwPvTaWZCguXCxtk+frf+?nm@3GK`V7H zi(stBcFuYu^iOk^zRQ~VSN8+H1h0fZM8vkTs<}C7B&gLf^k6qf>BW65KO;GPQ5eV3bTEs7gl{~{6=tCByxd6Iw_D{-4EZLAFlN@E!Xzz8 z=Id`Xq>dQRypdHgFq4{Xd3deEq-Gw@ z>(jcJtikY2#h8NY{Iy>*nsBfC(?M)63|~Ri@{Oe;R=tMTaB-tN58oW5t78OfbE*`{ z>Fjoub8vgbDRtD@)nt3**M5F7N+-<5f%D+r9&bX^wDs9RO2*vn9Nk40^GFr&!<821 zvp=1eeD!%&)cL$LzE&F9c8W28wv+Cx7a(bN36pr~njyQ0?Zzva>-w&9i3=}?@fyDU z8@~gF2V#W7N$7h|HT{lEFn2TB6BaEf@(3ksqA@XBWx_f!Cm~^vo#!gEuPYHvKchN4WVxczLoZAI4s_Hb_66k&v*Hb>`%idzIb*fz#V9Ga35 z=QcUlzH>aO?ZO_~BMnq^n&hG%PK~IUSfG{Aa;)v*(!!7xj#9-r-WU8P0g#nY_E zD4dtS8>@1f0kGP8SB3y z$@1Jn0Q$)OQqCZdm?ArFB=3Ai=TprHyvC+LL~m5Bn5Z5=7mvjp}0O&9;&*3fpc04|W*$lo&waa+n zb-41iP~#Z4^2hIzKFdci;nsx0fy z1iKqeH$G#$vsyEVp3<}G64n3M!{ww}xbmv->IdhtEZ;aA|2eQkK#zV6cLdU|N@%3( zkhC0kz_Tc)Me+?(wcI~4s-Na<+w5-~PaVE8#Oa=d)y(MC)AAn7+6Jyz3?(wwVK^%X zIrx4FND>yG*3h+8+UUD+<2|w(XloFDTd;Hk_r?s`9?kg>XjtxcL@B}9f-*0~v7073 z8W~L(OM0$B@@_!Xz*h4zP)j$hZFer(PzHb1m3-zd{sIx(9eq)9kM!fJ+Qudxr-Nn7 zvz`??OP0lCAF)tD&AAQwRovR@8`OGNBb{opa=wAmz;xi5&PnIpDjiozF2l%`kP=p7$Y$h|n+t`*|rZv^t}3m%eV|KU-1GwpdRwD1Q3^H-e!D zARp62BeQK!&suj*b_q`bHbE-e8dtuSiro^a`SE$FQfQ(%@q2T2Fhy9?}TxpR#?{-QEjp#VWWVE3gdvpC^F*=wlH=873PL115kNM z{{UfQVP8tP??K~DW4Oke698RJe(^?qg zDQ)(*ISKPu9Q(Qwq|k&D@as?WdPsdK4aeH?*s1gAa|gskt%RlZ$~9U+`ey#=m2j80 zt5r?YWAk(n9pGyr6CZFZUuu_xh8m1A!^#dNbd87%Zv&$M+z|lsxYF!;e)7m>CMH!a zz$j&I4RCwk+OLsZ9^RXOg=ggvk~UeB%b=?`{%jjPP7M&vep)Lo-~%Xx7$TqlsP)J6 zgE;~AYp2S$A>M0$DC+jn6<>|ByzSns&s%tg3vwd%GH@9xniU#ag4 zC5*jhg31mu2vvNnsAr^ZBctsi0Dczjv#d=;+#Vv>fU)`vP46WJ~EL$HUt3N-yj zI0T*Eil?f0DRVQDruXt8W4HU@D7P(IX?HaTex|lnnHzm97vZ zRL6H$mE6keiDTkYq#j8r%RC>3P4@CAF(d5aN0P3$dFLLFLb2Mf7S6@C_!p z$4~c*M~qLj!-|?}$y0ArxQ!V|E5c*a6)n%oNS%?>H|V{y;k`>hlx|z}jXsMqp0uD- z6<^7$N`1yq{k^^iR&TY0`e=&wn+D2w#@d^w&M>MOq`tI~q_z((9zLytV$zENyqYy) zd*JTsWxvlW%b&l%QEM?APcy(_Y~Ig1Amq`0wt%o*jy4T*Ao)QW6*)@dtI+mtW3$68#ZA*RLvJ<)e9 zRJk!%Wy===h@xi%RVPEIS}(d@(Pv*=iW?JV<-WqvV2`ETl*>y4;!fKW`h^02`o3<= zjYY9xmRFC4PG|Lyk61POW2i_Z5|IEl34E0IB2z+gHN^n_^d0rl$)OlGzD+T^VXQ70 z&J4@~H0qd61F)8N2Fp%zyD9}^$LoFWRB^QvvQw4rXyr>L@9HZL`=_?(W9-{S8nX|I zc5Yo}N;p>)gLtzyi88N{HW0ZRdzj+bx$W4BX4(LAbZ=cWN$fe)$}xDfH>qK#(AhI_ zQKs%j0VeEy7@hHqOo%H^C0_C(V(dq6JODibV)Pmp)hJtQm5NhSs13IT1F81HRPSy~ zqw(^hmFrz2oAMH~rq?=sQ8rILBdM)#%LCpE>rLnvj;BdOV@+t>K-VG-=Fa9*qcFSeVtNipMN}unHTt zu@HXh2PYb^kLg0694lPIjO{zPz9X4_dqDe8kvRZO3p>YCl3Gea?8wT zekHxEULW?_f9m0B!AWu`rXlOb=aXF{yNxVWDjx5pXJ%INJnby3D1PE> zzUuxeZivJJof-~vUvo%ekI^W_W5fEAohviM%+s)XiTLz=>odW-+kX-yA4GAvvVeyBS{Rw zhh*J1*-M)VCPaR67*{KBg6U9~#Aq$MYkp~}hCjZH8@{1rg$$sJ_{rl; zw-_p7%L4ob;PWpSSG_zYAs2fsOv@2Cigp4o)wt*s29NeQO_8w(ubyF zePXFJbJzdG3J%Cn?;pVCau;3OdUN8#w=#umNjkbB9oZV?loG~6!}E-hqYet()--!o8ma=3g$7q1avak&*$!h0sE3S#0s zbJ3zWgV;KXfuKO6FZ9?MmmQW4S5jZIe7ygdoLAyaOryPI?qmv@RLV)_SShWF8vA;{ zbv`CQT{T)KQNXs!D_Xg-)}iM!74t|^RlKXl3fIGpnMmSldpFLX7oc_y)SBP!*t*7oG0}WRtHEep#wa!_&#w(>^kMPQB z-@FXY!jEra`A4VRQ!U>lajml}P8c(JuOx>LS~}nnh6W1`oVU3gwY8ly?l_cza&jcc zHs-5e+d;0B><%*tu6z9Me29>gDq;2t$bKQtoGl<#{ciZgwHpxwq9|yzC7kU9jkW`J zLJDa;AW7mVtP`h(AL|guW1CoCiVvbs%q%UWePVh9vWs9DdJBZ3Dd?QjHSg~Jj08l< z>sF-6IIUTdY)Yqp-J1fw?9B7FIoY<@VbL8`;%O28F3mzc)G%quc{=^Hc+JWx@`^v4 zV`46xzT>YJHmM0p5MopKV*qpuu1KOmw@8{QY$^e@nDT+G^g{A?6zLx#ZL<0qLoa?PM|`drU7gOr?7 z4Chp#1Q^(#Z$+E70oFsD{)dEIa`2`;X5A=fk=ek?U9U5SHBc3`XC?QD18!tOsc=yu zvvj6KkC|^&`+|ZOOZc>ag(>TopSY@ZuHDZP;2SSHOTXOhS>CkczGY@yhV+r6djon4 z1S8qglV!nqFUFD>gq0xKZE@Gbtm~QMFiogUi6DAjtnOG`)6q5E5JQf2VLesSS6fvH zVlgP<`597I*4R?2*^cY!qU^YxO3|YiB(13NX#<p-6>K5V}O;_58(o8ON4x52{V^Z)^sHe9mtnPU%&_Rw4~ z;PHK4ik`n(Kj@RpLmqO~czUF8+Yfh;#9eUF!8}=quBNCDy}}Oh!b!pK=fj9S;72=B zT$6fY)xW%YoaL=ixDXb`SrU!Qn9}HHRcnklAh!4(xo&~`YvRwJ=elMzxQCf^T(htt zTo&Hn3nsf2dAF+`Ka8-qqW??{dmUI9LZwaoz&Y@c306TPo9P-kZ6)*qb1=N}_i{&D zo4i|?DUaV`YE+4{+e9^MZTR>@)iyaJ8y20Pi;#+Yp|z=7!)@?@@B{4VYjDj!j~uX# zaMCY-zpL;;e1i24#JmW)C{}3UvJ2H_QG1olRle%NG_WU%HS{u?ISAM8hK4z_yF{V) z>zL(vktLfWcue0o4{3Aak)JFS^^>v2dc4h<+#Q&p&FOlVMOXB6(k#<=+euiPT@p}j zfLb3C4v;T6_|Ua!qMW#=hb$vPXWb|snh(ke zpJh)XM@0k(clFmd#FfW8(dgr1P1viUhpy(+h&P1~BJ;HHi~a3q>$C{`?QeU8-)v1v zI=ri#yFl{@$LvP2ZD&KgvFUDt=Pd!ry~-TDai3=Htly;Aa#e5PcYSuviOlhfMME2h zZd